claude-code-workflow 6.3.30 → 6.3.32

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.
Files changed (47) hide show
  1. package/.claude/CLAUDE.md +2 -3
  2. package/.claude/commands/workflow/clean.md +3 -3
  3. package/.claude/commands/workflow/init.md +25 -12
  4. package/.claude/commands/workflow/lite-execute.md +2 -2
  5. package/.claude/commands/workflow/session/complete.md +31 -4
  6. package/.claude/commands/workflow/session/start.md +1 -1
  7. package/.claude/commands/workflow/tools/context-gather.md +2 -2
  8. package/.claude/skills/ccw-help/command.json +10 -1
  9. package/.claude/workflows/chinese-response.md +1 -1
  10. package/.claude/workflows/cli-templates/schemas/project-guidelines-schema.json +141 -0
  11. package/.claude/workflows/cli-templates/schemas/{project-json-schema.json → project-tech-schema.json} +2 -2
  12. package/.claude/workflows/cli-tools-usage.md +28 -4
  13. package/ccw/dist/cli.js +1 -1
  14. package/ccw/dist/cli.js.map +1 -1
  15. package/ccw/dist/commands/cli.d.ts +0 -1
  16. package/ccw/dist/commands/cli.d.ts.map +1 -1
  17. package/ccw/dist/commands/cli.js +4 -4
  18. package/ccw/dist/commands/cli.js.map +1 -1
  19. package/ccw/dist/core/data-aggregator.d.ts +13 -2
  20. package/ccw/dist/core/data-aggregator.d.ts.map +1 -1
  21. package/ccw/dist/core/data-aggregator.js +10 -16
  22. package/ccw/dist/core/data-aggregator.js.map +1 -1
  23. package/ccw/dist/core/routes/help-routes.d.ts.map +1 -1
  24. package/ccw/dist/core/routes/help-routes.js +156 -58
  25. package/ccw/dist/core/routes/help-routes.js.map +1 -1
  26. package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -1
  27. package/ccw/dist/core/routes/issue-routes.js +67 -0
  28. package/ccw/dist/core/routes/issue-routes.js.map +1 -1
  29. package/ccw/dist/tools/cli-executor-core.d.ts.map +1 -1
  30. package/ccw/dist/tools/cli-executor-core.js +11 -36
  31. package/ccw/dist/tools/cli-executor-core.js.map +1 -1
  32. package/ccw/dist/tools/cli-executor-utils.d.ts.map +1 -1
  33. package/ccw/dist/tools/cli-executor-utils.js +16 -1
  34. package/ccw/dist/tools/cli-executor-utils.js.map +1 -1
  35. package/ccw/src/cli.ts +1 -1
  36. package/ccw/src/commands/cli.ts +5 -5
  37. package/ccw/src/core/data-aggregator.ts +25 -19
  38. package/ccw/src/core/routes/help-routes.ts +172 -60
  39. package/ccw/src/core/routes/issue-routes.ts +81 -0
  40. package/ccw/src/templates/dashboard-css/32-issue-manager.css +28 -0
  41. package/ccw/src/templates/dashboard-js/components/hook-manager.js +120 -0
  42. package/ccw/src/templates/dashboard-js/i18n.js +20 -0
  43. package/ccw/src/templates/dashboard-js/views/hook-manager.js +21 -5
  44. package/ccw/src/templates/dashboard-js/views/issue-manager.js +108 -0
  45. package/ccw/src/tools/cli-executor-core.ts +13 -38
  46. package/ccw/src/tools/cli-executor-utils.ts +15 -1
  47. package/package.json +1 -1
@@ -562,6 +562,9 @@ function renderQueueCard(queue, isActive) {
562
562
  <i data-lucide="git-merge" class="w-3 h-3"></i>
563
563
  </button>
564
564
  ` : ''}
565
+ <button class="btn-sm btn-danger" onclick="confirmDeleteQueue('${safeQueueId}')" title="${t('issues.deleteQueue') || 'Delete queue'}">
566
+ <i data-lucide="trash-2" class="w-3 h-3"></i>
567
+ </button>
565
568
  </div>
566
569
  </div>
567
570
  `;
@@ -619,6 +622,33 @@ async function deactivateQueue(queueId) {
619
622
  }
620
623
  }
621
624
 
625
+ function confirmDeleteQueue(queueId) {
626
+ const msg = t('issues.confirmDeleteQueue') || 'Are you sure you want to delete this queue? This action cannot be undone.';
627
+ if (confirm(msg)) {
628
+ deleteQueue(queueId);
629
+ }
630
+ }
631
+
632
+ async function deleteQueue(queueId) {
633
+ try {
634
+ const response = await fetch('/api/queue/' + encodeURIComponent(queueId) + '?path=' + encodeURIComponent(projectPath), {
635
+ method: 'DELETE'
636
+ });
637
+ const result = await response.json();
638
+ if (result.success) {
639
+ showNotification(t('issues.queueDeleted') || 'Queue deleted successfully', 'success');
640
+ queueData.expandedQueueId = null;
641
+ await Promise.all([loadQueueData(), loadAllQueues()]);
642
+ renderIssueView();
643
+ } else {
644
+ showNotification(result.error || 'Failed to delete queue', 'error');
645
+ }
646
+ } catch (err) {
647
+ console.error('Failed to delete queue:', err);
648
+ showNotification('Failed to delete queue', 'error');
649
+ }
650
+ }
651
+
622
652
  async function renderExpandedQueueView(queueId) {
623
653
  const safeQueueId = escapeHtml(queueId || '');
624
654
  // Fetch queue detail
@@ -1405,6 +1435,23 @@ function renderIssueDetailPanel(issue) {
1405
1435
  `).join('') : '<p class="text-sm text-muted-foreground">' + (t('issues.noTasks') || 'No tasks') + '</p>'}
1406
1436
  </div>
1407
1437
  </div>
1438
+
1439
+ <!-- Actions -->
1440
+ <div class="detail-section issue-detail-actions">
1441
+ <label class="detail-label">${t('issues.actions') || 'Actions'}</label>
1442
+ <div class="flex gap-2 flex-wrap">
1443
+ ${!issue._isArchived ? `
1444
+ <button class="btn-secondary btn-sm" onclick="confirmArchiveIssue('${issue.id}')">
1445
+ <i data-lucide="archive" class="w-4 h-4"></i>
1446
+ ${t('issues.archive') || 'Archive'}
1447
+ </button>
1448
+ ` : ''}
1449
+ <button class="btn-secondary btn-sm btn-danger" onclick="confirmDeleteIssue('${issue.id}', ${issue._isArchived || false})">
1450
+ <i data-lucide="trash-2" class="w-4 h-4"></i>
1451
+ ${t('issues.delete') || 'Delete'}
1452
+ </button>
1453
+ </div>
1454
+ </div>
1408
1455
  </div>
1409
1456
  `;
1410
1457
 
@@ -1419,6 +1466,67 @@ function closeIssueDetail() {
1419
1466
  issueData.selectedIssue = null;
1420
1467
  }
1421
1468
 
1469
+ // ========== Issue Delete & Archive ==========
1470
+ function confirmDeleteIssue(issueId, isArchived) {
1471
+ const msg = t('issues.confirmDeleteIssue') || 'Are you sure you want to delete this issue? This action cannot be undone.';
1472
+ if (confirm(msg)) {
1473
+ deleteIssue(issueId, isArchived);
1474
+ }
1475
+ }
1476
+
1477
+ async function deleteIssue(issueId, isArchived) {
1478
+ try {
1479
+ const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
1480
+ method: 'DELETE'
1481
+ });
1482
+ const result = await response.json();
1483
+ if (result.success) {
1484
+ showNotification(t('issues.issueDeleted') || 'Issue deleted successfully', 'success');
1485
+ closeIssueDetail();
1486
+ if (isArchived) {
1487
+ issueData.historyIssues = issueData.historyIssues.filter(i => i.id !== issueId);
1488
+ } else {
1489
+ issueData.issues = issueData.issues.filter(i => i.id !== issueId);
1490
+ }
1491
+ renderIssueView();
1492
+ updateIssueBadge();
1493
+ } else {
1494
+ showNotification(result.error || 'Failed to delete issue', 'error');
1495
+ }
1496
+ } catch (err) {
1497
+ console.error('Failed to delete issue:', err);
1498
+ showNotification('Failed to delete issue', 'error');
1499
+ }
1500
+ }
1501
+
1502
+ function confirmArchiveIssue(issueId) {
1503
+ const msg = t('issues.confirmArchiveIssue') || 'Archive this issue? It will be moved to history.';
1504
+ if (confirm(msg)) {
1505
+ archiveIssue(issueId);
1506
+ }
1507
+ }
1508
+
1509
+ async function archiveIssue(issueId) {
1510
+ try {
1511
+ const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '/archive?path=' + encodeURIComponent(projectPath), {
1512
+ method: 'POST'
1513
+ });
1514
+ const result = await response.json();
1515
+ if (result.success) {
1516
+ showNotification(t('issues.issueArchived') || 'Issue archived successfully', 'success');
1517
+ closeIssueDetail();
1518
+ await loadIssueData();
1519
+ renderIssueView();
1520
+ updateIssueBadge();
1521
+ } else {
1522
+ showNotification(result.error || 'Failed to archive issue', 'error');
1523
+ }
1524
+ } catch (err) {
1525
+ console.error('Failed to archive issue:', err);
1526
+ showNotification('Failed to archive issue', 'error');
1527
+ }
1528
+ }
1529
+
1422
1530
  function toggleSolutionExpand(solId) {
1423
1531
  const el = document.getElementById('solution-' + solId);
1424
1532
  if (el) {
@@ -160,7 +160,7 @@ interface ClaudeWithSettingsParams {
160
160
  prompt: string;
161
161
  settingsPath: string;
162
162
  endpointId: string;
163
- mode: 'analysis' | 'write' | 'auto';
163
+ mode: 'analysis' | 'write' | 'auto' | 'review';
164
164
  workingDir: string;
165
165
  cd?: string;
166
166
  includeDirs?: string[];
@@ -351,12 +351,12 @@ type BuiltinCliTool = typeof BUILTIN_CLI_TOOLS[number];
351
351
  const ParamsSchema = z.object({
352
352
  tool: z.string().min(1, 'Tool is required'), // Accept any tool ID (built-in or custom endpoint)
353
353
  prompt: z.string().min(1, 'Prompt is required'),
354
- mode: z.enum(['analysis', 'write', 'auto']).default('analysis'),
354
+ mode: z.enum(['analysis', 'write', 'auto', 'review']).default('analysis'),
355
355
  format: z.enum(['plain', 'yaml', 'json']).default('plain'), // Multi-turn prompt concatenation format
356
356
  model: z.string().optional(),
357
357
  cd: z.string().optional(),
358
358
  includeDirs: z.string().optional(),
359
- timeout: z.number().default(0), // 0 = no internal timeout, controlled by external caller (e.g., bash timeout)
359
+ // timeout removed - controlled by external caller (bash timeout)
360
360
  resume: z.union([z.boolean(), z.string()]).optional(), // true = last, string = single ID or comma-separated IDs
361
361
  id: z.string().optional(), // Custom execution ID (e.g., IMPL-001-step1)
362
362
  noNative: z.boolean().optional(), // Force prompt concatenation instead of native resume
@@ -388,7 +388,7 @@ async function executeCliTool(
388
388
  throw new Error(`Invalid params: ${parsed.error.message}`);
389
389
  }
390
390
 
391
- const { tool, prompt, mode, format, model, cd, includeDirs, timeout, resume, id: customId, noNative, category, parentExecutionId, outputFormat } = parsed.data;
391
+ const { tool, prompt, mode, format, model, cd, includeDirs, resume, id: customId, noNative, category, parentExecutionId, outputFormat } = parsed.data;
392
392
 
393
393
  // Validate and determine working directory early (needed for conversation lookup)
394
394
  let workingDir: string;
@@ -862,7 +862,6 @@ async function executeCliTool(
862
862
 
863
863
  let stdout = '';
864
864
  let stderr = '';
865
- let timedOut = false;
866
865
 
867
866
  // Handle stdout
868
867
  child.stdout!.on('data', (data: Buffer) => {
@@ -924,18 +923,14 @@ async function executeCliTool(
924
923
  debugLog('CLOSE', `Process closed`, {
925
924
  exitCode: code,
926
925
  duration: `${duration}ms`,
927
- timedOut,
928
926
  stdoutLength: stdout.length,
929
927
  stderrLength: stderr.length,
930
928
  outputUnitsCount: allOutputUnits.length
931
929
  });
932
930
 
933
931
  // Determine status - prioritize output content over exit code
934
- let status: 'success' | 'error' | 'timeout' = 'success';
935
- if (timedOut) {
936
- status = 'timeout';
937
- debugLog('STATUS', `Execution timed out after ${duration}ms`);
938
- } else if (code !== 0) {
932
+ let status: 'success' | 'error' = 'success';
933
+ if (code !== 0) {
939
934
  // Non-zero exit code doesn't always mean failure
940
935
  // Check if there's valid output (AI response) - treat as success
941
936
  const hasValidOutput = stdout.trim().length > 0;
@@ -1169,25 +1164,8 @@ async function executeCliTool(
1169
1164
  reject(new Error(`Failed to spawn ${tool}: ${error.message}\n Command: ${command} ${args.join(' ')}\n Working Dir: ${workingDir}`));
1170
1165
  });
1171
1166
 
1172
- // Timeout handling (timeout=0 disables internal timeout, controlled by external caller)
1173
- let timeoutId: NodeJS.Timeout | null = null;
1174
- if (timeout > 0) {
1175
- timeoutId = setTimeout(() => {
1176
- timedOut = true;
1177
- child.kill('SIGTERM');
1178
- setTimeout(() => {
1179
- if (!child.killed) {
1180
- child.kill('SIGKILL');
1181
- }
1182
- }, 5000);
1183
- }, timeout);
1184
- }
1185
-
1186
- child.on('close', () => {
1187
- if (timeoutId) {
1188
- clearTimeout(timeoutId);
1189
- }
1190
- });
1167
+ // Timeout controlled by external caller (bash timeout)
1168
+ // When parent process terminates, child will be cleaned up via process exit handler
1191
1169
  });
1192
1170
  }
1193
1171
 
@@ -1198,7 +1176,8 @@ export const schema: ToolSchema = {
1198
1176
  Modes:
1199
1177
  - analysis: Read-only operations (default)
1200
1178
  - write: File modifications allowed
1201
- - auto: Full autonomous operations (codex only)`,
1179
+ - auto: Full autonomous operations (codex only)
1180
+ - review: Code review mode (codex uses 'codex review' subcommand, others accept but no operation change)`,
1202
1181
  inputSchema: {
1203
1182
  type: 'object',
1204
1183
  properties: {
@@ -1213,8 +1192,8 @@ Modes:
1213
1192
  },
1214
1193
  mode: {
1215
1194
  type: 'string',
1216
- enum: ['analysis', 'write', 'auto'],
1217
- description: 'Execution mode (default: analysis)',
1195
+ enum: ['analysis', 'write', 'auto', 'review'],
1196
+ description: 'Execution mode (default: analysis). review mode uses codex review subcommand for codex tool.',
1218
1197
  default: 'analysis'
1219
1198
  },
1220
1199
  model: {
@@ -1228,12 +1207,8 @@ Modes:
1228
1207
  includeDirs: {
1229
1208
  type: 'string',
1230
1209
  description: 'Additional directories (comma-separated). Maps to --include-directories for gemini/qwen, --add-dir for codex'
1231
- },
1232
- timeout: {
1233
- type: 'number',
1234
- description: 'Timeout in milliseconds (default: 0 = disabled, controlled by external caller)',
1235
- default: 0
1236
1210
  }
1211
+ // timeout removed - controlled by external caller (bash timeout)
1237
1212
  },
1238
1213
  required: ['tool', 'prompt']
1239
1214
  }
@@ -223,7 +223,21 @@ export function buildCommand(params: {
223
223
 
224
224
  case 'codex':
225
225
  useStdin = true;
226
- if (nativeResume?.enabled) {
226
+ if (mode === 'review') {
227
+ // codex review mode: non-interactive code review
228
+ // Format: codex review [OPTIONS] [PROMPT]
229
+ args.push('review');
230
+ // Default to --uncommitted if no specific review target in prompt
231
+ args.push('--uncommitted');
232
+ if (model) {
233
+ args.push('-m', model);
234
+ }
235
+ // codex review uses positional prompt argument, not stdin
236
+ useStdin = false;
237
+ if (prompt) {
238
+ args.push(prompt);
239
+ }
240
+ } else if (nativeResume?.enabled) {
227
241
  args.push('resume');
228
242
  if (nativeResume.isLatest) {
229
243
  args.push('--last');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-workflow",
3
- "version": "6.3.30",
3
+ "version": "6.3.32",
4
4
  "description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
5
5
  "type": "module",
6
6
  "main": "ccw/src/index.js",