claude-code-workflow 6.3.11 → 6.3.13

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 (33) hide show
  1. package/.claude/CLAUDE.md +33 -33
  2. package/.claude/agents/issue-plan-agent.md +77 -5
  3. package/.claude/agents/issue-queue-agent.md +122 -18
  4. package/.claude/commands/issue/execute.md +53 -40
  5. package/.claude/commands/issue/new.md +113 -11
  6. package/.claude/commands/issue/plan.md +112 -37
  7. package/.claude/commands/issue/queue.md +28 -18
  8. package/.claude/skills/software-manual/scripts/assemble_docsify.py +584 -0
  9. package/.claude/skills/software-manual/templates/css/docsify-base.css +984 -0
  10. package/.claude/skills/software-manual/templates/docsify-shell.html +466 -0
  11. package/.claude/workflows/cli-templates/schemas/issues-jsonl-schema.json +141 -168
  12. package/.claude/workflows/cli-templates/schemas/solution-schema.json +3 -2
  13. package/.codex/prompts/issue-execute.md +3 -3
  14. package/.codex/prompts/issue-queue.md +3 -3
  15. package/ccw/dist/commands/issue.d.ts.map +1 -1
  16. package/ccw/dist/commands/issue.js +2 -1
  17. package/ccw/dist/commands/issue.js.map +1 -1
  18. package/ccw/src/commands/issue.ts +2 -1
  19. package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +580 -467
  20. package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +532 -461
  21. package/ccw/src/templates/dashboard-js/components/notifications.js +774 -774
  22. package/ccw/src/templates/dashboard-js/i18n.js +4 -0
  23. package/ccw/src/templates/dashboard.html +10 -0
  24. package/ccw/src/tools/claude-cli-tools.ts +388 -388
  25. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  26. package/codex-lens/src/codexlens/config.py +19 -3
  27. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  28. package/codex-lens/src/codexlens/search/ranking.py +15 -4
  29. package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
  30. package/codex-lens/src/codexlens/semantic/vector_store.py +57 -47
  31. package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-313.pyc +0 -0
  32. package/codex-lens/src/codexlens/storage/registry.py +114 -101
  33. package/package.json +83 -83
@@ -128,40 +128,46 @@ if (flags.allPending) {
128
128
  }
129
129
  }
130
130
 
131
- // Intelligent grouping by similarity (tags title keywords)
132
- function groupBySimilarity(issues, maxSize) {
133
- const batches = [];
134
- const used = new Set();
135
-
136
- for (const issue of issues) {
137
- if (used.has(issue.id)) continue;
138
-
139
- const batch = [issue];
140
- used.add(issue.id);
141
- const issueTags = new Set(issue.tags);
142
- const issueWords = new Set(issue.title.toLowerCase().split(/\s+/));
143
-
144
- // Find similar issues
145
- for (const other of issues) {
146
- if (used.has(other.id) || batch.length >= maxSize) continue;
147
-
148
- // Similarity: shared tags or shared title keywords
149
- const sharedTags = other.tags.filter(t => issueTags.has(t)).length;
150
- const otherWords = other.title.toLowerCase().split(/\s+/);
151
- const sharedWords = otherWords.filter(w => issueWords.has(w) && w.length > 3).length;
152
-
153
- if (sharedTags > 0 || sharedWords >= 2) {
154
- batch.push(other);
155
- used.add(other.id);
156
- }
157
- }
158
- batches.push(batch);
131
+ // Semantic grouping via Gemini CLI (max 6 issues per group)
132
+ async function groupBySimilarityGemini(issues) {
133
+ const issueSummaries = issues.map(i => ({
134
+ id: i.id, title: i.title, tags: i.tags
135
+ }));
136
+
137
+ const prompt = `
138
+ PURPOSE: Group similar issues by semantic similarity for batch processing; maximize within-group coherence; max 6 issues per group
139
+ TASK: Analyze issue titles/tags semantically • Identify functional/architectural clusters • Assign each issue to one group
140
+ MODE: analysis
141
+ CONTEXT: Issue metadata only
142
+ EXPECTED: JSON with groups array, each containing max 6 issue_ids, theme, rationale
143
+ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Each issue in exactly one group | Max 6 issues per group | Balance group sizes
144
+
145
+ INPUT:
146
+ ${JSON.stringify(issueSummaries, null, 2)}
147
+
148
+ OUTPUT FORMAT:
149
+ {"groups":[{"group_id":1,"theme":"...","issue_ids":["..."],"rationale":"..."}],"ungrouped":[]}
150
+ `;
151
+
152
+ const taskId = Bash({
153
+ command: `ccw cli -p "${prompt}" --tool gemini --mode analysis`,
154
+ run_in_background: true, timeout: 600000
155
+ });
156
+ const output = TaskOutput({ task_id: taskId, block: true });
157
+
158
+ // Extract JSON from potential markdown code blocks
159
+ function extractJsonFromMarkdown(text) {
160
+ const jsonMatch = text.match(/```json\s*\n([\s\S]*?)\n```/) ||
161
+ text.match(/```\s*\n([\s\S]*?)\n```/);
162
+ return jsonMatch ? jsonMatch[1] : text;
159
163
  }
160
- return batches;
164
+
165
+ const result = JSON.parse(extractJsonFromMarkdown(output));
166
+ return result.groups.map(g => g.issue_ids.map(id => issues.find(i => i.id === id)));
161
167
  }
162
168
 
163
- const batches = groupBySimilarity(issues, batchSize);
164
- console.log(`Processing ${issues.length} issues in ${batches.length} batch(es) (grouped by similarity)`);
169
+ const batches = await groupBySimilarityGemini(issues);
170
+ console.log(`Processing ${issues.length} issues in ${batches.length} batch(es) (Gemini semantic grouping, max 6 issues/agent)`);
165
171
 
166
172
  TodoWrite({
167
173
  todos: batches.map((_, i) => ({
@@ -177,6 +183,7 @@ TodoWrite({
177
183
  ```javascript
178
184
  Bash(`mkdir -p .workflow/issues/solutions`);
179
185
  const pendingSelections = []; // Collect multi-solution issues for user selection
186
+ const agentResults = []; // Collect all agent results for conflict aggregation
180
187
 
181
188
  // Build prompts for all batches
182
189
  const agentTasks = batches.map((batch, batchIndex) => {
@@ -200,13 +207,15 @@ ${issueList}
200
207
  ### Steps
201
208
  1. Fetch: \`ccw issue status <id> --json\`
202
209
  2. Load project context (project-tech.json + project-guidelines.json)
203
- 3. **If source=discovery**: Use discovery_context (file, line, snippet, suggested_fix) as planning hints
210
+ 3. **If extended_context exists**: Use extended_context (location, suggested_fix, notes) as planning hints
204
211
  4. Explore (ACE) → Plan solution (respecting guidelines)
205
212
  5. Register & bind: \`ccw issue bind <id> --solution <file>\`
206
213
 
207
214
  ### Generate Files
208
215
  \`.workflow/issues/solutions/{issue-id}.jsonl\` - Solution with tasks (schema: cat .claude/workflows/cli-templates/schemas/solution-schema.json)
209
216
 
217
+ **Solution ID Format**: \`SOL-{issue-id}-{seq}\` (e.g., \`SOL-GH-123-1\`, \`SOL-ISS-20251229-1\`)
218
+
210
219
  ### Binding Rules
211
220
  - **Single solution**: Auto-bind via \`ccw issue bind <id> --solution <file>\`
212
221
  - **Multiple solutions**: Register only, return for user selection
@@ -216,7 +225,13 @@ ${issueList}
216
225
  {
217
226
  "bound": [{ "issue_id": "...", "solution_id": "...", "task_count": N }],
218
227
  "pending_selection": [{ "issue_id": "...", "solutions": [{ "id": "...", "description": "...", "task_count": N }] }],
219
- "conflicts": [{ "file": "...", "issues": [...] }]
228
+ "conflicts": [{
229
+ "type": "file_conflict|api_conflict|data_conflict|dependency_conflict|architecture_conflict",
230
+ "severity": "high|medium|low",
231
+ "summary": "brief description",
232
+ "recommended_resolution": "auto-resolution for low/medium",
233
+ "resolution_options": [{ "strategy": "...", "rationale": "..." }]
234
+ }]
220
235
  }
221
236
  \`\`\`
222
237
  `;
@@ -247,7 +262,18 @@ for (let i = 0; i < agentTasks.length; i += MAX_PARALLEL) {
247
262
  // Collect results from this chunk
248
263
  for (const { taskId, batchIndex } of taskIds) {
249
264
  const result = TaskOutput(task_id=taskId, block=true);
250
- const summary = JSON.parse(result);
265
+
266
+ // Extract JSON from potential markdown code blocks (agent may wrap in ```json...```)
267
+ const jsonText = extractJsonFromMarkdown(result);
268
+ let summary;
269
+ try {
270
+ summary = JSON.parse(jsonText);
271
+ } catch (e) {
272
+ console.log(`⚠ Batch ${batchIndex + 1}: Failed to parse agent result, skipping`);
273
+ updateTodo(`Plan batch ${batchIndex + 1}`, 'completed');
274
+ continue;
275
+ }
276
+ agentResults.push(summary); // Store for Phase 3 conflict aggregation
251
277
 
252
278
  for (const item of summary.bound || []) {
253
279
  console.log(`✓ ${item.issue_id}: ${item.solution_id} (${item.task_count} tasks)`);
@@ -258,17 +284,66 @@ for (let i = 0; i < agentTasks.length; i += MAX_PARALLEL) {
258
284
  pendingSelections.push(pending);
259
285
  }
260
286
  if (summary.conflicts?.length > 0) {
261
- console.log(`⚠ Conflicts: ${summary.conflicts.map(c => c.file).join(', ')}`);
287
+ console.log(`⚠ Conflicts: ${summary.conflicts.length} detected (will resolve in Phase 3)`);
262
288
  }
263
289
  updateTodo(`Plan batch ${batchIndex + 1}`, 'completed');
264
290
  }
265
291
  }
266
292
  ```
267
293
 
268
- ### Phase 3: Multi-Solution Selection (MANDATORY when pendingSelections > 0)
294
+ ### Phase 3: Conflict Resolution & Solution Selection
269
295
 
270
296
  ```javascript
271
- // MUST trigger user selection when multiple solutions exist
297
+ // Helper: Extract selected solution ID from AskUserQuestion answer
298
+ function extractSelectedSolutionId(answer, issueId) {
299
+ // answer format: { [header]: selectedLabel } or { answers: { [question]: label } }
300
+ const key = Object.keys(answer).find(k => k.includes(issueId));
301
+ if (!key) return null;
302
+ const selected = answer[key];
303
+ // Label format: "SOL-xxx (N tasks)" - extract solution ID
304
+ const match = selected.match(/^(SOL-[^\s]+)/);
305
+ return match ? match[1] : null;
306
+ }
307
+
308
+ // Phase 3a: Aggregate and resolve conflicts from all agents
309
+ const allConflicts = [];
310
+ for (const result of agentResults) {
311
+ if (result.conflicts?.length > 0) {
312
+ allConflicts.push(...result.conflicts);
313
+ }
314
+ }
315
+
316
+ if (allConflicts.length > 0) {
317
+ console.log(`\n## Resolving ${allConflicts.length} conflict(s) detected by agents\n`);
318
+
319
+ // ALWAYS confirm high-severity conflicts (per user preference)
320
+ const highSeverity = allConflicts.filter(c => c.severity === 'high');
321
+ const lowMedium = allConflicts.filter(c => c.severity !== 'high');
322
+
323
+ // Auto-resolve low/medium severity
324
+ for (const conflict of lowMedium) {
325
+ console.log(` Auto-resolved: ${conflict.summary} → ${conflict.recommended_resolution}`);
326
+ }
327
+
328
+ // ALWAYS require user confirmation for high severity
329
+ if (highSeverity.length > 0) {
330
+ const conflictAnswer = AskUserQuestion({
331
+ questions: highSeverity.slice(0, 4).map(conflict => ({
332
+ question: `${conflict.type}: ${conflict.summary}. How to resolve?`,
333
+ header: conflict.type.replace('_conflict', ''),
334
+ multiSelect: false,
335
+ options: conflict.resolution_options.map(opt => ({
336
+ label: opt.strategy,
337
+ description: opt.rationale
338
+ }))
339
+ }))
340
+ });
341
+ // Apply user-selected resolutions
342
+ console.log('Applied user-selected conflict resolutions');
343
+ }
344
+ }
345
+
346
+ // Phase 3b: Multi-Solution Selection (MANDATORY when pendingSelections > 0)
272
347
  if (pendingSelections.length > 0) {
273
348
  console.log(`\n## User Selection Required: ${pendingSelections.length} issue(s) have multiple solutions\n`);
274
349
 
@@ -89,7 +89,7 @@ Queue formation command using **issue-queue-agent** that analyzes all bound solu
89
89
  {
90
90
  "item_id": "S-1",
91
91
  "issue_id": "ISS-20251227-003",
92
- "solution_id": "SOL-20251227-003",
92
+ "solution_id": "SOL-ISS-20251227-003-1",
93
93
  "status": "pending",
94
94
  "execution_order": 1,
95
95
  "execution_group": "P1",
@@ -102,7 +102,7 @@ Queue formation command using **issue-queue-agent** that analyzes all bound solu
102
102
  {
103
103
  "item_id": "S-2",
104
104
  "issue_id": "ISS-20251227-001",
105
- "solution_id": "SOL-20251227-001",
105
+ "solution_id": "SOL-ISS-20251227-001-1",
106
106
  "status": "pending",
107
107
  "execution_order": 2,
108
108
  "execution_group": "P1",
@@ -115,7 +115,7 @@ Queue formation command using **issue-queue-agent** that analyzes all bound solu
115
115
  {
116
116
  "item_id": "S-3",
117
117
  "issue_id": "ISS-20251227-002",
118
- "solution_id": "SOL-20251227-002",
118
+ "solution_id": "SOL-ISS-20251227-002-1",
119
119
  "status": "pending",
120
120
  "execution_order": 3,
121
121
  "execution_group": "S2",
@@ -329,27 +329,33 @@ const result = Task(
329
329
  const summary = JSON.parse(result);
330
330
  ```
331
331
 
332
- ### Phase 5: Summary & Status Update
332
+ ### Phase 5: Validation & Status Update
333
333
 
334
334
  ```javascript
335
- // Agent already generated queue files, use summary
335
+ const queuePath = `.workflow/issues/queues/${queueId}.json`;
336
+
337
+ // 1. Validate queue has solutions
338
+ const solCount = Bash(`jq ".solutions | length" "${queuePath}"`).trim();
339
+ if (!solCount || solCount === '0') {
340
+ console.error(`✗ Queue has no solutions. Aborting.`);
341
+ return;
342
+ }
343
+
344
+ // 2. Update issue statuses
345
+ for (const id of summary.issues_queued) {
346
+ Bash(`ccw issue update ${id} --status queued`);
347
+ }
348
+
349
+ // 3. Summary
336
350
  console.log(`
337
- ## Queue Formed: ${summary.queue_id}
351
+ ## Queue: ${summary.queue_id}
338
352
 
339
- **Solutions**: ${summary.total_solutions}
340
- **Tasks**: ${summary.total_tasks}
341
- **Issues**: ${summary.issues_queued.join(', ')}
342
- **Groups**: ${summary.execution_groups.map(g => `${g.id}(${g.count})`).join(', ')}
343
- **Conflicts Resolved**: ${summary.conflicts_resolved}
353
+ - Solutions: ${summary.total_solutions}
354
+ - Tasks: ${summary.total_tasks}
355
+ - Issues: ${summary.issues_queued.join(', ')}
344
356
 
345
- Next: \`/issue:execute\`
357
+ Next: /issue:execute
346
358
  `);
347
-
348
- // Update issue statuses via CLI (use `update` for pure field changes)
349
- // Note: `queue add` has its own logic; here we only need status update
350
- for (const issueId of summary.issues_queued) {
351
- Bash(`ccw issue update ${issueId} --status queued`);
352
- }
353
359
  ```
354
360
 
355
361
  ## Error Handling
@@ -360,6 +366,10 @@ for (const issueId of summary.issues_queued) {
360
366
  | Circular dependency | List cycles, abort queue formation |
361
367
  | Unresolved conflicts | Agent resolves using ordering rules |
362
368
  | Invalid task reference | Skip and warn |
369
+ | **index.json not updated** | Auto-fix: Set active_queue_id to new queue |
370
+ | **Wrong status value** | Auto-fix: Convert non-pending status to "pending" |
371
+ | **No entry points (all have deps)** | Auto-fix: Clear depends_on for first item |
372
+ | **Queue file missing solutions** | Abort with error, agent must regenerate |
363
373
 
364
374
  ## Related Commands
365
375