claude-code-workflow 6.3.37 → 6.3.39
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/.codex/agents/action-planning-agent.md +885 -0
- package/.codex/agents/ccw-loop-b-complete.md +227 -0
- package/.codex/agents/ccw-loop-b-debug.md +172 -0
- package/.codex/agents/ccw-loop-b-develop.md +147 -0
- package/.codex/agents/ccw-loop-b-init.md +82 -0
- package/.codex/agents/ccw-loop-b-validate.md +204 -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/clean.md +409 -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 +301 -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/.codex/skills/ccw-loop-b/specs/action-catalog.md +383 -0
- package/.codex/skills/parallel-dev-cycle/README.md +382 -0
- package/.codex/skills/parallel-dev-cycle/SKILL.md +512 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/code-developer.md +242 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/exploration-planner.md +285 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/requirements-analyst.md +285 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/validation-archivist.md +381 -0
- package/.codex/skills/parallel-dev-cycle/phases/orchestrator.md +696 -0
- package/.codex/skills/parallel-dev-cycle/phases/state-schema.md +436 -0
- package/.codex/skills/parallel-dev-cycle/specs/communication-optimization.md +423 -0
- package/.codex/skills/parallel-dev-cycle/specs/coordination-protocol.md +391 -0
- package/.codex/skills/parallel-dev-cycle/specs/versioning-strategy.md +330 -0
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +4 -0
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/install.d.ts.map +1 -1
- package/ccw/dist/commands/install.js +39 -8
- 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/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/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/cli-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 +2 -2
- package/ccw/dist/core/routes/codexlens/semantic-handlers.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-v2-routes.d.ts +9 -0
- package/ccw/dist/core/routes/loop-v2-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/loop-v2-routes.js +56 -4
- package/ccw/dist/core/routes/loop-v2-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/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +5 -3
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +4 -3
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/cli-config-manager.d.ts +1 -0
- package/ccw/dist/tools/cli-config-manager.d.ts.map +1 -1
- package/ccw/dist/tools/cli-config-manager.js +2 -1
- package/ccw/dist/tools/cli-config-manager.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-task-manager.d.ts +13 -2
- package/ccw/dist/tools/loop-task-manager.d.ts.map +1 -1
- package/ccw/dist/tools/loop-task-manager.js.map +1 -1
- 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/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 +4 -0
- package/ccw/src/commands/install.ts +51 -8
- package/ccw/src/commands/issue.ts +119 -0
- package/ccw/src/commands/upgrade.ts +1 -1
- package/ccw/src/config/litellm-api-config-manager.ts +3 -2
- package/ccw/src/core/memory-embedder-bridge.ts +2 -6
- package/ccw/src/core/routes/cli-routes.ts +1 -1
- package/ccw/src/core/routes/codexlens/config-handlers.ts +7 -6
- package/ccw/src/core/routes/codexlens/semantic-handlers.ts +2 -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-v2-routes.ts +64 -6
- package/ccw/src/core/routes/system-routes.ts +3 -2
- package/ccw/src/core/server.ts +6 -3
- 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/32-issue-manager.css +32 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +48 -48
- package/ccw/src/templates/dashboard-js/components/navigation.js +6 -0
- package/ccw/src/templates/dashboard-js/components/notifications.js +6 -0
- package/ccw/src/templates/dashboard-js/components/version-check.js +38 -0
- package/ccw/src/templates/dashboard-js/i18n.js +126 -0
- package/ccw/src/templates/dashboard-js/state.js +2 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +1 -1
- 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 +112 -11
- package/ccw/src/templates/dashboard.html +48 -2
- package/ccw/src/tools/claude-cli-tools.ts +4 -3
- package/ccw/src/tools/cli-config-manager.ts +3 -1
- 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-task-manager.ts +13 -2
- package/ccw/src/tools/native-session-discovery.ts +38 -7
- package/ccw/src/utils/codexlens-path.ts +60 -0
- package/ccw/src/utils/uv-manager.ts +3 -2
- package/package.json +1 -1
|
@@ -203,18 +203,20 @@ function getIssueDetail(issuesDir, issueId) {
|
|
|
203
203
|
function enrichIssues(issues, issuesDir) {
|
|
204
204
|
return issues.map(issue => {
|
|
205
205
|
const solutions = readSolutionsJsonl(issuesDir, issue.id);
|
|
206
|
-
let
|
|
207
|
-
// Get
|
|
206
|
+
let tasks = [];
|
|
207
|
+
// Get tasks from bound solution
|
|
208
208
|
if (issue.bound_solution_id) {
|
|
209
209
|
const boundSol = solutions.find(s => s.id === issue.bound_solution_id);
|
|
210
210
|
if (boundSol?.tasks) {
|
|
211
|
-
|
|
211
|
+
tasks = boundSol.tasks;
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
return {
|
|
215
215
|
...issue,
|
|
216
|
+
solutions, // Add full solutions array
|
|
217
|
+
tasks, // Add full tasks array
|
|
216
218
|
solution_count: solutions.length,
|
|
217
|
-
task_count:
|
|
219
|
+
task_count: tasks.length
|
|
218
220
|
};
|
|
219
221
|
});
|
|
220
222
|
}
|
|
@@ -310,37 +312,52 @@ export async function handleIssueRoutes(ctx) {
|
|
|
310
312
|
return true;
|
|
311
313
|
}
|
|
312
314
|
const issuesDir = join(projectPath, '.workflow', 'issues');
|
|
313
|
-
// =====
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
// ===== Helper: Normalize queue path (supports both /api/queue/* and /api/issues/queue/*) =====
|
|
316
|
+
const normalizeQueuePath = (path) => {
|
|
317
|
+
if (path.startsWith('/api/issues/queue')) {
|
|
318
|
+
return path.replace('/api/issues/queue', '/api/queue');
|
|
319
|
+
}
|
|
320
|
+
if (path.startsWith('/api/queue')) {
|
|
321
|
+
return path;
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
};
|
|
325
|
+
const normalizedPath = normalizeQueuePath(pathname);
|
|
326
|
+
// ===== Queue Routes (supports both /api/queue/* and /api/issues/queue/*) =====
|
|
327
|
+
// GET /api/queue or /api/issues/queue - Get execution queue
|
|
328
|
+
if ((normalizedPath === '/api/queue') && req.method === 'GET') {
|
|
316
329
|
const queue = groupQueueByExecutionGroup(readQueue(issuesDir));
|
|
317
330
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
318
331
|
res.end(JSON.stringify(queue));
|
|
319
332
|
return true;
|
|
320
333
|
}
|
|
321
|
-
// GET /api/queue/history - Get queue history (all queues from index)
|
|
322
|
-
if (
|
|
334
|
+
// GET /api/queue/history or /api/issues/queue/history - Get queue history (all queues from index)
|
|
335
|
+
if (normalizedPath === '/api/queue/history' && req.method === 'GET') {
|
|
323
336
|
const queuesDir = join(issuesDir, 'queues');
|
|
324
337
|
const indexPath = join(queuesDir, 'index.json');
|
|
325
338
|
if (!existsSync(indexPath)) {
|
|
326
339
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
327
|
-
res.end(JSON.stringify({ queues: [], active_queue_id: null }));
|
|
340
|
+
res.end(JSON.stringify({ queues: [], active_queue_id: null, active_queue_ids: [] }));
|
|
328
341
|
return true;
|
|
329
342
|
}
|
|
330
343
|
try {
|
|
331
344
|
const index = JSON.parse(readFileSync(indexPath, 'utf8'));
|
|
345
|
+
// Ensure active_queue_ids is always returned for multi-queue support
|
|
346
|
+
if (!index.active_queue_ids) {
|
|
347
|
+
index.active_queue_ids = index.active_queue_id ? [index.active_queue_id] : [];
|
|
348
|
+
}
|
|
332
349
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
333
350
|
res.end(JSON.stringify(index));
|
|
334
351
|
}
|
|
335
352
|
catch {
|
|
336
353
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
337
|
-
res.end(JSON.stringify({ queues: [], active_queue_id: null }));
|
|
354
|
+
res.end(JSON.stringify({ queues: [], active_queue_id: null, active_queue_ids: [] }));
|
|
338
355
|
}
|
|
339
356
|
return true;
|
|
340
357
|
}
|
|
341
|
-
// GET /api/queue/:id - Get specific queue by ID
|
|
342
|
-
const queueDetailMatch =
|
|
343
|
-
const reservedQueuePaths = ['history', 'reorder', 'switch', 'deactivate', 'merge'];
|
|
358
|
+
// GET /api/queue/:id or /api/issues/queue/:id - Get specific queue by ID
|
|
359
|
+
const queueDetailMatch = normalizedPath?.match(/^\/api\/queue\/([^/]+)$/);
|
|
360
|
+
const reservedQueuePaths = ['history', 'reorder', 'switch', 'deactivate', 'merge', 'activate'];
|
|
344
361
|
if (queueDetailMatch && req.method === 'GET' && !reservedQueuePaths.includes(queueDetailMatch[1])) {
|
|
345
362
|
const queueId = queueDetailMatch[1];
|
|
346
363
|
const queuesDir = join(issuesDir, 'queues');
|
|
@@ -361,8 +378,47 @@ export async function handleIssueRoutes(ctx) {
|
|
|
361
378
|
}
|
|
362
379
|
return true;
|
|
363
380
|
}
|
|
364
|
-
// POST /api/queue/
|
|
365
|
-
if (
|
|
381
|
+
// POST /api/queue/activate or /api/issues/queue/activate - Activate one or more queues (multi-queue support)
|
|
382
|
+
if (normalizedPath === '/api/queue/activate' && req.method === 'POST') {
|
|
383
|
+
handlePostRequest(req, res, async (body) => {
|
|
384
|
+
const { queueId, queueIds } = body;
|
|
385
|
+
// Support both single queueId and array queueIds
|
|
386
|
+
const idsToActivate = queueIds
|
|
387
|
+
? (Array.isArray(queueIds) ? queueIds : [queueIds])
|
|
388
|
+
: (queueId ? [queueId] : []);
|
|
389
|
+
if (idsToActivate.length === 0) {
|
|
390
|
+
return { error: 'queueId or queueIds required' };
|
|
391
|
+
}
|
|
392
|
+
const queuesDir = join(issuesDir, 'queues');
|
|
393
|
+
const indexPath = join(queuesDir, 'index.json');
|
|
394
|
+
// Validate all queue IDs exist
|
|
395
|
+
for (const id of idsToActivate) {
|
|
396
|
+
const queueFilePath = join(queuesDir, `${id}.json`);
|
|
397
|
+
if (!existsSync(queueFilePath)) {
|
|
398
|
+
return { error: `Queue ${id} not found` };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const index = existsSync(indexPath)
|
|
403
|
+
? JSON.parse(readFileSync(indexPath, 'utf8'))
|
|
404
|
+
: { active_queue_id: null, active_queue_ids: [], queues: [] };
|
|
405
|
+
index.active_queue_ids = idsToActivate;
|
|
406
|
+
index.active_queue_id = idsToActivate[0] || null; // Backward compat
|
|
407
|
+
writeFileSync(indexPath, JSON.stringify(index, null, 2));
|
|
408
|
+
return {
|
|
409
|
+
success: true,
|
|
410
|
+
active_queue_ids: idsToActivate,
|
|
411
|
+
active_queue_id: idsToActivate[0] || null // Backward compat
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
return { error: 'Failed to activate queue(s)' };
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
// POST /api/queue/switch or /api/issues/queue/switch - Switch active queue (legacy, single queue)
|
|
421
|
+
if (normalizedPath === '/api/queue/switch' && req.method === 'POST') {
|
|
366
422
|
handlePostRequest(req, res, async (body) => {
|
|
367
423
|
const { queueId } = body;
|
|
368
424
|
if (!queueId)
|
|
@@ -376,10 +432,15 @@ export async function handleIssueRoutes(ctx) {
|
|
|
376
432
|
try {
|
|
377
433
|
const index = existsSync(indexPath)
|
|
378
434
|
? JSON.parse(readFileSync(indexPath, 'utf8'))
|
|
379
|
-
: { active_queue_id: null, queues: [] };
|
|
435
|
+
: { active_queue_id: null, active_queue_ids: [], queues: [] };
|
|
380
436
|
index.active_queue_id = queueId;
|
|
437
|
+
index.active_queue_ids = [queueId]; // Also update multi-queue array
|
|
381
438
|
writeFileSync(indexPath, JSON.stringify(index, null, 2));
|
|
382
|
-
return {
|
|
439
|
+
return {
|
|
440
|
+
success: true,
|
|
441
|
+
active_queue_id: queueId,
|
|
442
|
+
active_queue_ids: [queueId]
|
|
443
|
+
};
|
|
383
444
|
}
|
|
384
445
|
catch (err) {
|
|
385
446
|
return { error: 'Failed to switch queue' };
|
|
@@ -387,19 +448,38 @@ export async function handleIssueRoutes(ctx) {
|
|
|
387
448
|
});
|
|
388
449
|
return true;
|
|
389
450
|
}
|
|
390
|
-
// POST /api/queue/deactivate - Deactivate
|
|
391
|
-
if (
|
|
451
|
+
// POST /api/queue/deactivate or /api/issues/queue/deactivate - Deactivate queue(s)
|
|
452
|
+
if (normalizedPath === '/api/queue/deactivate' && req.method === 'POST') {
|
|
392
453
|
handlePostRequest(req, res, async (body) => {
|
|
454
|
+
const { queueId } = body; // Optional: specific queue to deactivate
|
|
393
455
|
const queuesDir = join(issuesDir, 'queues');
|
|
394
456
|
const indexPath = join(queuesDir, 'index.json');
|
|
395
457
|
try {
|
|
396
458
|
const index = existsSync(indexPath)
|
|
397
459
|
? JSON.parse(readFileSync(indexPath, 'utf8'))
|
|
398
|
-
: { active_queue_id: null, queues: [] };
|
|
399
|
-
const
|
|
400
|
-
|
|
460
|
+
: { active_queue_id: null, active_queue_ids: [], queues: [] };
|
|
461
|
+
const currentActiveIds = index.active_queue_ids || (index.active_queue_id ? [index.active_queue_id] : []);
|
|
462
|
+
let deactivatedIds = [];
|
|
463
|
+
let remainingIds = [];
|
|
464
|
+
if (queueId) {
|
|
465
|
+
// Deactivate specific queue
|
|
466
|
+
deactivatedIds = currentActiveIds.includes(queueId) ? [queueId] : [];
|
|
467
|
+
remainingIds = currentActiveIds.filter((id) => id !== queueId);
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
// Deactivate all
|
|
471
|
+
deactivatedIds = [...currentActiveIds];
|
|
472
|
+
remainingIds = [];
|
|
473
|
+
}
|
|
474
|
+
index.active_queue_ids = remainingIds;
|
|
475
|
+
index.active_queue_id = remainingIds[0] || null; // Backward compat
|
|
401
476
|
writeFileSync(indexPath, JSON.stringify(index, null, 2));
|
|
402
|
-
return {
|
|
477
|
+
return {
|
|
478
|
+
success: true,
|
|
479
|
+
deactivated_queue_ids: deactivatedIds,
|
|
480
|
+
active_queue_ids: remainingIds,
|
|
481
|
+
active_queue_id: remainingIds[0] || null // Backward compat
|
|
482
|
+
};
|
|
403
483
|
}
|
|
404
484
|
catch (err) {
|
|
405
485
|
return { error: 'Failed to deactivate queue' };
|
|
@@ -407,8 +487,8 @@ export async function handleIssueRoutes(ctx) {
|
|
|
407
487
|
});
|
|
408
488
|
return true;
|
|
409
489
|
}
|
|
410
|
-
// POST /api/queue/reorder - Reorder queue items (supports both solutions and tasks)
|
|
411
|
-
if (
|
|
490
|
+
// POST /api/queue/reorder or /api/issues/queue/reorder - Reorder queue items (supports both solutions and tasks)
|
|
491
|
+
if (normalizedPath === '/api/queue/reorder' && req.method === 'POST') {
|
|
412
492
|
handlePostRequest(req, res, async (body) => {
|
|
413
493
|
const { groupId, newOrder } = body;
|
|
414
494
|
if (!groupId || !Array.isArray(newOrder)) {
|
|
@@ -454,8 +534,8 @@ export async function handleIssueRoutes(ctx) {
|
|
|
454
534
|
});
|
|
455
535
|
return true;
|
|
456
536
|
}
|
|
457
|
-
// DELETE /api/queue/:queueId/item/:itemId
|
|
458
|
-
const queueItemDeleteMatch =
|
|
537
|
+
// DELETE /api/queue/:queueId/item/:itemId or /api/issues/queue/:queueId/item/:itemId
|
|
538
|
+
const queueItemDeleteMatch = normalizedPath?.match(/^\/api\/queue\/([^/]+)\/item\/([^/]+)$/);
|
|
459
539
|
if (queueItemDeleteMatch && req.method === 'DELETE') {
|
|
460
540
|
const queueId = queueItemDeleteMatch[1];
|
|
461
541
|
const itemId = decodeURIComponent(queueItemDeleteMatch[2]);
|
|
@@ -523,8 +603,8 @@ export async function handleIssueRoutes(ctx) {
|
|
|
523
603
|
}
|
|
524
604
|
return true;
|
|
525
605
|
}
|
|
526
|
-
// DELETE /api/queue/:queueId - Delete entire queue
|
|
527
|
-
const queueDeleteMatch =
|
|
606
|
+
// DELETE /api/queue/:queueId or /api/issues/queue/:queueId - Delete entire queue
|
|
607
|
+
const queueDeleteMatch = normalizedPath?.match(/^\/api\/queue\/([^/]+)$/);
|
|
528
608
|
if (queueDeleteMatch && req.method === 'DELETE') {
|
|
529
609
|
const queueId = queueDeleteMatch[1];
|
|
530
610
|
const queuesDir = join(issuesDir, 'queues');
|
|
@@ -558,8 +638,8 @@ export async function handleIssueRoutes(ctx) {
|
|
|
558
638
|
}
|
|
559
639
|
return true;
|
|
560
640
|
}
|
|
561
|
-
// POST /api/queue/merge - Merge source queue into target queue
|
|
562
|
-
if (
|
|
641
|
+
// POST /api/queue/merge or /api/issues/queue/merge - Merge source queue into target queue
|
|
642
|
+
if (normalizedPath === '/api/queue/merge' && req.method === 'POST') {
|
|
563
643
|
handlePostRequest(req, res, async (body) => {
|
|
564
644
|
const { sourceQueueId, targetQueueId } = body;
|
|
565
645
|
if (!sourceQueueId || !targetQueueId) {
|
|
@@ -681,7 +761,7 @@ export async function handleIssueRoutes(ctx) {
|
|
|
681
761
|
return true;
|
|
682
762
|
}
|
|
683
763
|
// POST /api/queue/split - Split items from source queue into a new queue
|
|
684
|
-
if (
|
|
764
|
+
if (normalizedPath === '/api/queue/split' && req.method === 'POST') {
|
|
685
765
|
handlePostRequest(req, res, async (body) => {
|
|
686
766
|
const { sourceQueueId, itemIds } = body;
|
|
687
767
|
if (!sourceQueueId || !itemIds || !Array.isArray(itemIds) || itemIds.length === 0) {
|
|
@@ -889,6 +969,173 @@ export async function handleIssueRoutes(ctx) {
|
|
|
889
969
|
});
|
|
890
970
|
return true;
|
|
891
971
|
}
|
|
972
|
+
// POST /api/issues/pull - Pull issues from GitHub
|
|
973
|
+
if (pathname === '/api/issues/pull' && req.method === 'POST') {
|
|
974
|
+
const state = url.searchParams.get('state') || 'open';
|
|
975
|
+
const limit = parseInt(url.searchParams.get('limit') || '100');
|
|
976
|
+
const labels = url.searchParams.get('labels') || '';
|
|
977
|
+
const downloadImages = url.searchParams.get('downloadImages') === 'true';
|
|
978
|
+
try {
|
|
979
|
+
const { execSync } = await import('child_process');
|
|
980
|
+
const https = await import('https');
|
|
981
|
+
const http = await import('http');
|
|
982
|
+
// Check if gh CLI is available
|
|
983
|
+
try {
|
|
984
|
+
execSync('gh --version', { stdio: 'ignore', timeout: 5000 });
|
|
985
|
+
}
|
|
986
|
+
catch {
|
|
987
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
988
|
+
res.end(JSON.stringify({ error: 'GitHub CLI (gh) is not installed or not in PATH' }));
|
|
989
|
+
return true;
|
|
990
|
+
}
|
|
991
|
+
// Build gh command
|
|
992
|
+
let ghCommand = `gh issue list --state ${state} --limit ${limit} --json number,title,body,labels,url,state`;
|
|
993
|
+
if (labels)
|
|
994
|
+
ghCommand += ` --label "${labels}"`;
|
|
995
|
+
// Execute gh command from project root
|
|
996
|
+
const ghOutput = execSync(ghCommand, {
|
|
997
|
+
encoding: 'utf-8',
|
|
998
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
999
|
+
timeout: 60000,
|
|
1000
|
+
cwd: issuesDir.replace(/[\\/]\.workflow[\\/]issues$/, '')
|
|
1001
|
+
}).trim();
|
|
1002
|
+
if (!ghOutput) {
|
|
1003
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1004
|
+
res.end(JSON.stringify({ imported: 0, updated: 0, skipped: 0, images_downloaded: 0 }));
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
const ghIssues = JSON.parse(ghOutput);
|
|
1008
|
+
const existingIssues = readIssuesJsonl(issuesDir);
|
|
1009
|
+
let imported = 0;
|
|
1010
|
+
let skipped = 0;
|
|
1011
|
+
let updated = 0;
|
|
1012
|
+
let imagesDownloaded = 0;
|
|
1013
|
+
// Create images directory if needed
|
|
1014
|
+
const imagesDir = join(issuesDir, 'images');
|
|
1015
|
+
if (downloadImages && !existsSync(imagesDir)) {
|
|
1016
|
+
mkdirSync(imagesDir, { recursive: true });
|
|
1017
|
+
}
|
|
1018
|
+
// Helper function to download image
|
|
1019
|
+
const downloadImage = async (imageUrl, issueNumber, imageIndex) => {
|
|
1020
|
+
return new Promise((resolveDownload) => {
|
|
1021
|
+
try {
|
|
1022
|
+
const ext = imageUrl.match(/\.(png|jpg|jpeg|gif|webp|svg)/i)?.[1] || 'png';
|
|
1023
|
+
const filename = `GH-${issueNumber}-${imageIndex}.${ext}`;
|
|
1024
|
+
const filePath = join(imagesDir, filename);
|
|
1025
|
+
// Skip if already downloaded
|
|
1026
|
+
if (existsSync(filePath)) {
|
|
1027
|
+
resolveDownload(`.workflow/issues/images/${filename}`);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const protocol = imageUrl.startsWith('https') ? https : http;
|
|
1031
|
+
protocol.get(imageUrl, { timeout: 30000 }, (response) => {
|
|
1032
|
+
// Handle redirect
|
|
1033
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
1034
|
+
const redirectUrl = response.headers.location;
|
|
1035
|
+
if (redirectUrl) {
|
|
1036
|
+
downloadImage(redirectUrl, issueNumber, imageIndex).then(resolveDownload);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (response.statusCode !== 200) {
|
|
1041
|
+
resolveDownload(null);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
const chunks = [];
|
|
1045
|
+
response.on('data', (chunk) => chunks.push(chunk));
|
|
1046
|
+
response.on('end', () => {
|
|
1047
|
+
try {
|
|
1048
|
+
writeFileSync(filePath, Buffer.concat(chunks));
|
|
1049
|
+
resolveDownload(`.workflow/issues/images/${filename}`);
|
|
1050
|
+
}
|
|
1051
|
+
catch {
|
|
1052
|
+
resolveDownload(null);
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
response.on('error', () => resolveDownload(null));
|
|
1056
|
+
}).on('error', () => resolveDownload(null));
|
|
1057
|
+
}
|
|
1058
|
+
catch {
|
|
1059
|
+
resolveDownload(null);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
};
|
|
1063
|
+
// Process issues
|
|
1064
|
+
for (const ghIssue of ghIssues) {
|
|
1065
|
+
const issueId = `GH-${ghIssue.number}`;
|
|
1066
|
+
const existingIssue = existingIssues.find((i) => i.id === issueId);
|
|
1067
|
+
let context = ghIssue.body || ghIssue.title;
|
|
1068
|
+
// Extract and download images if enabled
|
|
1069
|
+
if (downloadImages && ghIssue.body) {
|
|
1070
|
+
// Find all image URLs in the body
|
|
1071
|
+
const imgPattern = /!\[[^\]]*\]\((https?:\/\/[^)]+)\)|<img[^>]+src=["'](https?:\/\/[^"']+)["']/gi;
|
|
1072
|
+
const imageUrls = [];
|
|
1073
|
+
let match;
|
|
1074
|
+
while ((match = imgPattern.exec(ghIssue.body)) !== null) {
|
|
1075
|
+
imageUrls.push(match[1] || match[2]);
|
|
1076
|
+
}
|
|
1077
|
+
// Download images and build reference list
|
|
1078
|
+
if (imageUrls.length > 0) {
|
|
1079
|
+
const downloadedImages = [];
|
|
1080
|
+
for (let i = 0; i < imageUrls.length; i++) {
|
|
1081
|
+
const localPath = await downloadImage(imageUrls[i], ghIssue.number, i + 1);
|
|
1082
|
+
if (localPath) {
|
|
1083
|
+
downloadedImages.push(localPath);
|
|
1084
|
+
imagesDownloaded++;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
// Append image references to context
|
|
1088
|
+
if (downloadedImages.length > 0) {
|
|
1089
|
+
context += '\n\n---\n**Downloaded Images:**\n';
|
|
1090
|
+
downloadedImages.forEach((path, idx) => {
|
|
1091
|
+
context += `- Image ${idx + 1}: \`${path}\`\n`;
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
// Prepare issue data (truncate context to 2000 chars max)
|
|
1097
|
+
const issueData = {
|
|
1098
|
+
id: issueId,
|
|
1099
|
+
title: ghIssue.title,
|
|
1100
|
+
status: ghIssue.state === 'OPEN' ? 'registered' : 'completed',
|
|
1101
|
+
priority: 3,
|
|
1102
|
+
context: context.substring(0, 2000),
|
|
1103
|
+
source: 'github',
|
|
1104
|
+
source_url: ghIssue.url,
|
|
1105
|
+
tags: ghIssue.labels?.map((l) => l.name) || [],
|
|
1106
|
+
created_at: new Date().toISOString(),
|
|
1107
|
+
updated_at: new Date().toISOString()
|
|
1108
|
+
};
|
|
1109
|
+
if (existingIssue) {
|
|
1110
|
+
// Update if changed
|
|
1111
|
+
const newStatus = ghIssue.state === 'OPEN' ? 'registered' : 'completed';
|
|
1112
|
+
if (existingIssue.status !== newStatus || existingIssue.title !== ghIssue.title) {
|
|
1113
|
+
existingIssue.title = ghIssue.title;
|
|
1114
|
+
existingIssue.status = newStatus;
|
|
1115
|
+
existingIssue.context = issueData.context;
|
|
1116
|
+
existingIssue.updated_at = new Date().toISOString();
|
|
1117
|
+
updated++;
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
skipped++;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
else {
|
|
1124
|
+
existingIssues.push(issueData);
|
|
1125
|
+
imported++;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
// Save all issues
|
|
1129
|
+
writeIssuesJsonl(issuesDir, existingIssues);
|
|
1130
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1131
|
+
res.end(JSON.stringify({ imported, updated, skipped, images_downloaded: imagesDownloaded, total: ghIssues.length }));
|
|
1132
|
+
}
|
|
1133
|
+
catch (err) {
|
|
1134
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1135
|
+
res.end(JSON.stringify({ error: err.message || 'Failed to pull issues from GitHub' }));
|
|
1136
|
+
}
|
|
1137
|
+
return true;
|
|
1138
|
+
}
|
|
892
1139
|
// GET /api/issues/:id - Get issue detail
|
|
893
1140
|
const detailMatch = pathname.match(/^\/api\/issues\/([^/]+)$/);
|
|
894
1141
|
if (detailMatch && req.method === 'GET') {
|