claude-code-workflow 6.3.36 → 6.3.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/workflow/lite-execute.md +2 -0
- package/.claude/commands/workflow/lite-fix.md +108 -9
- package/.claude/skills/ccw-loop/README.md +303 -0
- package/.claude/skills/ccw-loop/SKILL.md +259 -0
- package/.claude/skills/ccw-loop/phases/actions/action-complete.md +320 -0
- package/.claude/skills/ccw-loop/phases/actions/action-debug-with-file.md +485 -0
- package/.claude/skills/ccw-loop/phases/actions/action-develop-with-file.md +365 -0
- package/.claude/skills/ccw-loop/phases/actions/action-init.md +200 -0
- package/.claude/skills/ccw-loop/phases/actions/action-menu.md +192 -0
- package/.claude/skills/ccw-loop/phases/actions/action-validate-with-file.md +307 -0
- package/.claude/skills/ccw-loop/phases/orchestrator.md +486 -0
- package/.claude/skills/ccw-loop/phases/state-schema.md +474 -0
- package/.claude/skills/ccw-loop/specs/action-catalog.md +300 -0
- package/.claude/skills/ccw-loop/specs/loop-requirements.md +192 -0
- package/.claude/skills/ccw-loop/templates/progress-template.md +175 -0
- package/.claude/skills/ccw-loop/templates/understanding-template.md +303 -0
- package/.claude/skills/ccw-loop/templates/validation-template.md +258 -0
- package/.codex/agents/action-planning-agent.md +885 -0
- package/.codex/agents/ccw-loop-executor.md +260 -0
- package/.codex/agents/cli-discuss-agent.md +391 -0
- package/.codex/agents/cli-execution-agent.md +333 -0
- package/.codex/agents/cli-explore-agent.md +186 -0
- package/.codex/agents/cli-lite-planning-agent.md +736 -0
- package/.codex/agents/cli-planning-agent.md +562 -0
- package/.codex/agents/code-developer.md +408 -0
- package/.codex/agents/conceptual-planning-agent.md +321 -0
- package/.codex/agents/context-search-agent.md +585 -0
- package/.codex/agents/debug-explore-agent.md +436 -0
- package/.codex/agents/doc-generator.md +334 -0
- package/.codex/agents/issue-plan-agent.md +417 -0
- package/.codex/agents/issue-queue-agent.md +311 -0
- package/.codex/agents/memory-bridge.md +96 -0
- package/.codex/agents/test-context-search-agent.md +402 -0
- package/.codex/agents/test-fix-agent.md +359 -0
- package/.codex/agents/ui-design-agent.md +595 -0
- package/.codex/agents/universal-executor.md +135 -0
- package/.codex/prompts/issue-discover-by-prompt.md +364 -0
- package/.codex/prompts/issue-discover.md +261 -0
- package/.codex/prompts/issue-execute.md +10 -0
- package/.codex/prompts/issue-new.md +285 -0
- package/.codex/prompts/issue-plan.md +161 -63
- package/.codex/prompts/issue-queue.md +298 -288
- package/.codex/prompts/lite-execute.md +627 -133
- package/.codex/prompts/lite-fix.md +670 -0
- package/.codex/prompts/lite-plan-a.md +337 -0
- package/.codex/prompts/lite-plan-b.md +485 -0
- package/.codex/prompts/{lite-plan.md → lite-plan-c.md} +601 -469
- package/.codex/skills/ccw-loop/README.md +171 -0
- package/.codex/skills/ccw-loop/SKILL.md +349 -0
- package/.codex/skills/ccw-loop/phases/actions/action-complete.md +269 -0
- package/.codex/skills/ccw-loop/phases/actions/action-debug.md +286 -0
- package/.codex/skills/ccw-loop/phases/actions/action-develop.md +183 -0
- package/.codex/skills/ccw-loop/phases/actions/action-init.md +164 -0
- package/.codex/skills/ccw-loop/phases/actions/action-menu.md +205 -0
- package/.codex/skills/ccw-loop/phases/actions/action-validate.md +250 -0
- package/.codex/skills/ccw-loop/phases/orchestrator.md +416 -0
- package/.codex/skills/ccw-loop/phases/state-schema.md +388 -0
- package/.codex/skills/ccw-loop/specs/action-catalog.md +182 -0
- package/.codex/skills/ccw-loop-b/README.md +102 -0
- package/.codex/skills/ccw-loop-b/SKILL.md +322 -0
- package/.codex/skills/ccw-loop-b/phases/orchestrator.md +257 -0
- package/.codex/skills/ccw-loop-b/phases/state-schema.md +181 -0
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +12 -1
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +14 -1
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/install.d.ts.map +1 -1
- package/ccw/dist/commands/install.js +38 -7
- package/ccw/dist/commands/install.js.map +1 -1
- package/ccw/dist/commands/issue.d.ts +3 -0
- package/ccw/dist/commands/issue.d.ts.map +1 -1
- package/ccw/dist/commands/issue.js +107 -0
- package/ccw/dist/commands/issue.js.map +1 -1
- package/ccw/dist/commands/loop.d.ts +10 -0
- package/ccw/dist/commands/loop.d.ts.map +1 -0
- package/ccw/dist/commands/loop.js +289 -0
- package/ccw/dist/commands/loop.js.map +1 -0
- package/ccw/dist/commands/upgrade.js +1 -1
- package/ccw/dist/commands/upgrade.js.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.js +3 -2
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -1
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
- package/ccw/dist/core/dashboard-generator.js +4 -1
- package/ccw/dist/core/dashboard-generator.js.map +1 -1
- package/ccw/dist/core/memory-embedder-bridge.d.ts.map +1 -1
- package/ccw/dist/core/memory-embedder-bridge.js +2 -5
- package/ccw/dist/core/memory-embedder-bridge.js.map +1 -1
- package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/claude-routes.js +5 -3
- package/ccw/dist/core/routes/claude-routes.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts +6 -0
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +42 -13
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/cli-settings-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-settings-routes.js +44 -0
- package/ccw/dist/core/routes/cli-settings-routes.js.map +1 -1
- package/ccw/dist/core/routes/codexlens/config-handlers.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens/config-handlers.js +7 -6
- package/ccw/dist/core/routes/codexlens/config-handlers.js.map +1 -1
- package/ccw/dist/core/routes/codexlens/semantic-handlers.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens/semantic-handlers.js +5 -4
- package/ccw/dist/core/routes/codexlens/semantic-handlers.js.map +1 -1
- package/ccw/dist/core/routes/core-memory-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/core-memory-routes.js +4 -2
- package/ccw/dist/core/routes/core-memory-routes.js.map +1 -1
- package/ccw/dist/core/routes/files-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/files-routes.js +4 -2
- package/ccw/dist/core/routes/files-routes.js.map +1 -1
- package/ccw/dist/core/routes/graph-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/graph-routes.js +17 -2
- package/ccw/dist/core/routes/graph-routes.js.map +1 -1
- package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/issue-routes.js +280 -33
- package/ccw/dist/core/routes/issue-routes.js.map +1 -1
- package/ccw/dist/core/routes/loop-routes.d.ts +24 -0
- package/ccw/dist/core/routes/loop-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/loop-routes.js +334 -0
- package/ccw/dist/core/routes/loop-routes.js.map +1 -0
- package/ccw/dist/core/routes/loop-v2-routes.d.ts +44 -0
- package/ccw/dist/core/routes/loop-v2-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/loop-v2-routes.js +1260 -0
- package/ccw/dist/core/routes/loop-v2-routes.js.map +1 -0
- package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/memory-routes.js +2 -1
- package/ccw/dist/core/routes/memory-routes.js.map +1 -1
- package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/system-routes.js +3 -2
- package/ccw/dist/core/routes/system-routes.js.map +1 -1
- package/ccw/dist/core/routes/task-routes.d.ts +12 -0
- package/ccw/dist/core/routes/task-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/task-routes.js +321 -0
- package/ccw/dist/core/routes/task-routes.js.map +1 -0
- package/ccw/dist/core/routes/test-loop-routes.d.ts +11 -0
- package/ccw/dist/core/routes/test-loop-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/test-loop-routes.js +298 -0
- package/ccw/dist/core/routes/test-loop-routes.js.map +1 -0
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +47 -5
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/core/websocket.d.ts +59 -0
- package/ccw/dist/core/websocket.d.ts.map +1 -1
- package/ccw/dist/core/websocket.js +34 -0
- package/ccw/dist/core/websocket.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +40 -0
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +119 -0
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/codex-lens-lsp.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens-lsp.js +2 -5
- package/ccw/dist/tools/codex-lens-lsp.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +22 -32
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts +6 -0
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -1
- package/ccw/dist/tools/litellm-client.js +15 -2
- package/ccw/dist/tools/litellm-client.js.map +1 -1
- package/ccw/dist/tools/loop-manager.d.ts +84 -0
- package/ccw/dist/tools/loop-manager.d.ts.map +1 -0
- package/ccw/dist/tools/loop-manager.js +425 -0
- package/ccw/dist/tools/loop-manager.js.map +1 -0
- package/ccw/dist/tools/loop-state-manager.d.ts +47 -0
- package/ccw/dist/tools/loop-state-manager.d.ts.map +1 -0
- package/ccw/dist/tools/loop-state-manager.js +149 -0
- package/ccw/dist/tools/loop-state-manager.js.map +1 -0
- package/ccw/dist/tools/loop-task-manager.d.ts +149 -0
- package/ccw/dist/tools/loop-task-manager.d.ts.map +1 -0
- package/ccw/dist/tools/loop-task-manager.js +270 -0
- package/ccw/dist/tools/loop-task-manager.js.map +1 -0
- package/ccw/dist/tools/native-session-discovery.d.ts.map +1 -1
- package/ccw/dist/tools/native-session-discovery.js +35 -7
- package/ccw/dist/tools/native-session-discovery.js.map +1 -1
- package/ccw/dist/types/index.d.ts +1 -0
- package/ccw/dist/types/index.d.ts.map +1 -1
- package/ccw/dist/types/index.js +1 -0
- package/ccw/dist/types/index.js.map +1 -1
- package/ccw/dist/types/loop.d.ts +257 -0
- package/ccw/dist/types/loop.d.ts.map +1 -0
- package/ccw/dist/types/loop.js +17 -0
- package/ccw/dist/types/loop.js.map +1 -0
- package/ccw/dist/utils/codexlens-path.d.ts +36 -0
- package/ccw/dist/utils/codexlens-path.d.ts.map +1 -0
- package/ccw/dist/utils/codexlens-path.js +56 -0
- package/ccw/dist/utils/codexlens-path.js.map +1 -0
- package/ccw/dist/utils/uv-manager.d.ts.map +1 -1
- package/ccw/dist/utils/uv-manager.js +3 -2
- package/ccw/dist/utils/uv-manager.js.map +1 -1
- package/ccw/src/cli.ts +13 -1
- package/ccw/src/commands/cli.ts +14 -1
- package/ccw/src/commands/install.ts +50 -7
- package/ccw/src/commands/issue.ts +119 -0
- package/ccw/src/commands/loop.ts +344 -0
- package/ccw/src/commands/upgrade.ts +1 -1
- package/ccw/src/config/litellm-api-config-manager.ts +3 -2
- package/ccw/src/core/dashboard-generator.ts +4 -1
- package/ccw/src/core/memory-embedder-bridge.ts +2 -6
- package/ccw/src/core/routes/claude-routes.ts +5 -3
- package/ccw/src/core/routes/cli-routes.ts +48 -16
- package/ccw/src/core/routes/cli-settings-routes.ts +47 -0
- package/ccw/src/core/routes/codexlens/config-handlers.ts +7 -6
- package/ccw/src/core/routes/codexlens/semantic-handlers.ts +5 -4
- package/ccw/src/core/routes/core-memory-routes.ts +4 -2
- package/ccw/src/core/routes/files-routes.ts +4 -2
- package/ccw/src/core/routes/graph-routes.ts +18 -2
- package/ccw/src/core/routes/issue-routes.ts +308 -33
- package/ccw/src/core/routes/loop-routes.ts +386 -0
- package/ccw/src/core/routes/loop-v2-routes.ts +1470 -0
- package/ccw/src/core/routes/memory-routes.ts +2 -1
- package/ccw/src/core/routes/system-routes.ts +3 -2
- package/ccw/src/core/routes/task-routes.ts +361 -0
- package/ccw/src/core/routes/test-loop-routes.ts +312 -0
- package/ccw/src/core/server.ts +49 -5
- package/ccw/src/core/websocket.ts +104 -0
- package/ccw/src/templates/dashboard-css/02-session.css +2 -0
- package/ccw/src/templates/dashboard-css/04-lite-tasks.css +103 -1
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +56 -0
- package/ccw/src/templates/dashboard-css/32-issue-manager.css +32 -0
- package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +55 -0
- package/ccw/src/templates/dashboard-css/36-loop-monitor.css +1896 -0
- package/ccw/src/templates/dashboard-css/36-loop-monitor.css.backup +1877 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +48 -48
- package/ccw/src/templates/dashboard-js/components/cli-status.js +64 -3
- package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +251 -110
- package/ccw/src/templates/dashboard-js/components/navigation.js +16 -0
- package/ccw/src/templates/dashboard-js/components/notifications.js +22 -0
- package/ccw/src/templates/dashboard-js/components/version-check.js +38 -0
- package/ccw/src/templates/dashboard-js/i18n.js +601 -1
- package/ccw/src/templates/dashboard-js/state.js +2 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +4 -3
- package/ccw/src/templates/dashboard-js/views/issue-manager.js +183 -1
- package/ccw/src/templates/dashboard-js/views/lite-tasks.js +55 -11
- package/ccw/src/templates/dashboard-js/views/loop-monitor.js +3345 -0
- package/ccw/src/templates/dashboard.html +68 -4
- package/ccw/src/tools/claude-cli-tools.ts +143 -0
- package/ccw/src/tools/codex-lens-lsp.ts +2 -5
- package/ccw/src/tools/codex-lens.ts +27 -38
- package/ccw/src/tools/litellm-client.ts +16 -2
- package/ccw/src/tools/loop-manager.ts +519 -0
- package/ccw/src/tools/loop-state-manager.ts +173 -0
- package/ccw/src/tools/loop-task-manager.ts +391 -0
- package/ccw/src/tools/native-session-discovery.ts +38 -7
- package/ccw/src/types/index.ts +1 -0
- package/ccw/src/types/loop.ts +316 -0
- package/ccw/src/utils/codexlens-path.ts +60 -0
- package/ccw/src/utils/uv-manager.ts +3 -2
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { homedir } from 'os';
|
|
|
6
6
|
import { getMemoryStore } from '../memory-store.js';
|
|
7
7
|
import { executeCliTool } from '../../tools/cli-executor.js';
|
|
8
8
|
import { SmartContentFormatter } from '../../tools/cli-output-converter.js';
|
|
9
|
+
import { getDefaultTool } from '../../tools/claude-cli-tools.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Route context interface
|
|
@@ -340,7 +341,7 @@ export async function handleMemoryRoutes(ctx: RouteContext): Promise<boolean> {
|
|
|
340
341
|
if (pathname === '/api/memory/insights/analyze' && req.method === 'POST') {
|
|
341
342
|
handlePostRequest(req, res, async (body: any) => {
|
|
342
343
|
const projectPath = body.path || initialPath;
|
|
343
|
-
const tool = body.tool ||
|
|
344
|
+
const tool = body.tool || getDefaultTool(projectPath);
|
|
344
345
|
const prompts = body.prompts || [];
|
|
345
346
|
const lang = body.lang || 'en'; // Language preference
|
|
346
347
|
|
|
@@ -145,7 +145,7 @@ async function getWorkflowData(projectPath: string): Promise<any> {
|
|
|
145
145
|
generatedAt: new Date().toISOString(),
|
|
146
146
|
activeSessions: [],
|
|
147
147
|
archivedSessions: [],
|
|
148
|
-
liteTasks: { litePlan: [], liteFix: [] },
|
|
148
|
+
liteTasks: { litePlan: [], liteFix: [], multiCliPlan: [] },
|
|
149
149
|
reviewData: { dimensions: {} },
|
|
150
150
|
projectOverview: null,
|
|
151
151
|
statistics: {
|
|
@@ -155,7 +155,8 @@ async function getWorkflowData(projectPath: string): Promise<any> {
|
|
|
155
155
|
completedTasks: 0,
|
|
156
156
|
reviewFindings: 0,
|
|
157
157
|
litePlanCount: 0,
|
|
158
|
-
liteFixCount: 0
|
|
158
|
+
liteFixCount: 0,
|
|
159
|
+
multiCliPlanCount: 0
|
|
159
160
|
},
|
|
160
161
|
projectPath: normalizePathForDisplay(resolvedPath),
|
|
161
162
|
recentPaths: getRecentPaths()
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Routes Module
|
|
3
|
+
* CCW Loop System - HTTP API endpoints for Task management
|
|
4
|
+
* Reference: .workflow/.scratchpad/loop-system-complete-design-20260121.md section 6.1
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { readdir, readFile, writeFile } from 'fs/promises';
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
import type { RouteContext } from './types.js';
|
|
11
|
+
import type { Task } from '../../types/loop.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handle task routes
|
|
15
|
+
* @returns true if route was handled, false otherwise
|
|
16
|
+
*/
|
|
17
|
+
export async function handleTaskRoutes(ctx: RouteContext): Promise<boolean> {
|
|
18
|
+
const { pathname, req, res, initialPath, handlePostRequest } = ctx;
|
|
19
|
+
|
|
20
|
+
// Get workflow directory from initialPath
|
|
21
|
+
const workflowDir = initialPath || process.cwd();
|
|
22
|
+
const taskDir = join(workflowDir, '.task');
|
|
23
|
+
|
|
24
|
+
// GET /api/tasks - List all tasks
|
|
25
|
+
if (pathname === '/api/tasks' && req.method === 'GET') {
|
|
26
|
+
try {
|
|
27
|
+
// Ensure task directory exists
|
|
28
|
+
if (!existsSync(taskDir)) {
|
|
29
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
30
|
+
res.end(JSON.stringify({ success: true, data: [], total: 0 }));
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Read all task files
|
|
35
|
+
const files = await readdir(taskDir);
|
|
36
|
+
const taskFiles = files.filter(f => f.endsWith('.json'));
|
|
37
|
+
|
|
38
|
+
const tasks: Task[] = [];
|
|
39
|
+
for (const file of taskFiles) {
|
|
40
|
+
try {
|
|
41
|
+
const filePath = join(taskDir, file);
|
|
42
|
+
const content = await readFile(filePath, 'utf-8');
|
|
43
|
+
const task = JSON.parse(content) as Task;
|
|
44
|
+
tasks.push(task);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
// Skip invalid task files
|
|
47
|
+
console.error('Failed to read task file ' + file + ':', error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Parse query parameters
|
|
52
|
+
const url = new URL(req.url || '', `http://localhost`);
|
|
53
|
+
const loopOnly = url.searchParams.get('loop_only') === 'true';
|
|
54
|
+
const filterStatus = url.searchParams.get('filter'); // active | completed
|
|
55
|
+
|
|
56
|
+
// Apply filters
|
|
57
|
+
let filteredTasks = tasks;
|
|
58
|
+
|
|
59
|
+
// Filter by loop_control.enabled
|
|
60
|
+
if (loopOnly) {
|
|
61
|
+
filteredTasks = filteredTasks.filter(t => t.loop_control?.enabled);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Filter by status
|
|
65
|
+
if (filterStatus) {
|
|
66
|
+
filteredTasks = filteredTasks.filter(t => t.status === filterStatus);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
70
|
+
res.end(JSON.stringify({
|
|
71
|
+
success: true,
|
|
72
|
+
data: filteredTasks,
|
|
73
|
+
total: filteredTasks.length,
|
|
74
|
+
timestamp: new Date().toISOString()
|
|
75
|
+
}));
|
|
76
|
+
return true;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
79
|
+
res.end(JSON.stringify({
|
|
80
|
+
success: false,
|
|
81
|
+
error: (error as Error).message
|
|
82
|
+
}));
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// POST /api/tasks - Create new task
|
|
88
|
+
if (pathname === '/api/tasks' && req.method === 'POST') {
|
|
89
|
+
handlePostRequest(req, res, async (body) => {
|
|
90
|
+
const task = body as Partial<Task>;
|
|
91
|
+
|
|
92
|
+
// Validate required fields
|
|
93
|
+
if (!task.id) {
|
|
94
|
+
return { success: false, error: 'Task ID is required', status: 400 };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Sanitize taskId to prevent path traversal
|
|
98
|
+
if (task.id.includes('/') || task.id.includes('\\') || task.id === '..' || task.id === '.') {
|
|
99
|
+
return { success: false, error: 'Invalid task ID format', status: 400 };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!task.loop_control) {
|
|
103
|
+
return { success: false, error: 'loop_control is required', status: 400 };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!task.loop_control.enabled) {
|
|
107
|
+
return { success: false, error: 'loop_control.enabled must be true', status: 400 };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!task.loop_control.cli_sequence || task.loop_control.cli_sequence.length === 0) {
|
|
111
|
+
return { success: false, error: 'cli_sequence must contain at least one step', status: 400 };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Ensure task directory exists
|
|
116
|
+
const { mkdir } = await import('fs/promises');
|
|
117
|
+
if (!existsSync(taskDir)) {
|
|
118
|
+
await mkdir(taskDir, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check if task already exists
|
|
122
|
+
const taskPath = join(taskDir, task.id + '.json');
|
|
123
|
+
if (existsSync(taskPath)) {
|
|
124
|
+
return { success: false, error: 'Task already exists: ' + task.id, status: 409 };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Build complete task object
|
|
128
|
+
const fullTask: Task = {
|
|
129
|
+
id: task.id,
|
|
130
|
+
title: task.title || task.id,
|
|
131
|
+
description: task.description || task.loop_control?.description || '',
|
|
132
|
+
status: task.status || 'active',
|
|
133
|
+
meta: task.meta,
|
|
134
|
+
context: task.context,
|
|
135
|
+
loop_control: task.loop_control
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Write task file
|
|
139
|
+
await writeFile(taskPath, JSON.stringify(fullTask, null, 2), 'utf-8');
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
data: {
|
|
144
|
+
task: fullTask,
|
|
145
|
+
path: taskPath
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return { success: false, error: (error as Error).message, status: 500 };
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// GET /api/tasks/:taskId - Get single task
|
|
156
|
+
const taskDetailMatch = pathname.match(/^\/api\/tasks\/([^\/]+)$/);
|
|
157
|
+
if (taskDetailMatch && req.method === 'GET') {
|
|
158
|
+
const taskId = decodeURIComponent(taskDetailMatch[1]);
|
|
159
|
+
|
|
160
|
+
// Sanitize taskId to prevent path traversal
|
|
161
|
+
if (taskId.includes('/') || taskId.includes('\\') || taskId === '..' || taskId === '.') {
|
|
162
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
163
|
+
res.end(JSON.stringify({ success: false, error: 'Invalid task ID format' }));
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const taskPath = join(taskDir, taskId + '.json');
|
|
169
|
+
|
|
170
|
+
if (!existsSync(taskPath)) {
|
|
171
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
172
|
+
res.end(JSON.stringify({ success: false, error: 'Task not found: ' + taskId }));
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const content = await readFile(taskPath, 'utf-8');
|
|
177
|
+
const task = JSON.parse(content) as Task;
|
|
178
|
+
|
|
179
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
180
|
+
res.end(JSON.stringify({
|
|
181
|
+
success: true,
|
|
182
|
+
data: {
|
|
183
|
+
task: task
|
|
184
|
+
}
|
|
185
|
+
}));
|
|
186
|
+
return true;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
189
|
+
res.end(JSON.stringify({
|
|
190
|
+
success: false,
|
|
191
|
+
error: (error as Error).message
|
|
192
|
+
}));
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// POST /api/tasks/validate - Validate task loop_control configuration
|
|
198
|
+
if (pathname === '/api/tasks/validate' && req.method === 'POST') {
|
|
199
|
+
handlePostRequest(req, res, async (body) => {
|
|
200
|
+
const task = body as Partial<Task>;
|
|
201
|
+
const errors: string[] = [];
|
|
202
|
+
const warnings: string[] = [];
|
|
203
|
+
|
|
204
|
+
// Validate loop_control
|
|
205
|
+
if (!task.loop_control) {
|
|
206
|
+
errors.push('loop_control is required');
|
|
207
|
+
} else {
|
|
208
|
+
// Check enabled flag
|
|
209
|
+
if (typeof task.loop_control.enabled !== 'boolean') {
|
|
210
|
+
errors.push('loop_control.enabled must be a boolean');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check cli_sequence
|
|
214
|
+
if (!task.loop_control.cli_sequence || !Array.isArray(task.loop_control.cli_sequence)) {
|
|
215
|
+
errors.push('loop_control.cli_sequence must be an array');
|
|
216
|
+
} else if (task.loop_control.cli_sequence.length === 0) {
|
|
217
|
+
errors.push('loop_control.cli_sequence must contain at least one step');
|
|
218
|
+
} else {
|
|
219
|
+
// Validate each step
|
|
220
|
+
task.loop_control.cli_sequence.forEach((step, index) => {
|
|
221
|
+
if (!step.step_id) {
|
|
222
|
+
errors.push(`Step ${index + 1}: step_id is required`);
|
|
223
|
+
}
|
|
224
|
+
if (!step.tool) {
|
|
225
|
+
errors.push(`Step ${index + 1}: tool is required`);
|
|
226
|
+
} else if (!['gemini', 'qwen', 'codex', 'claude', 'bash'].includes(step.tool)) {
|
|
227
|
+
warnings.push(`Step ${index + 1}: unknown tool '${step.tool}'`);
|
|
228
|
+
}
|
|
229
|
+
if (!step.prompt_template && step.tool !== 'bash') {
|
|
230
|
+
errors.push(`Step ${index + 1}: prompt_template is required for non-bash steps`);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Check max_iterations
|
|
236
|
+
if (task.loop_control.max_iterations !== undefined) {
|
|
237
|
+
if (typeof task.loop_control.max_iterations !== 'number' || task.loop_control.max_iterations < 1) {
|
|
238
|
+
errors.push('loop_control.max_iterations must be a positive number');
|
|
239
|
+
}
|
|
240
|
+
if (task.loop_control.max_iterations > 100) {
|
|
241
|
+
warnings.push('max_iterations > 100 may cause long execution times');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Return validation result
|
|
247
|
+
const isValid = errors.length === 0;
|
|
248
|
+
return {
|
|
249
|
+
success: true,
|
|
250
|
+
data: {
|
|
251
|
+
valid: isValid,
|
|
252
|
+
errors,
|
|
253
|
+
warnings
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// PUT /api/tasks/:taskId - Update existing task
|
|
261
|
+
if (pathname.match(/^\/api\/tasks\/[^/]+$/) && req.method === 'PUT') {
|
|
262
|
+
const taskId = pathname.split('/').pop();
|
|
263
|
+
if (!taskId) {
|
|
264
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
265
|
+
res.end(JSON.stringify({ success: false, error: 'Task ID required' }));
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Sanitize taskId to prevent path traversal
|
|
270
|
+
if (taskId.includes('/') || taskId.includes('\\') || taskId === '..' || taskId === '.') {
|
|
271
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
272
|
+
res.end(JSON.stringify({ success: false, error: 'Invalid task ID format' }));
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
handlePostRequest(req, res, async (body) => {
|
|
277
|
+
const updates = body as Partial<Task>;
|
|
278
|
+
const taskPath = join(taskDir, taskId + '.json');
|
|
279
|
+
|
|
280
|
+
// Check if task exists
|
|
281
|
+
if (!existsSync(taskPath)) {
|
|
282
|
+
return { success: false, error: 'Task not found: ' + taskId, status: 404 };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
// Read existing task
|
|
287
|
+
const existingContent = await readFile(taskPath, 'utf-8');
|
|
288
|
+
const existingTask = JSON.parse(existingContent) as Task;
|
|
289
|
+
|
|
290
|
+
// Merge updates (preserve id)
|
|
291
|
+
const updatedTask: Task = {
|
|
292
|
+
...existingTask,
|
|
293
|
+
...updates,
|
|
294
|
+
id: existingTask.id // Prevent id change
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// If loop_control is being updated, merge it properly
|
|
298
|
+
if (updates.loop_control) {
|
|
299
|
+
updatedTask.loop_control = {
|
|
300
|
+
...existingTask.loop_control,
|
|
301
|
+
...updates.loop_control
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Write updated task
|
|
306
|
+
await writeFile(taskPath, JSON.stringify(updatedTask, null, 2), 'utf-8');
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
success: true,
|
|
310
|
+
data: {
|
|
311
|
+
task: updatedTask,
|
|
312
|
+
path: taskPath
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
} catch (error) {
|
|
316
|
+
return { success: false, error: (error as Error).message, status: 500 };
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// GET /api/tasks/:taskId - Get specific task
|
|
323
|
+
if (pathname.match(/^\/api\/tasks\/[^/]+$/) && req.method === 'GET') {
|
|
324
|
+
const taskId = pathname.split('/').pop();
|
|
325
|
+
if (!taskId) {
|
|
326
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
327
|
+
res.end(JSON.stringify({ success: false, error: 'Task ID required' }));
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Sanitize taskId to prevent path traversal
|
|
332
|
+
if (taskId.includes('/') || taskId.includes('\\') || taskId === '..' || taskId === '.') {
|
|
333
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
334
|
+
res.end(JSON.stringify({ success: false, error: 'Invalid task ID format' }));
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
const taskPath = join(taskDir, taskId + '.json');
|
|
340
|
+
|
|
341
|
+
if (!existsSync(taskPath)) {
|
|
342
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
343
|
+
res.end(JSON.stringify({ success: false, error: 'Task not found' }));
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const content = await readFile(taskPath, 'utf-8');
|
|
348
|
+
const task = JSON.parse(content) as Task;
|
|
349
|
+
|
|
350
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
351
|
+
res.end(JSON.stringify({ success: true, data: task }));
|
|
352
|
+
return true;
|
|
353
|
+
} catch (error) {
|
|
354
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
355
|
+
res.end(JSON.stringify({ success: false, error: (error as Error).message }));
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return false;
|
|
361
|
+
}
|