mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
bump version to 6.2.6 in package.json
This commit is contained in:
@@ -284,8 +284,12 @@ function normalizeTask(task: unknown): NormalizedTask | null {
|
||||
const implementation = taskObj.implementation as unknown[] | undefined;
|
||||
const modificationPoints = taskObj.modification_points as Array<{ file?: string }> | undefined;
|
||||
|
||||
// Ensure id is always a string (handle numeric IDs from JSON)
|
||||
const rawId = taskObj.id ?? taskObj.task_id;
|
||||
const stringId = rawId != null ? String(rawId) : 'unknown';
|
||||
|
||||
return {
|
||||
id: (taskObj.id as string) || (taskObj.task_id as string) || 'unknown',
|
||||
id: stringId,
|
||||
title: (taskObj.title as string) || (taskObj.name as string) || (taskObj.summary as string) || 'Untitled Task',
|
||||
status: (status as string).toLowerCase(),
|
||||
// Preserve original fields for flexible rendering
|
||||
|
||||
@@ -284,8 +284,12 @@ function normalizeTask(task: unknown): NormalizedTask | null {
|
||||
const implementation = taskObj.implementation as unknown[] | undefined;
|
||||
const modificationPoints = taskObj.modification_points as Array<{ file?: string }> | undefined;
|
||||
|
||||
// Ensure id is always a string (handle numeric IDs from JSON)
|
||||
const rawId = taskObj.id ?? taskObj.task_id;
|
||||
const stringId = rawId != null ? String(rawId) : 'unknown';
|
||||
|
||||
return {
|
||||
id: (taskObj.id as string) || (taskObj.task_id as string) || 'unknown',
|
||||
id: stringId,
|
||||
title: (taskObj.title as string) || (taskObj.name as string) || (taskObj.summary as string) || 'Untitled Task',
|
||||
status: (status as string).toLowerCase(),
|
||||
// Preserve original fields for flexible rendering
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Handles all CLAUDE.md memory rules management endpoints
|
||||
*/
|
||||
import type { IncomingMessage, ServerResponse } from 'http';
|
||||
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'fs';
|
||||
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync, unlinkSync, mkdirSync } from 'fs';
|
||||
import { join, relative } from 'path';
|
||||
import { homedir } from 'os';
|
||||
|
||||
@@ -453,8 +453,7 @@ function deleteClaudeFile(filePath: string): { success: boolean; error?: string
|
||||
writeFileSync(backupPath, content, 'utf8');
|
||||
|
||||
// Delete original file
|
||||
const fs = require('fs');
|
||||
fs.unlinkSync(filePath);
|
||||
unlinkSync(filePath);
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
@@ -500,9 +499,8 @@ function createNewClaudeFile(level: 'user' | 'project' | 'module', template: str
|
||||
|
||||
// Ensure directory exists
|
||||
const dir = filePath.substring(0, filePath.lastIndexOf('/') || filePath.lastIndexOf('\\'));
|
||||
const fs = require('fs');
|
||||
if (!existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
// Write file
|
||||
|
||||
@@ -362,8 +362,9 @@ export async function handleCliRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
const status = url.searchParams.get('status') || null;
|
||||
const category = url.searchParams.get('category') as 'user' | 'internal' | 'insight' | null;
|
||||
const search = url.searchParams.get('search') || null;
|
||||
const recursive = url.searchParams.get('recursive') !== 'false';
|
||||
|
||||
getHistoryWithNativeInfo(projectPath, { limit, tool, status, category, search })
|
||||
getHistoryWithNativeInfo(projectPath, { limit, tool, status, category, search, recursive })
|
||||
.then(history => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(history));
|
||||
|
||||
@@ -291,13 +291,14 @@ FOCUS AREAS: ${extractFocus || 'naming conventions, error handling, code structu
|
||||
return { error: `Unknown generation type: ${generationType}` };
|
||||
}
|
||||
|
||||
// Execute CLI tool (Gemini) with at least 10 minutes timeout
|
||||
// Execute CLI tool (Claude) with at least 10 minutes timeout
|
||||
const result = await executeCliTool({
|
||||
tool: 'gemini',
|
||||
tool: 'claude',
|
||||
prompt,
|
||||
mode,
|
||||
cd: workingDir,
|
||||
timeout: 600000 // 10 minutes
|
||||
timeout: 600000, // 10 minutes
|
||||
category: 'internal'
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
|
||||
@@ -123,6 +123,7 @@ function getSkillsConfig(projectPath) {
|
||||
|
||||
result.projectSkills.push({
|
||||
name: parsed.name || skill.name,
|
||||
folderName: skill.name, // Actual folder name for API queries
|
||||
description: parsed.description,
|
||||
version: parsed.version,
|
||||
allowedTools: parsed.allowedTools,
|
||||
@@ -152,6 +153,7 @@ function getSkillsConfig(projectPath) {
|
||||
|
||||
result.userSkills.push({
|
||||
name: parsed.name || skill.name,
|
||||
folderName: skill.name, // Actual folder name for API queries
|
||||
description: parsed.description,
|
||||
version: parsed.version,
|
||||
allowedTools: parsed.allowedTools,
|
||||
@@ -197,6 +199,7 @@ function getSkillDetail(skillName, location, projectPath) {
|
||||
return {
|
||||
skill: {
|
||||
name: parsed.name || skillName,
|
||||
folderName: skillName, // Actual folder name for API queries
|
||||
description: parsed.description,
|
||||
version: parsed.version,
|
||||
allowedTools: parsed.allowedTools,
|
||||
@@ -390,7 +393,7 @@ async function importSkill(sourcePath, location, projectPath, customName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate skill via CLI tool (Gemini)
|
||||
* Generate skill via CLI tool (Claude)
|
||||
* @param {Object} params - Generation parameters
|
||||
* @param {string} params.generationType - 'description' or 'template'
|
||||
* @param {string} params.description - Skill description from user
|
||||
@@ -455,9 +458,9 @@ REQUIREMENTS:
|
||||
3. If the skill requires supporting files (e.g., templates, scripts), create them in the skill folder
|
||||
4. Ensure all files are properly formatted and follow best practices`;
|
||||
|
||||
// Execute CLI tool (Gemini) with write mode
|
||||
// Execute CLI tool (Claude) with write mode
|
||||
const result = await executeCliTool({
|
||||
tool: 'gemini',
|
||||
tool: 'claude',
|
||||
prompt,
|
||||
mode: 'write',
|
||||
cd: baseDir,
|
||||
@@ -515,8 +518,143 @@ export async function handleSkillsRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: Get single skill detail
|
||||
if (pathname.startsWith('/api/skills/') && req.method === 'GET' && !pathname.endsWith('/skills/')) {
|
||||
// API: List skill directory contents
|
||||
if (pathname.match(/^\/api\/skills\/[^/]+\/dir$/) && req.method === 'GET') {
|
||||
const pathParts = pathname.split('/');
|
||||
const skillName = decodeURIComponent(pathParts[3]);
|
||||
const subPath = url.searchParams.get('subpath') || '';
|
||||
const location = url.searchParams.get('location') || 'project';
|
||||
const projectPathParam = url.searchParams.get('path') || initialPath;
|
||||
|
||||
const baseDir = location === 'project'
|
||||
? join(projectPathParam, '.claude', 'skills')
|
||||
: join(homedir(), '.claude', 'skills');
|
||||
|
||||
const dirPath = subPath
|
||||
? join(baseDir, skillName, subPath)
|
||||
: join(baseDir, skillName);
|
||||
|
||||
// Security check: ensure path is within skill folder
|
||||
if (!dirPath.startsWith(join(baseDir, skillName))) {
|
||||
res.writeHead(403, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Access denied' }));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!existsSync(dirPath)) {
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Directory not found' }));
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const stat = statSync(dirPath);
|
||||
if (!stat.isDirectory()) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Path is not a directory' }));
|
||||
return true;
|
||||
}
|
||||
|
||||
const entries = readdirSync(dirPath, { withFileTypes: true });
|
||||
const files = entries.map(entry => ({
|
||||
name: entry.name,
|
||||
isDirectory: entry.isDirectory(),
|
||||
path: subPath ? `${subPath}/${entry.name}` : entry.name
|
||||
}));
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ files, subPath, skillName }));
|
||||
} catch (error) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: (error as Error).message }));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: Read skill file content
|
||||
if (pathname.match(/^\/api\/skills\/[^/]+\/file$/) && req.method === 'GET') {
|
||||
const pathParts = pathname.split('/');
|
||||
const skillName = decodeURIComponent(pathParts[3]);
|
||||
const fileName = url.searchParams.get('filename');
|
||||
const location = url.searchParams.get('location') || 'project';
|
||||
const projectPathParam = url.searchParams.get('path') || initialPath;
|
||||
|
||||
if (!fileName) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'filename parameter is required' }));
|
||||
return true;
|
||||
}
|
||||
|
||||
const baseDir = location === 'project'
|
||||
? join(projectPathParam, '.claude', 'skills')
|
||||
: join(homedir(), '.claude', 'skills');
|
||||
|
||||
const filePath = join(baseDir, skillName, fileName);
|
||||
|
||||
// Security check: ensure file is within skill folder
|
||||
if (!filePath.startsWith(join(baseDir, skillName))) {
|
||||
res.writeHead(403, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Access denied' }));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!existsSync(filePath)) {
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'File not found' }));
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = readFileSync(filePath, 'utf8');
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ content, fileName, path: filePath }));
|
||||
} catch (error) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: (error as Error).message }));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: Write skill file content
|
||||
if (pathname.match(/^\/api\/skills\/[^/]+\/file$/) && req.method === 'POST') {
|
||||
const pathParts = pathname.split('/');
|
||||
const skillName = decodeURIComponent(pathParts[3]);
|
||||
|
||||
handlePostRequest(req, res, async (body) => {
|
||||
const { fileName, content, location, projectPath: projectPathParam } = body;
|
||||
|
||||
if (!fileName) {
|
||||
return { error: 'fileName is required' };
|
||||
}
|
||||
|
||||
if (content === undefined) {
|
||||
return { error: 'content is required' };
|
||||
}
|
||||
|
||||
const baseDir = location === 'project'
|
||||
? join(projectPathParam || initialPath, '.claude', 'skills')
|
||||
: join(homedir(), '.claude', 'skills');
|
||||
|
||||
const filePath = join(baseDir, skillName, fileName);
|
||||
|
||||
// Security check: ensure file is within skill folder
|
||||
if (!filePath.startsWith(join(baseDir, skillName))) {
|
||||
return { error: 'Access denied' };
|
||||
}
|
||||
|
||||
try {
|
||||
await fsPromises.writeFile(filePath, content, 'utf8');
|
||||
return { success: true, fileName, path: filePath };
|
||||
} catch (error) {
|
||||
return { error: (error as Error).message };
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// API: Get single skill detail (exclude /dir and /file sub-routes)
|
||||
if (pathname.startsWith('/api/skills/') && req.method === 'GET' &&
|
||||
!pathname.endsWith('/skills/') && !pathname.endsWith('/dir') && !pathname.endsWith('/file')) {
|
||||
const skillName = decodeURIComponent(pathname.replace('/api/skills/', ''));
|
||||
const location = url.searchParams.get('location') || 'project';
|
||||
const projectPathParam = url.searchParams.get('path') || initialPath;
|
||||
@@ -576,7 +714,7 @@ export async function handleSkillsRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
|
||||
return await importSkill(sourcePath, location, projectPath, skillName);
|
||||
} else if (mode === 'cli-generate') {
|
||||
// CLI generate mode: use Gemini to generate skill
|
||||
// CLI generate mode: use Claude to generate skill
|
||||
if (!skillName) {
|
||||
return { error: 'Skill name is required for CLI generation mode' };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user