claude-code-workflow 6.3.4 → 6.3.6

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 (111) hide show
  1. package/.claude/agents/issue-plan-agent.md +859 -0
  2. package/.claude/agents/issue-queue-agent.md +702 -0
  3. package/.claude/commands/issue/execute.md +453 -0
  4. package/.claude/commands/issue/manage.md +865 -0
  5. package/.claude/commands/issue/new.md +484 -0
  6. package/.claude/commands/issue/plan.md +421 -0
  7. package/.claude/commands/issue/queue.md +354 -0
  8. package/.claude/commands/{clean.md → workflow/clean.md} +5 -5
  9. package/.claude/commands/workflow/docs/analyze.md +1467 -0
  10. package/.claude/commands/workflow/docs/copyright.md +1265 -0
  11. package/.claude/commands/workflow/execute.md +0 -1
  12. package/.claude/commands/workflow/tools/conflict-resolution.md +76 -240
  13. package/.claude/commands/workflow/tools/context-gather.md +0 -2
  14. package/.claude/commands/workflow/tools/task-generate-agent.md +81 -8
  15. package/.claude/commands/workflow/tools/task-generate-tdd.md +0 -9
  16. package/.claude/commands/workflow/tools/test-context-gather.md +2 -3
  17. package/.claude/commands/workflow/tools/test-task-generate.md +0 -2
  18. package/.claude/skills/_shared/mermaid-utils.md +584 -0
  19. package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +0 -2
  20. package/.claude/skills/command-guide/reference/commands/workflow/execute.md +1 -1
  21. package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +1 -2
  22. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +1 -8
  23. package/.claude/skills/command-guide/reference/commands/workflow/tools/test-context-gather.md +1 -4
  24. package/.claude/skills/command-guide/reference/commands/workflow/tools/test-task-generate.md +0 -2
  25. package/.claude/skills/copyright-docs/SKILL.md +132 -0
  26. package/.claude/skills/copyright-docs/phases/01-metadata-collection.md +78 -0
  27. package/.claude/skills/copyright-docs/phases/01.5-project-exploration.md +150 -0
  28. package/.claude/skills/copyright-docs/phases/02-deep-analysis.md +664 -0
  29. package/.claude/skills/copyright-docs/phases/02.5-consolidation.md +192 -0
  30. package/.claude/skills/copyright-docs/phases/04-document-assembly.md +261 -0
  31. package/.claude/skills/copyright-docs/phases/05-compliance-refinement.md +192 -0
  32. package/.claude/skills/copyright-docs/specs/cpcc-requirements.md +121 -0
  33. package/.claude/skills/copyright-docs/templates/agent-base.md +200 -0
  34. package/.claude/skills/project-analyze/SKILL.md +162 -0
  35. package/.claude/skills/project-analyze/phases/01-requirements-discovery.md +79 -0
  36. package/.claude/skills/project-analyze/phases/02-project-exploration.md +176 -0
  37. package/.claude/skills/project-analyze/phases/03-deep-analysis.md +854 -0
  38. package/.claude/skills/project-analyze/phases/03.5-consolidation.md +233 -0
  39. package/.claude/skills/project-analyze/phases/04-report-generation.md +217 -0
  40. package/.claude/skills/project-analyze/phases/05-iterative-refinement.md +124 -0
  41. package/.claude/skills/project-analyze/specs/quality-standards.md +115 -0
  42. package/.claude/skills/project-analyze/specs/writing-style.md +152 -0
  43. package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +79 -65
  44. package/.claude/workflows/cli-templates/schemas/issue-task-jsonl-schema.json +136 -0
  45. package/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json +74 -0
  46. package/.claude/workflows/cli-templates/schemas/queue-schema.json +136 -0
  47. package/.claude/workflows/cli-templates/schemas/registry-schema.json +94 -0
  48. package/.claude/workflows/cli-templates/schemas/solution-schema.json +120 -0
  49. package/.claude/workflows/cli-templates/schemas/solutions-jsonl-schema.json +125 -0
  50. package/.codex/prompts/issue-execute.md +266 -0
  51. package/README.md +11 -1
  52. package/ccw/dist/cli.d.ts.map +1 -1
  53. package/ccw/dist/cli.js +25 -0
  54. package/ccw/dist/cli.js.map +1 -1
  55. package/ccw/dist/commands/cli.d.ts.map +1 -1
  56. package/ccw/dist/commands/cli.js +46 -8
  57. package/ccw/dist/commands/cli.js.map +1 -1
  58. package/ccw/dist/commands/issue.d.ts +21 -0
  59. package/ccw/dist/commands/issue.d.ts.map +1 -0
  60. package/ccw/dist/commands/issue.js +895 -0
  61. package/ccw/dist/commands/issue.js.map +1 -0
  62. package/ccw/dist/core/dashboard-generator-patch.js +1 -0
  63. package/ccw/dist/core/dashboard-generator-patch.js.map +1 -1
  64. package/ccw/dist/core/routes/cli-routes.js +2 -2
  65. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  66. package/ccw/dist/core/routes/issue-routes.d.ts +34 -0
  67. package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -0
  68. package/ccw/dist/core/routes/issue-routes.js +487 -0
  69. package/ccw/dist/core/routes/issue-routes.js.map +1 -0
  70. package/ccw/dist/core/server.d.ts.map +1 -1
  71. package/ccw/dist/core/server.js +17 -2
  72. package/ccw/dist/core/server.js.map +1 -1
  73. package/ccw/dist/tools/claude-cli-tools.d.ts +7 -3
  74. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  75. package/ccw/dist/tools/claude-cli-tools.js +31 -17
  76. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  77. package/ccw/dist/tools/smart-search.d.ts +25 -0
  78. package/ccw/dist/tools/smart-search.d.ts.map +1 -1
  79. package/ccw/dist/tools/smart-search.js +121 -17
  80. package/ccw/dist/tools/smart-search.js.map +1 -1
  81. package/ccw/src/cli.ts +26 -0
  82. package/ccw/src/commands/cli.ts +49 -7
  83. package/ccw/src/commands/issue.ts +1184 -0
  84. package/ccw/src/core/dashboard-generator-patch.ts +1 -0
  85. package/ccw/src/core/routes/cli-routes.ts +3 -3
  86. package/ccw/src/core/routes/issue-routes.ts +559 -0
  87. package/ccw/src/core/server.ts +17 -2
  88. package/ccw/src/templates/dashboard-css/32-issue-manager.css +2544 -0
  89. package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +467 -0
  90. package/ccw/src/templates/dashboard-js/components/cli-history.js +40 -13
  91. package/ccw/src/templates/dashboard-js/components/cli-status.js +26 -2
  92. package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +461 -0
  93. package/ccw/src/templates/dashboard-js/components/navigation.js +8 -0
  94. package/ccw/src/templates/dashboard-js/components/notifications.js +16 -0
  95. package/ccw/src/templates/dashboard-js/i18n.js +290 -2
  96. package/ccw/src/templates/dashboard-js/views/cli-manager.js +5 -0
  97. package/ccw/src/templates/dashboard-js/views/history.js +19 -4
  98. package/ccw/src/templates/dashboard-js/views/hook-manager.js +11 -5
  99. package/ccw/src/templates/dashboard-js/views/issue-manager.js +1546 -0
  100. package/ccw/src/templates/dashboard.html +55 -0
  101. package/ccw/src/tools/claude-cli-tools.ts +37 -20
  102. package/ccw/src/tools/smart-search.ts +157 -16
  103. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  104. package/codex-lens/src/codexlens/config.py +5 -0
  105. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  106. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  107. package/codex-lens/src/codexlens/search/hybrid_search.py +144 -11
  108. package/codex-lens/src/codexlens/search/ranking.py +267 -1
  109. package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
  110. package/codex-lens/src/codexlens/semantic/chunker.py +55 -10
  111. package/package.json +2 -2
@@ -0,0 +1,865 @@
1
+ ---
2
+ name: manage
3
+ description: Interactive issue management (CRUD) via ccw cli endpoints with menu-driven interface
4
+ argument-hint: "[issue-id] [--action list|view|edit|delete|bulk]"
5
+ allowed-tools: TodoWrite(*), Bash(*), Read(*), Write(*), AskUserQuestion(*), Task(*)
6
+ ---
7
+
8
+ # Issue Manage Command (/issue:manage)
9
+
10
+ ## Overview
11
+
12
+ Interactive menu-driven interface for issue management using `ccw issue` CLI endpoints:
13
+ - **List**: Browse and filter issues
14
+ - **View**: Detailed issue inspection
15
+ - **Edit**: Modify issue fields
16
+ - **Delete**: Remove issues
17
+ - **Bulk**: Batch operations on multiple issues
18
+
19
+ ## CLI Endpoints Reference
20
+
21
+ ```bash
22
+ # Core endpoints (ccw issue)
23
+ ccw issue list # List all issues
24
+ ccw issue list <id> --json # Get issue details
25
+ ccw issue status <id> # Detailed status
26
+ ccw issue init <id> --title "..." # Create issue
27
+ ccw issue task <id> --title "..." # Add task
28
+
29
+ # Queue management
30
+ ccw issue queue # List queue
31
+ ccw issue queue add <id> # Add to queue
32
+ ccw issue next # Get next task
33
+ ccw issue done <queue-id> # Complete task
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ```bash
39
+ # Interactive mode (menu-driven)
40
+ /issue:manage
41
+
42
+ # Direct to specific issue
43
+ /issue:manage GH-123
44
+
45
+ # Direct action
46
+ /issue:manage --action list
47
+ /issue:manage GH-123 --action edit
48
+ ```
49
+
50
+ ## Implementation
51
+
52
+ ### Phase 1: Entry Point
53
+
54
+ ```javascript
55
+ const issueId = parseIssueId(userInput);
56
+ const action = flags.action;
57
+
58
+ // Show main menu if no action specified
59
+ if (!action) {
60
+ await showMainMenu(issueId);
61
+ } else {
62
+ await executeAction(action, issueId);
63
+ }
64
+ ```
65
+
66
+ ### Phase 2: Main Menu
67
+
68
+ ```javascript
69
+ async function showMainMenu(preselectedIssue = null) {
70
+ // Fetch current issues summary
71
+ const issuesResult = Bash('ccw issue list --json 2>/dev/null || echo "[]"');
72
+ const issues = JSON.parse(issuesResult) || [];
73
+
74
+ const queueResult = Bash('ccw issue status --json 2>/dev/null');
75
+ const queueStatus = JSON.parse(queueResult || '{}');
76
+
77
+ console.log(`
78
+ ## Issue Management Dashboard
79
+
80
+ **Total Issues**: ${issues.length}
81
+ **Queue Status**: ${queueStatus.queue?.total_tasks || 0} tasks (${queueStatus.queue?.pending_count || 0} pending)
82
+
83
+ ### Quick Stats
84
+ - Registered: ${issues.filter(i => i.status === 'registered').length}
85
+ - Planned: ${issues.filter(i => i.status === 'planned').length}
86
+ - Executing: ${issues.filter(i => i.status === 'executing').length}
87
+ - Completed: ${issues.filter(i => i.status === 'completed').length}
88
+ `);
89
+
90
+ const answer = AskUserQuestion({
91
+ questions: [{
92
+ question: 'What would you like to do?',
93
+ header: 'Action',
94
+ multiSelect: false,
95
+ options: [
96
+ { label: 'List Issues', description: 'Browse all issues with filters' },
97
+ { label: 'View Issue', description: 'Detailed view of specific issue' },
98
+ { label: 'Create Issue', description: 'Add new issue from text or GitHub' },
99
+ { label: 'Edit Issue', description: 'Modify issue fields' },
100
+ { label: 'Delete Issue', description: 'Remove issue(s)' },
101
+ { label: 'Bulk Operations', description: 'Batch actions on multiple issues' }
102
+ ]
103
+ }]
104
+ });
105
+
106
+ const selected = parseAnswer(answer);
107
+
108
+ switch (selected) {
109
+ case 'List Issues':
110
+ await listIssuesInteractive();
111
+ break;
112
+ case 'View Issue':
113
+ await viewIssueInteractive(preselectedIssue);
114
+ break;
115
+ case 'Create Issue':
116
+ await createIssueInteractive();
117
+ break;
118
+ case 'Edit Issue':
119
+ await editIssueInteractive(preselectedIssue);
120
+ break;
121
+ case 'Delete Issue':
122
+ await deleteIssueInteractive(preselectedIssue);
123
+ break;
124
+ case 'Bulk Operations':
125
+ await bulkOperationsInteractive();
126
+ break;
127
+ }
128
+ }
129
+ ```
130
+
131
+ ### Phase 3: List Issues
132
+
133
+ ```javascript
134
+ async function listIssuesInteractive() {
135
+ // Ask for filter
136
+ const filterAnswer = AskUserQuestion({
137
+ questions: [{
138
+ question: 'Filter issues by status?',
139
+ header: 'Filter',
140
+ multiSelect: true,
141
+ options: [
142
+ { label: 'All', description: 'Show all issues' },
143
+ { label: 'Registered', description: 'New, unplanned issues' },
144
+ { label: 'Planned', description: 'Issues with bound solutions' },
145
+ { label: 'Queued', description: 'In execution queue' },
146
+ { label: 'Executing', description: 'Currently being worked on' },
147
+ { label: 'Completed', description: 'Finished issues' },
148
+ { label: 'Failed', description: 'Failed issues' }
149
+ ]
150
+ }]
151
+ });
152
+
153
+ const filters = parseMultiAnswer(filterAnswer);
154
+
155
+ // Fetch and filter issues
156
+ const result = Bash('ccw issue list --json');
157
+ let issues = JSON.parse(result) || [];
158
+
159
+ if (!filters.includes('All')) {
160
+ const statusMap = {
161
+ 'Registered': 'registered',
162
+ 'Planned': 'planned',
163
+ 'Queued': 'queued',
164
+ 'Executing': 'executing',
165
+ 'Completed': 'completed',
166
+ 'Failed': 'failed'
167
+ };
168
+ const allowedStatuses = filters.map(f => statusMap[f]).filter(Boolean);
169
+ issues = issues.filter(i => allowedStatuses.includes(i.status));
170
+ }
171
+
172
+ if (issues.length === 0) {
173
+ console.log('No issues found matching filters.');
174
+ return showMainMenu();
175
+ }
176
+
177
+ // Display issues table
178
+ console.log(`
179
+ ## Issues (${issues.length})
180
+
181
+ | ID | Status | Priority | Title |
182
+ |----|--------|----------|-------|
183
+ ${issues.map(i => `| ${i.id} | ${i.status} | P${i.priority} | ${i.title.substring(0, 40)} |`).join('\n')}
184
+ `);
185
+
186
+ // Ask for action on issue
187
+ const actionAnswer = AskUserQuestion({
188
+ questions: [{
189
+ question: 'Select an issue to view/edit, or return to menu:',
190
+ header: 'Select',
191
+ multiSelect: false,
192
+ options: [
193
+ ...issues.slice(0, 10).map(i => ({
194
+ label: i.id,
195
+ description: i.title.substring(0, 50)
196
+ })),
197
+ { label: 'Back to Menu', description: 'Return to main menu' }
198
+ ]
199
+ }]
200
+ });
201
+
202
+ const selected = parseAnswer(actionAnswer);
203
+
204
+ if (selected === 'Back to Menu') {
205
+ return showMainMenu();
206
+ }
207
+
208
+ // View selected issue
209
+ await viewIssueInteractive(selected);
210
+ }
211
+ ```
212
+
213
+ ### Phase 4: View Issue
214
+
215
+ ```javascript
216
+ async function viewIssueInteractive(issueId) {
217
+ if (!issueId) {
218
+ // Ask for issue ID
219
+ const issues = JSON.parse(Bash('ccw issue list --json') || '[]');
220
+
221
+ const idAnswer = AskUserQuestion({
222
+ questions: [{
223
+ question: 'Select issue to view:',
224
+ header: 'Issue',
225
+ multiSelect: false,
226
+ options: issues.slice(0, 10).map(i => ({
227
+ label: i.id,
228
+ description: `${i.status} - ${i.title.substring(0, 40)}`
229
+ }))
230
+ }]
231
+ });
232
+
233
+ issueId = parseAnswer(idAnswer);
234
+ }
235
+
236
+ // Fetch detailed status
237
+ const result = Bash(`ccw issue status ${issueId} --json`);
238
+ const data = JSON.parse(result);
239
+
240
+ const issue = data.issue;
241
+ const solutions = data.solutions || [];
242
+ const bound = data.bound;
243
+
244
+ console.log(`
245
+ ## Issue: ${issue.id}
246
+
247
+ **Title**: ${issue.title}
248
+ **Status**: ${issue.status}
249
+ **Priority**: P${issue.priority}
250
+ **Created**: ${issue.created_at}
251
+ **Updated**: ${issue.updated_at}
252
+
253
+ ### Context
254
+ ${issue.context || 'No context provided'}
255
+
256
+ ### Solutions (${solutions.length})
257
+ ${solutions.length === 0 ? 'No solutions registered' :
258
+ solutions.map(s => `- ${s.is_bound ? '◉' : '○'} ${s.id}: ${s.tasks?.length || 0} tasks`).join('\n')}
259
+
260
+ ${bound ? `### Bound Solution: ${bound.id}\n**Tasks**: ${bound.tasks?.length || 0}` : ''}
261
+ `);
262
+
263
+ // Show tasks if bound solution exists
264
+ if (bound?.tasks?.length > 0) {
265
+ console.log(`
266
+ ### Tasks
267
+ | ID | Action | Scope | Title |
268
+ |----|--------|-------|-------|
269
+ ${bound.tasks.map(t => `| ${t.id} | ${t.action} | ${t.scope?.substring(0, 20) || '-'} | ${t.title.substring(0, 30)} |`).join('\n')}
270
+ `);
271
+ }
272
+
273
+ // Action menu
274
+ const actionAnswer = AskUserQuestion({
275
+ questions: [{
276
+ question: 'What would you like to do?',
277
+ header: 'Action',
278
+ multiSelect: false,
279
+ options: [
280
+ { label: 'Edit Issue', description: 'Modify issue fields' },
281
+ { label: 'Plan Issue', description: 'Generate solution (/issue:plan)' },
282
+ { label: 'Add to Queue', description: 'Queue bound solution tasks' },
283
+ { label: 'View Queue', description: 'See queue status' },
284
+ { label: 'Delete Issue', description: 'Remove this issue' },
285
+ { label: 'Back to Menu', description: 'Return to main menu' }
286
+ ]
287
+ }]
288
+ });
289
+
290
+ const action = parseAnswer(actionAnswer);
291
+
292
+ switch (action) {
293
+ case 'Edit Issue':
294
+ await editIssueInteractive(issueId);
295
+ break;
296
+ case 'Plan Issue':
297
+ console.log(`Running: /issue:plan ${issueId}`);
298
+ // Invoke plan skill
299
+ break;
300
+ case 'Add to Queue':
301
+ Bash(`ccw issue queue add ${issueId}`);
302
+ console.log(`✓ Added ${issueId} tasks to queue`);
303
+ break;
304
+ case 'View Queue':
305
+ const queueOutput = Bash('ccw issue queue');
306
+ console.log(queueOutput);
307
+ break;
308
+ case 'Delete Issue':
309
+ await deleteIssueInteractive(issueId);
310
+ break;
311
+ default:
312
+ return showMainMenu();
313
+ }
314
+ }
315
+ ```
316
+
317
+ ### Phase 5: Edit Issue
318
+
319
+ ```javascript
320
+ async function editIssueInteractive(issueId) {
321
+ if (!issueId) {
322
+ const issues = JSON.parse(Bash('ccw issue list --json') || '[]');
323
+ const idAnswer = AskUserQuestion({
324
+ questions: [{
325
+ question: 'Select issue to edit:',
326
+ header: 'Issue',
327
+ multiSelect: false,
328
+ options: issues.slice(0, 10).map(i => ({
329
+ label: i.id,
330
+ description: `${i.status} - ${i.title.substring(0, 40)}`
331
+ }))
332
+ }]
333
+ });
334
+ issueId = parseAnswer(idAnswer);
335
+ }
336
+
337
+ // Get current issue data
338
+ const result = Bash(`ccw issue list ${issueId} --json`);
339
+ const issueData = JSON.parse(result);
340
+ const issue = issueData.issue || issueData;
341
+
342
+ // Ask which field to edit
343
+ const fieldAnswer = AskUserQuestion({
344
+ questions: [{
345
+ question: 'Which field to edit?',
346
+ header: 'Field',
347
+ multiSelect: false,
348
+ options: [
349
+ { label: 'Title', description: `Current: ${issue.title?.substring(0, 40)}` },
350
+ { label: 'Priority', description: `Current: P${issue.priority}` },
351
+ { label: 'Status', description: `Current: ${issue.status}` },
352
+ { label: 'Context', description: 'Edit problem description' },
353
+ { label: 'Labels', description: `Current: ${issue.labels?.join(', ') || 'none'}` },
354
+ { label: 'Back', description: 'Return without changes' }
355
+ ]
356
+ }]
357
+ });
358
+
359
+ const field = parseAnswer(fieldAnswer);
360
+
361
+ if (field === 'Back') {
362
+ return viewIssueInteractive(issueId);
363
+ }
364
+
365
+ let updatePayload = {};
366
+
367
+ switch (field) {
368
+ case 'Title':
369
+ const titleAnswer = AskUserQuestion({
370
+ questions: [{
371
+ question: 'Enter new title (or select current to keep):',
372
+ header: 'Title',
373
+ multiSelect: false,
374
+ options: [
375
+ { label: issue.title.substring(0, 50), description: 'Keep current title' }
376
+ ]
377
+ }]
378
+ });
379
+ const newTitle = parseAnswer(titleAnswer);
380
+ if (newTitle && newTitle !== issue.title.substring(0, 50)) {
381
+ updatePayload.title = newTitle;
382
+ }
383
+ break;
384
+
385
+ case 'Priority':
386
+ const priorityAnswer = AskUserQuestion({
387
+ questions: [{
388
+ question: 'Select priority:',
389
+ header: 'Priority',
390
+ multiSelect: false,
391
+ options: [
392
+ { label: 'P1 - Critical', description: 'Production blocking' },
393
+ { label: 'P2 - High', description: 'Major functionality' },
394
+ { label: 'P3 - Medium', description: 'Normal priority (default)' },
395
+ { label: 'P4 - Low', description: 'Minor issues' },
396
+ { label: 'P5 - Trivial', description: 'Nice to have' }
397
+ ]
398
+ }]
399
+ });
400
+ const priorityStr = parseAnswer(priorityAnswer);
401
+ updatePayload.priority = parseInt(priorityStr.charAt(1));
402
+ break;
403
+
404
+ case 'Status':
405
+ const statusAnswer = AskUserQuestion({
406
+ questions: [{
407
+ question: 'Select status:',
408
+ header: 'Status',
409
+ multiSelect: false,
410
+ options: [
411
+ { label: 'registered', description: 'New issue, not yet planned' },
412
+ { label: 'planning', description: 'Solution being generated' },
413
+ { label: 'planned', description: 'Solution bound, ready for queue' },
414
+ { label: 'queued', description: 'In execution queue' },
415
+ { label: 'executing', description: 'Currently being worked on' },
416
+ { label: 'completed', description: 'All tasks finished' },
417
+ { label: 'failed', description: 'Execution failed' },
418
+ { label: 'paused', description: 'Temporarily on hold' }
419
+ ]
420
+ }]
421
+ });
422
+ updatePayload.status = parseAnswer(statusAnswer);
423
+ break;
424
+
425
+ case 'Context':
426
+ console.log(`Current context:\n${issue.context || '(empty)'}\n`);
427
+ const contextAnswer = AskUserQuestion({
428
+ questions: [{
429
+ question: 'Enter new context (problem description):',
430
+ header: 'Context',
431
+ multiSelect: false,
432
+ options: [
433
+ { label: 'Keep current', description: 'No changes' }
434
+ ]
435
+ }]
436
+ });
437
+ const newContext = parseAnswer(contextAnswer);
438
+ if (newContext && newContext !== 'Keep current') {
439
+ updatePayload.context = newContext;
440
+ }
441
+ break;
442
+
443
+ case 'Labels':
444
+ const labelsAnswer = AskUserQuestion({
445
+ questions: [{
446
+ question: 'Enter labels (comma-separated):',
447
+ header: 'Labels',
448
+ multiSelect: false,
449
+ options: [
450
+ { label: issue.labels?.join(',') || '', description: 'Keep current labels' }
451
+ ]
452
+ }]
453
+ });
454
+ const labelsStr = parseAnswer(labelsAnswer);
455
+ if (labelsStr) {
456
+ updatePayload.labels = labelsStr.split(',').map(l => l.trim());
457
+ }
458
+ break;
459
+ }
460
+
461
+ // Apply update if any
462
+ if (Object.keys(updatePayload).length > 0) {
463
+ // Read, update, write issues.jsonl
464
+ const issuesPath = '.workflow/issues/issues.jsonl';
465
+ const allIssues = Bash(`cat "${issuesPath}"`)
466
+ .split('\n')
467
+ .filter(line => line.trim())
468
+ .map(line => JSON.parse(line));
469
+
470
+ const idx = allIssues.findIndex(i => i.id === issueId);
471
+ if (idx !== -1) {
472
+ allIssues[idx] = {
473
+ ...allIssues[idx],
474
+ ...updatePayload,
475
+ updated_at: new Date().toISOString()
476
+ };
477
+
478
+ Write(issuesPath, allIssues.map(i => JSON.stringify(i)).join('\n'));
479
+ console.log(`✓ Updated ${issueId}: ${Object.keys(updatePayload).join(', ')}`);
480
+ }
481
+ }
482
+
483
+ // Continue editing or return
484
+ const continueAnswer = AskUserQuestion({
485
+ questions: [{
486
+ question: 'Continue editing?',
487
+ header: 'Continue',
488
+ multiSelect: false,
489
+ options: [
490
+ { label: 'Edit Another Field', description: 'Continue editing this issue' },
491
+ { label: 'View Issue', description: 'See updated issue' },
492
+ { label: 'Back to Menu', description: 'Return to main menu' }
493
+ ]
494
+ }]
495
+ });
496
+
497
+ const cont = parseAnswer(continueAnswer);
498
+ if (cont === 'Edit Another Field') {
499
+ await editIssueInteractive(issueId);
500
+ } else if (cont === 'View Issue') {
501
+ await viewIssueInteractive(issueId);
502
+ } else {
503
+ return showMainMenu();
504
+ }
505
+ }
506
+ ```
507
+
508
+ ### Phase 6: Delete Issue
509
+
510
+ ```javascript
511
+ async function deleteIssueInteractive(issueId) {
512
+ if (!issueId) {
513
+ const issues = JSON.parse(Bash('ccw issue list --json') || '[]');
514
+ const idAnswer = AskUserQuestion({
515
+ questions: [{
516
+ question: 'Select issue to delete:',
517
+ header: 'Delete',
518
+ multiSelect: false,
519
+ options: issues.slice(0, 10).map(i => ({
520
+ label: i.id,
521
+ description: `${i.status} - ${i.title.substring(0, 40)}`
522
+ }))
523
+ }]
524
+ });
525
+ issueId = parseAnswer(idAnswer);
526
+ }
527
+
528
+ // Confirm deletion
529
+ const confirmAnswer = AskUserQuestion({
530
+ questions: [{
531
+ question: `Delete issue ${issueId}? This will also remove associated solutions.`,
532
+ header: 'Confirm',
533
+ multiSelect: false,
534
+ options: [
535
+ { label: 'Delete', description: 'Permanently remove issue and solutions' },
536
+ { label: 'Cancel', description: 'Keep issue' }
537
+ ]
538
+ }]
539
+ });
540
+
541
+ if (parseAnswer(confirmAnswer) !== 'Delete') {
542
+ console.log('Deletion cancelled.');
543
+ return showMainMenu();
544
+ }
545
+
546
+ // Remove from issues.jsonl
547
+ const issuesPath = '.workflow/issues/issues.jsonl';
548
+ const allIssues = Bash(`cat "${issuesPath}"`)
549
+ .split('\n')
550
+ .filter(line => line.trim())
551
+ .map(line => JSON.parse(line));
552
+
553
+ const filtered = allIssues.filter(i => i.id !== issueId);
554
+ Write(issuesPath, filtered.map(i => JSON.stringify(i)).join('\n'));
555
+
556
+ // Remove solutions file if exists
557
+ const solPath = `.workflow/issues/solutions/${issueId}.jsonl`;
558
+ Bash(`rm -f "${solPath}" 2>/dev/null || true`);
559
+
560
+ // Remove from queue if present
561
+ const queuePath = '.workflow/issues/queue.json';
562
+ if (Bash(`test -f "${queuePath}" && echo exists`) === 'exists') {
563
+ const queue = JSON.parse(Bash(`cat "${queuePath}"`));
564
+ queue.queue = queue.queue.filter(q => q.issue_id !== issueId);
565
+ Write(queuePath, JSON.stringify(queue, null, 2));
566
+ }
567
+
568
+ console.log(`✓ Deleted issue ${issueId}`);
569
+ return showMainMenu();
570
+ }
571
+ ```
572
+
573
+ ### Phase 7: Bulk Operations
574
+
575
+ ```javascript
576
+ async function bulkOperationsInteractive() {
577
+ const bulkAnswer = AskUserQuestion({
578
+ questions: [{
579
+ question: 'Select bulk operation:',
580
+ header: 'Bulk',
581
+ multiSelect: false,
582
+ options: [
583
+ { label: 'Update Status', description: 'Change status of multiple issues' },
584
+ { label: 'Update Priority', description: 'Change priority of multiple issues' },
585
+ { label: 'Add Labels', description: 'Add labels to multiple issues' },
586
+ { label: 'Delete Multiple', description: 'Remove multiple issues' },
587
+ { label: 'Queue All Planned', description: 'Add all planned issues to queue' },
588
+ { label: 'Retry All Failed', description: 'Reset all failed tasks to pending' },
589
+ { label: 'Back', description: 'Return to main menu' }
590
+ ]
591
+ }]
592
+ });
593
+
594
+ const operation = parseAnswer(bulkAnswer);
595
+
596
+ if (operation === 'Back') {
597
+ return showMainMenu();
598
+ }
599
+
600
+ // Get issues for selection
601
+ const allIssues = JSON.parse(Bash('ccw issue list --json') || '[]');
602
+
603
+ if (operation === 'Queue All Planned') {
604
+ const planned = allIssues.filter(i => i.status === 'planned' && i.bound_solution_id);
605
+ for (const issue of planned) {
606
+ Bash(`ccw issue queue add ${issue.id}`);
607
+ console.log(`✓ Queued ${issue.id}`);
608
+ }
609
+ console.log(`\n✓ Queued ${planned.length} issues`);
610
+ return showMainMenu();
611
+ }
612
+
613
+ if (operation === 'Retry All Failed') {
614
+ Bash('ccw issue retry');
615
+ console.log('✓ Reset all failed tasks to pending');
616
+ return showMainMenu();
617
+ }
618
+
619
+ // Multi-select issues
620
+ const selectAnswer = AskUserQuestion({
621
+ questions: [{
622
+ question: 'Select issues (multi-select):',
623
+ header: 'Select',
624
+ multiSelect: true,
625
+ options: allIssues.slice(0, 15).map(i => ({
626
+ label: i.id,
627
+ description: `${i.status} - ${i.title.substring(0, 30)}`
628
+ }))
629
+ }]
630
+ });
631
+
632
+ const selectedIds = parseMultiAnswer(selectAnswer);
633
+
634
+ if (selectedIds.length === 0) {
635
+ console.log('No issues selected.');
636
+ return showMainMenu();
637
+ }
638
+
639
+ // Execute bulk operation
640
+ const issuesPath = '.workflow/issues/issues.jsonl';
641
+ let issues = Bash(`cat "${issuesPath}"`)
642
+ .split('\n')
643
+ .filter(line => line.trim())
644
+ .map(line => JSON.parse(line));
645
+
646
+ switch (operation) {
647
+ case 'Update Status':
648
+ const statusAnswer = AskUserQuestion({
649
+ questions: [{
650
+ question: 'Select new status:',
651
+ header: 'Status',
652
+ multiSelect: false,
653
+ options: [
654
+ { label: 'registered', description: 'Reset to registered' },
655
+ { label: 'paused', description: 'Pause issues' },
656
+ { label: 'completed', description: 'Mark completed' }
657
+ ]
658
+ }]
659
+ });
660
+ const newStatus = parseAnswer(statusAnswer);
661
+ issues = issues.map(i =>
662
+ selectedIds.includes(i.id)
663
+ ? { ...i, status: newStatus, updated_at: new Date().toISOString() }
664
+ : i
665
+ );
666
+ break;
667
+
668
+ case 'Update Priority':
669
+ const prioAnswer = AskUserQuestion({
670
+ questions: [{
671
+ question: 'Select new priority:',
672
+ header: 'Priority',
673
+ multiSelect: false,
674
+ options: [
675
+ { label: 'P1', description: 'Critical' },
676
+ { label: 'P2', description: 'High' },
677
+ { label: 'P3', description: 'Medium' },
678
+ { label: 'P4', description: 'Low' },
679
+ { label: 'P5', description: 'Trivial' }
680
+ ]
681
+ }]
682
+ });
683
+ const newPrio = parseInt(parseAnswer(prioAnswer).charAt(1));
684
+ issues = issues.map(i =>
685
+ selectedIds.includes(i.id)
686
+ ? { ...i, priority: newPrio, updated_at: new Date().toISOString() }
687
+ : i
688
+ );
689
+ break;
690
+
691
+ case 'Add Labels':
692
+ const labelAnswer = AskUserQuestion({
693
+ questions: [{
694
+ question: 'Enter labels to add (comma-separated):',
695
+ header: 'Labels',
696
+ multiSelect: false,
697
+ options: [
698
+ { label: 'bug', description: 'Bug fix' },
699
+ { label: 'feature', description: 'New feature' },
700
+ { label: 'urgent', description: 'Urgent priority' }
701
+ ]
702
+ }]
703
+ });
704
+ const newLabels = parseAnswer(labelAnswer).split(',').map(l => l.trim());
705
+ issues = issues.map(i =>
706
+ selectedIds.includes(i.id)
707
+ ? {
708
+ ...i,
709
+ labels: [...new Set([...(i.labels || []), ...newLabels])],
710
+ updated_at: new Date().toISOString()
711
+ }
712
+ : i
713
+ );
714
+ break;
715
+
716
+ case 'Delete Multiple':
717
+ const confirmDelete = AskUserQuestion({
718
+ questions: [{
719
+ question: `Delete ${selectedIds.length} issues permanently?`,
720
+ header: 'Confirm',
721
+ multiSelect: false,
722
+ options: [
723
+ { label: 'Delete All', description: 'Remove selected issues' },
724
+ { label: 'Cancel', description: 'Keep issues' }
725
+ ]
726
+ }]
727
+ });
728
+ if (parseAnswer(confirmDelete) === 'Delete All') {
729
+ issues = issues.filter(i => !selectedIds.includes(i.id));
730
+ // Clean up solutions
731
+ for (const id of selectedIds) {
732
+ Bash(`rm -f ".workflow/issues/solutions/${id}.jsonl" 2>/dev/null || true`);
733
+ }
734
+ } else {
735
+ console.log('Deletion cancelled.');
736
+ return showMainMenu();
737
+ }
738
+ break;
739
+ }
740
+
741
+ Write(issuesPath, issues.map(i => JSON.stringify(i)).join('\n'));
742
+ console.log(`✓ Updated ${selectedIds.length} issues`);
743
+ return showMainMenu();
744
+ }
745
+ ```
746
+
747
+ ### Phase 8: Create Issue (Redirect)
748
+
749
+ ```javascript
750
+ async function createIssueInteractive() {
751
+ const typeAnswer = AskUserQuestion({
752
+ questions: [{
753
+ question: 'Create issue from:',
754
+ header: 'Source',
755
+ multiSelect: false,
756
+ options: [
757
+ { label: 'GitHub URL', description: 'Import from GitHub issue' },
758
+ { label: 'Text Description', description: 'Enter problem description' },
759
+ { label: 'Quick Create', description: 'Just title and priority' }
760
+ ]
761
+ }]
762
+ });
763
+
764
+ const type = parseAnswer(typeAnswer);
765
+
766
+ if (type === 'GitHub URL' || type === 'Text Description') {
767
+ console.log('Use /issue:new for structured issue creation');
768
+ console.log('Example: /issue:new https://github.com/org/repo/issues/123');
769
+ return showMainMenu();
770
+ }
771
+
772
+ // Quick create
773
+ const titleAnswer = AskUserQuestion({
774
+ questions: [{
775
+ question: 'Enter issue title:',
776
+ header: 'Title',
777
+ multiSelect: false,
778
+ options: [
779
+ { label: 'Authentication Bug', description: 'Example title' }
780
+ ]
781
+ }]
782
+ });
783
+
784
+ const title = parseAnswer(titleAnswer);
785
+
786
+ const prioAnswer = AskUserQuestion({
787
+ questions: [{
788
+ question: 'Select priority:',
789
+ header: 'Priority',
790
+ multiSelect: false,
791
+ options: [
792
+ { label: 'P3 - Medium (Recommended)', description: 'Normal priority' },
793
+ { label: 'P1 - Critical', description: 'Production blocking' },
794
+ { label: 'P2 - High', description: 'Major functionality' }
795
+ ]
796
+ }]
797
+ });
798
+
799
+ const priority = parseInt(parseAnswer(prioAnswer).charAt(1));
800
+
801
+ // Generate ID and create
802
+ const id = `ISS-${Date.now()}`;
803
+ Bash(`ccw issue init ${id} --title "${title}" --priority ${priority}`);
804
+
805
+ console.log(`✓ Created issue ${id}`);
806
+ await viewIssueInteractive(id);
807
+ }
808
+ ```
809
+
810
+ ## Helper Functions
811
+
812
+ ```javascript
813
+ function parseAnswer(answer) {
814
+ // Extract selected option from AskUserQuestion response
815
+ if (typeof answer === 'string') return answer;
816
+ if (answer.answers) {
817
+ const values = Object.values(answer.answers);
818
+ return values[0] || '';
819
+ }
820
+ return '';
821
+ }
822
+
823
+ function parseMultiAnswer(answer) {
824
+ // Extract multiple selections
825
+ if (typeof answer === 'string') return answer.split(',').map(s => s.trim());
826
+ if (answer.answers) {
827
+ const values = Object.values(answer.answers);
828
+ return values.flatMap(v => v.split(',').map(s => s.trim()));
829
+ }
830
+ return [];
831
+ }
832
+
833
+ function parseFlags(input) {
834
+ const flags = {};
835
+ const matches = input.matchAll(/--(\w+)\s+([^\s-]+)/g);
836
+ for (const match of matches) {
837
+ flags[match[1]] = match[2];
838
+ }
839
+ return flags;
840
+ }
841
+
842
+ function parseIssueId(input) {
843
+ const match = input.match(/^([A-Z]+-\d+|ISS-\d+|GH-\d+)/i);
844
+ return match ? match[1] : null;
845
+ }
846
+ ```
847
+
848
+ ## Error Handling
849
+
850
+ | Error | Resolution |
851
+ |-------|------------|
852
+ | No issues found | Suggest creating with /issue:new |
853
+ | Issue not found | Show available issues, ask for correction |
854
+ | Invalid selection | Show error, re-prompt |
855
+ | Write failure | Check permissions, show error |
856
+ | Queue operation fails | Show ccw issue error, suggest fix |
857
+
858
+ ## Related Commands
859
+
860
+ - `/issue:new` - Create structured issue
861
+ - `/issue:plan` - Plan solution for issue
862
+ - `/issue:queue` - Form execution queue
863
+ - `/issue:execute` - Execute queued tasks
864
+ - `ccw issue list` - CLI list command
865
+ - `ccw issue status` - CLI status command