claude-code-workflow 7.2.29 → 7.2.30

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 (124) hide show
  1. package/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json +2 -2
  2. package/.ccw/workflows/cli-templates/schemas/task-schema.json +14 -7
  3. package/.claude/agents/action-planning-agent.md +7 -4
  4. package/.claude/agents/cli-explore-agent.md +77 -63
  5. package/.claude/agents/cli-lite-planning-agent.md +11 -10
  6. package/.claude/agents/issue-plan-agent.md +421 -426
  7. package/.claude/commands/workflow/spec/setup.md +1 -1
  8. package/.claude/skills/ccw-chain/SKILL.md +119 -0
  9. package/.claude/skills/ccw-chain/chains/ccw-cycle.json +21 -0
  10. package/.claude/skills/ccw-chain/chains/ccw-exploration.json +47 -0
  11. package/.claude/skills/ccw-chain/chains/ccw-issue.json +33 -0
  12. package/.claude/skills/ccw-chain/chains/ccw-lightweight.json +57 -0
  13. package/.claude/skills/ccw-chain/chains/ccw-main.json +52 -0
  14. package/.claude/skills/ccw-chain/chains/ccw-standard.json +39 -0
  15. package/.claude/skills/ccw-chain/chains/ccw-team.json +10 -0
  16. package/.claude/skills/ccw-chain/chains/ccw-with-file.json +31 -0
  17. package/.claude/skills/ccw-chain/phases/analyze-with-file.md +788 -0
  18. package/.claude/skills/ccw-chain/phases/brainstorm/SKILL.md +408 -0
  19. package/.claude/skills/ccw-chain/phases/brainstorm/phases/01-mode-routing.md +207 -0
  20. package/.claude/skills/ccw-chain/phases/brainstorm/phases/02-artifacts.md +567 -0
  21. package/.claude/skills/ccw-chain/phases/brainstorm/phases/03-role-analysis.md +748 -0
  22. package/.claude/skills/ccw-chain/phases/brainstorm/phases/04-synthesis.md +827 -0
  23. package/.claude/skills/ccw-chain/phases/brainstorm-with-file.md +482 -0
  24. package/.claude/skills/ccw-chain/phases/collaborative-plan-with-file.md +639 -0
  25. package/.claude/skills/ccw-chain/phases/debug-with-file.md +656 -0
  26. package/.claude/skills/ccw-chain/phases/integration-test-cycle.md +936 -0
  27. package/.claude/skills/ccw-chain/phases/issue-convert-to-plan.md +720 -0
  28. package/.claude/skills/ccw-chain/phases/issue-discover.md +483 -0
  29. package/.claude/skills/ccw-chain/phases/issue-execute.md +629 -0
  30. package/.claude/skills/ccw-chain/phases/issue-from-brainstorm.md +382 -0
  31. package/.claude/skills/ccw-chain/phases/issue-plan.md +343 -0
  32. package/.claude/skills/ccw-chain/phases/issue-queue.md +464 -0
  33. package/.claude/skills/ccw-chain/phases/refactor-cycle.md +852 -0
  34. package/.claude/skills/ccw-chain/phases/review-cycle/SKILL.md +132 -0
  35. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-fix.md +760 -0
  36. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-module.md +764 -0
  37. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-session.md +775 -0
  38. package/.claude/skills/ccw-chain/phases/roadmap-with-file.md +544 -0
  39. package/.claude/skills/ccw-chain/phases/spec-generator/SKILL.md +338 -0
  40. package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-5-requirement-clarification.md +404 -0
  41. package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-discovery.md +257 -0
  42. package/.claude/skills/ccw-chain/phases/spec-generator/phases/02-product-brief.md +274 -0
  43. package/.claude/skills/ccw-chain/phases/spec-generator/phases/03-requirements.md +184 -0
  44. package/.claude/skills/ccw-chain/phases/spec-generator/phases/04-architecture.md +248 -0
  45. package/.claude/skills/ccw-chain/phases/spec-generator/phases/05-epics-stories.md +178 -0
  46. package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-5-auto-fix.md +144 -0
  47. package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-readiness-check.md +480 -0
  48. package/.claude/skills/ccw-chain/phases/team-planex.md +123 -0
  49. package/.claude/skills/ccw-chain/phases/ui-design-explore-auto.md +678 -0
  50. package/.claude/skills/ccw-chain/phases/unified-execute-with-file.md +870 -0
  51. package/.claude/skills/ccw-chain/phases/workflow-execute/SKILL.md +625 -0
  52. package/.claude/skills/ccw-chain/phases/workflow-execute/phases/06-review.md +215 -0
  53. package/.claude/skills/ccw-chain/phases/workflow-lite-plan.md +616 -0
  54. package/.claude/skills/ccw-chain/phases/workflow-multi-cli-plan.md +424 -0
  55. package/.claude/skills/ccw-chain/phases/workflow-plan/SKILL.md +466 -0
  56. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/01-session-discovery.md +99 -0
  57. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/02-context-gathering.md +338 -0
  58. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/03-conflict-resolution.md +422 -0
  59. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/04-task-generation.md +440 -0
  60. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/05-plan-verify.md +395 -0
  61. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/06-replan.md +594 -0
  62. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/SKILL.md +527 -0
  63. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/01-session-discovery.md +57 -0
  64. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/02-context-gathering.md +407 -0
  65. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/03-test-coverage-analysis.md +172 -0
  66. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/04-conflict-resolution.md +426 -0
  67. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/05-tdd-task-generation.md +473 -0
  68. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/06-tdd-structure-validation.md +189 -0
  69. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/07-tdd-verify.md +635 -0
  70. package/.claude/skills/ccw-chain/phases/workflow-test-fix/SKILL.md +482 -0
  71. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/01-session-start.md +60 -0
  72. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/02-test-context-gather.md +493 -0
  73. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/03-test-concept-enhanced.md +150 -0
  74. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/04-test-task-generate.md +346 -0
  75. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/05-test-cycle-execute.md +538 -0
  76. package/.claude/skills/ccw-chain/specs/auto-mode.md +47 -0
  77. package/.claude/skills/ccw-chain/specs/intent-patterns.md +60 -0
  78. package/.claude/skills/chain-loader/SKILL.md +78 -0
  79. package/.claude/skills/chain-loader/phases/01-analyze-skill.md +53 -0
  80. package/.claude/skills/chain-loader/phases/02-design-graph.md +73 -0
  81. package/.claude/skills/chain-loader/phases/03-generate-validate.md +75 -0
  82. package/.claude/skills/chain-loader/specs/chain-schema.md +99 -0
  83. package/.claude/skills/chain-loader/specs/design-patterns.md +99 -0
  84. package/.claude/skills/chain-loader/templates/chain-json.md +63 -0
  85. package/.claude/skills/review-cycle/phases/review-module.md +764 -764
  86. package/.claude/skills/review-cycle/phases/review-session.md +775 -775
  87. package/.claude/skills/workflow-multi-cli-plan/SKILL.md +2 -2
  88. package/.claude/skills/workflow-plan/phases/03-conflict-resolution.md +422 -422
  89. package/.claude/skills/workflow-plan/phases/05-plan-verify.md +395 -395
  90. package/.claude/skills/workflow-tdd-plan/phases/02-context-gathering.md +407 -407
  91. package/.claude/skills/workflow-tdd-plan/phases/04-conflict-resolution.md +426 -426
  92. package/.claude/skills/workflow-test-fix/phases/02-test-context-gather.md +493 -493
  93. package/README.md +14 -0
  94. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
  95. package/ccw/dist/core/routes/litellm-api-routes.js +0 -23
  96. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
  97. package/ccw/dist/tools/chain-loader.d.ts +10 -0
  98. package/ccw/dist/tools/chain-loader.d.ts.map +1 -0
  99. package/ccw/dist/tools/chain-loader.js +642 -0
  100. package/ccw/dist/tools/chain-loader.js.map +1 -0
  101. package/ccw/dist/tools/index.d.ts.map +1 -1
  102. package/ccw/dist/tools/index.js +2 -0
  103. package/ccw/dist/tools/index.js.map +1 -1
  104. package/ccw/dist/tools/json-builder.js +20 -0
  105. package/ccw/dist/tools/json-builder.js.map +1 -1
  106. package/ccw/dist/types/chain-types.d.ts +72 -0
  107. package/ccw/dist/types/chain-types.d.ts.map +1 -0
  108. package/ccw/dist/types/chain-types.js +5 -0
  109. package/ccw/dist/types/chain-types.js.map +1 -0
  110. package/ccw/scripts/prepublish-clean.mjs +0 -1
  111. package/package.json +1 -3
  112. package/ccw-litellm/README.md +0 -180
  113. package/ccw-litellm/pyproject.toml +0 -35
  114. package/ccw-litellm/src/ccw_litellm/__init__.py +0 -47
  115. package/ccw-litellm/src/ccw_litellm/cli.py +0 -108
  116. package/ccw-litellm/src/ccw_litellm/clients/__init__.py +0 -12
  117. package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +0 -270
  118. package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +0 -198
  119. package/ccw-litellm/src/ccw_litellm/config/__init__.py +0 -22
  120. package/ccw-litellm/src/ccw_litellm/config/loader.py +0 -343
  121. package/ccw-litellm/src/ccw_litellm/config/models.py +0 -162
  122. package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +0 -14
  123. package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +0 -52
  124. package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +0 -45
@@ -0,0 +1,720 @@
1
+ ---
2
+ name: convert-to-plan
3
+ description: Convert planning artifacts (lite-plan, workflow session, markdown) to issue solutions
4
+ argument-hint: "[-y|--yes] [--issue <id>] [--supplement] <SOURCE>"
5
+ allowed-tools: TodoWrite(*), Bash(*), Read(*), Write(*), Glob(*), AskUserQuestion(*)
6
+ ---
7
+
8
+ ## Auto Mode
9
+
10
+ When `--yes` or `-y`: Skip confirmation, auto-create issue and bind solution.
11
+
12
+ # Issue Convert-to-Plan Command (/issue:convert-to-plan)
13
+
14
+ ## Overview
15
+
16
+ Converts various planning artifact formats into issue workflow solutions with intelligent detection and automatic binding.
17
+
18
+ **Supported Sources** (auto-detected):
19
+ - **lite-plan**: `.workflow/.lite-plan/{slug}/plan.json`
20
+ - **workflow-session**: `WFS-xxx` ID or `.workflow/active/{session}/` folder
21
+ - **markdown**: Any `.md` file with implementation/task content
22
+ - **json**: Direct JSON files matching plan-json-schema
23
+
24
+ ## Quick Reference
25
+
26
+ ```bash
27
+ # Convert lite-plan to new issue (auto-creates issue)
28
+ /issue:convert-to-plan ".workflow/.lite-plan/implement-auth-2026-01-25"
29
+
30
+ # Convert workflow session to existing issue
31
+ /issue:convert-to-plan WFS-auth-impl --issue GH-123
32
+
33
+ # Supplement existing solution with additional tasks
34
+ /issue:convert-to-plan "./docs/additional-tasks.md" --issue ISS-001 --supplement
35
+
36
+ # Auto mode - skip confirmations
37
+ /issue:convert-to-plan ".workflow/.lite-plan/my-plan" -y
38
+ ```
39
+
40
+ ## Command Options
41
+
42
+ | Option | Description | Default |
43
+ |--------|-------------|---------|
44
+ | `<SOURCE>` | Planning artifact path or WFS-xxx ID | Required |
45
+ | `--issue <id>` | Bind to existing issue instead of creating new | Auto-create |
46
+ | `--supplement` | Add tasks to existing solution (requires --issue) | false |
47
+ | `-y, --yes` | Skip all confirmations | false |
48
+
49
+ ## Core Data Access Principle
50
+
51
+ **⚠️ Important**: Use CLI commands for all issue/solution operations.
52
+
53
+ | Operation | Correct | Incorrect |
54
+ |-----------|---------|-----------|
55
+ | Get issue | `ccw issue status <id> --json` | Read issues.jsonl directly |
56
+ | Create issue | `ccw issue init <id> --title "..."` | Write to issues.jsonl |
57
+ | Bind solution | `ccw issue bind <id> <sol-id>` | Edit issues.jsonl |
58
+ | List solutions | `ccw issue solution <id> --brief` | Read solutions/*.jsonl |
59
+
60
+ ## Solution Schema Reference
61
+
62
+ Target format for all extracted data (from solution-schema.json):
63
+
64
+ ```typescript
65
+ interface Solution {
66
+ id: string; // SOL-{issue-id}-{4-char-uid}
67
+ description?: string; // High-level summary
68
+ approach?: string; // Technical strategy
69
+ tasks: Task[]; // Required: at least 1 task
70
+ exploration_context?: object; // Optional: source context
71
+ analysis?: { risk, impact, complexity };
72
+ score?: number; // 0.0-1.0
73
+ is_bound: boolean;
74
+ created_at: string;
75
+ bound_at?: string;
76
+ }
77
+
78
+ interface Task {
79
+ id: string; // T1, T2, T3... (pattern: ^T[0-9]+$)
80
+ title: string; // Required: action verb + target
81
+ scope: string; // Required: module path or feature area
82
+ action: Action; // Required: Create|Update|Implement|...
83
+ description?: string;
84
+ files?: Array<{path, target, change}>;
85
+ implementation: string[]; // Required: step-by-step guide
86
+ test?: { unit?, integration?, commands?, coverage_target? };
87
+ convergence: { criteria: string[], verification: string[] }; // Required
88
+ commit?: { type, scope, message_template, breaking? };
89
+ depends_on?: string[];
90
+ priority?: number; // 1-5 (default: 3)
91
+ }
92
+
93
+ type Action = 'Create' | 'Update' | 'Implement' | 'Refactor' | 'Add' | 'Delete' | 'Configure' | 'Test' | 'Fix';
94
+ ```
95
+
96
+ ## Implementation
97
+
98
+ ### Phase 1: Parse Arguments & Detect Source Type
99
+
100
+ ```javascript
101
+ const input = userInput.trim();
102
+ const flags = parseFlags(userInput); // --issue, --supplement, -y/--yes
103
+
104
+ // Extract source path (first non-flag argument)
105
+ const source = extractSourceArg(input);
106
+
107
+ // Detect source type
108
+ function detectSourceType(source) {
109
+ // Check for WFS-xxx pattern (workflow session ID)
110
+ if (source.match(/^WFS-[\w-]+$/)) {
111
+ return { type: 'workflow-session-id', path: `.workflow/active/${source}` };
112
+ }
113
+
114
+ // Check if directory
115
+ const isDir = Bash(`test -d "${source}" && echo "dir" || echo "file"`).trim() === 'dir';
116
+
117
+ if (isDir) {
118
+ // Check for lite-plan indicator
119
+ const hasPlanJson = Bash(`test -f "${source}/plan.json" && echo "yes" || echo "no"`).trim() === 'yes';
120
+ if (hasPlanJson) {
121
+ return { type: 'lite-plan', path: source };
122
+ }
123
+
124
+ // Check for workflow session indicator
125
+ const hasSession = Bash(`test -f "${source}/workflow-session.json" && echo "yes" || echo "no"`).trim() === 'yes';
126
+ if (hasSession) {
127
+ return { type: 'workflow-session', path: source };
128
+ }
129
+ }
130
+
131
+ // Check file extensions
132
+ if (source.endsWith('.json')) {
133
+ return { type: 'json-file', path: source };
134
+ }
135
+ if (source.endsWith('.md')) {
136
+ return { type: 'markdown-file', path: source };
137
+ }
138
+
139
+ // Check if path exists at all
140
+ const exists = Bash(`test -e "${source}" && echo "yes" || echo "no"`).trim() === 'yes';
141
+ if (!exists) {
142
+ throw new Error(`E001: Source not found: ${source}`);
143
+ }
144
+
145
+ return { type: 'unknown', path: source };
146
+ }
147
+
148
+ const sourceInfo = detectSourceType(source);
149
+ if (sourceInfo.type === 'unknown') {
150
+ throw new Error(`E002: Unable to detect source format for: ${source}`);
151
+ }
152
+
153
+ console.log(`Detected source type: ${sourceInfo.type}`);
154
+ ```
155
+
156
+ ### Phase 2: Extract Data Using Format-Specific Extractor
157
+
158
+ ```javascript
159
+ let extracted = { title: '', approach: '', tasks: [], metadata: {} };
160
+
161
+ switch (sourceInfo.type) {
162
+ case 'lite-plan':
163
+ extracted = extractFromLitePlan(sourceInfo.path);
164
+ break;
165
+ case 'workflow-session':
166
+ case 'workflow-session-id':
167
+ extracted = extractFromWorkflowSession(sourceInfo.path);
168
+ break;
169
+ case 'markdown-file':
170
+ extracted = await extractFromMarkdownAI(sourceInfo.path);
171
+ break;
172
+ case 'json-file':
173
+ extracted = extractFromJsonFile(sourceInfo.path);
174
+ break;
175
+ }
176
+
177
+ // Validate extraction
178
+ if (!extracted.tasks || extracted.tasks.length === 0) {
179
+ throw new Error('E006: No tasks extracted from source');
180
+ }
181
+
182
+ // Ensure task IDs are normalized to T1, T2, T3...
183
+ extracted.tasks = normalizeTaskIds(extracted.tasks);
184
+
185
+ console.log(`Extracted: ${extracted.tasks.length} tasks`);
186
+ ```
187
+
188
+ #### Extractor: Lite-Plan
189
+
190
+ ```javascript
191
+ function extractFromLitePlan(folderPath) {
192
+ const planJson = Read(`${folderPath}/plan.json`);
193
+ const plan = JSON.parse(planJson);
194
+
195
+ return {
196
+ title: plan.summary?.split('.')[0]?.trim() || 'Untitled Plan',
197
+ description: plan.summary,
198
+ approach: plan.approach,
199
+ tasks: plan.tasks.map(t => ({
200
+ id: t.id,
201
+ title: t.title,
202
+ scope: t.scope || '',
203
+ action: t.action || 'Implement',
204
+ description: t.description || t.title,
205
+ files: (t.modification_points || []).map(mp => ({ path: mp.file, target: mp.target, change: mp.change })),
206
+ implementation: Array.isArray(t.implementation) ? t.implementation : [t.implementation || ''],
207
+ test: t.verification ? {
208
+ unit: t.verification.unit_tests,
209
+ integration: t.verification.integration_tests,
210
+ commands: t.verification.manual_checks
211
+ } : {},
212
+ convergence: {
213
+ criteria: Array.isArray(t.acceptance) ? t.acceptance : [t.acceptance || ''],
214
+ verification: t.verification?.manual_checks || []
215
+ },
216
+ depends_on: t.depends_on || [],
217
+ priority: 3
218
+ })),
219
+ metadata: {
220
+ source_type: 'lite-plan',
221
+ source_path: folderPath,
222
+ complexity: plan.complexity,
223
+ estimated_time: plan.estimated_time,
224
+ exploration_angles: plan._metadata?.exploration_angles || [],
225
+ original_timestamp: plan._metadata?.timestamp
226
+ }
227
+ };
228
+ }
229
+ ```
230
+
231
+ #### Extractor: Workflow Session
232
+
233
+ ```javascript
234
+ function extractFromWorkflowSession(sessionPath) {
235
+ // Load session metadata
236
+ const sessionJson = Read(`${sessionPath}/workflow-session.json`);
237
+ const session = JSON.parse(sessionJson);
238
+
239
+ // Load IMPL_PLAN.md for approach (if exists)
240
+ let approach = '';
241
+ const implPlanPath = `${sessionPath}/IMPL_PLAN.md`;
242
+ const hasImplPlan = Bash(`test -f "${implPlanPath}" && echo "yes" || echo "no"`).trim() === 'yes';
243
+ if (hasImplPlan) {
244
+ const implPlan = Read(implPlanPath);
245
+ // Extract overview/approach section
246
+ const overviewMatch = implPlan.match(/##\s*(?:Overview|Approach|Strategy)\s*\n([\s\S]*?)(?=\n##|$)/i);
247
+ approach = overviewMatch?.[1]?.trim() || implPlan.split('\n').slice(0, 10).join('\n');
248
+ }
249
+
250
+ // Load all task JSONs from .task folder
251
+ const taskFiles = Glob({ pattern: `${sessionPath}/.task/IMPL-*.json` });
252
+ const tasks = taskFiles.map(f => {
253
+ const taskJson = Read(f);
254
+ const task = JSON.parse(taskJson);
255
+ return {
256
+ id: task.id?.replace(/^IMPL-0*/, 'T') || 'T1', // IMPL-001 → T1
257
+ title: task.title,
258
+ scope: task.scope || inferScopeFromTask(task),
259
+ action: capitalizeAction(task.type) || 'Implement',
260
+ description: task.description,
261
+ files: (task.implementation?.modification_points || []).map(mp => ({ path: mp.file, target: mp.target, change: mp.change })),
262
+ implementation: task.implementation?.steps || [],
263
+ test: task.implementation?.test || {},
264
+ convergence: {
265
+ criteria: task.acceptance_criteria || [],
266
+ verification: task.verification_steps || []
267
+ },
268
+ commit: task.commit,
269
+ depends_on: (task.depends_on || []).map(d => d.replace(/^IMPL-0*/, 'T')),
270
+ priority: task.priority || 3
271
+ };
272
+ });
273
+
274
+ return {
275
+ title: session.name || session.description?.split('.')[0] || 'Workflow Session',
276
+ description: session.description || session.name,
277
+ approach: approach || session.description,
278
+ tasks: tasks,
279
+ metadata: {
280
+ source_type: 'workflow-session',
281
+ source_path: sessionPath,
282
+ session_id: session.id,
283
+ created_at: session.created_at
284
+ }
285
+ };
286
+ }
287
+
288
+ function inferScopeFromTask(task) {
289
+ if (task.files?.length) {
290
+ const paths = task.files.map(f => f.path);
291
+ // Find common directory prefix
292
+ const dirs = paths.map(p => p.split('/').slice(0, -1).join('/'));
293
+ return [...new Set(dirs)][0] || '';
294
+ }
295
+ return '';
296
+ }
297
+
298
+ function capitalizeAction(type) {
299
+ if (!type) return 'Implement';
300
+ const map = { feature: 'Implement', bugfix: 'Fix', refactor: 'Refactor', test: 'Test', docs: 'Update' };
301
+ return map[type.toLowerCase()] || type.charAt(0).toUpperCase() + type.slice(1);
302
+ }
303
+ ```
304
+
305
+ #### Extractor: Markdown (AI-Assisted via Gemini)
306
+
307
+ ```javascript
308
+ async function extractFromMarkdownAI(filePath) {
309
+ const fileContent = Read(filePath);
310
+
311
+ // Use Gemini CLI for intelligent extraction
312
+ const cliPrompt = `PURPOSE: Extract implementation plan from markdown document for issue solution conversion. Must output ONLY valid JSON.
313
+ TASK: • Analyze document structure • Identify title/summary • Extract approach/strategy section • Parse tasks from any format (lists, tables, sections, code blocks) • Normalize each task to solution schema
314
+ MODE: analysis
315
+ CONTEXT: Document content provided below
316
+ EXPECTED: Valid JSON object with format:
317
+ {
318
+ "title": "extracted title",
319
+ "approach": "extracted approach/strategy",
320
+ "tasks": [
321
+ {
322
+ "id": "T1",
323
+ "title": "task title",
324
+ "scope": "module or feature area",
325
+ "action": "Implement|Update|Create|Fix|Refactor|Add|Delete|Configure|Test",
326
+ "description": "what to do",
327
+ "implementation": ["step 1", "step 2"],
328
+ "acceptance": ["criteria 1", "criteria 2"]
329
+ }
330
+ ]
331
+ }
332
+ CONSTRAINTS: Output ONLY valid JSON - no markdown, no explanation | Action must be one of: Create, Update, Implement, Refactor, Add, Delete, Configure, Test, Fix | Tasks must have id, title, scope, action, implementation (array), acceptance (array)
333
+
334
+ DOCUMENT CONTENT:
335
+ ${fileContent}`;
336
+
337
+ // Execute Gemini CLI
338
+ const result = Bash(`ccw cli -p '${cliPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis`, { timeout: 120000 });
339
+
340
+ // Parse JSON from result (may be wrapped in markdown code block)
341
+ let jsonText = result.trim();
342
+ const jsonMatch = jsonText.match(/```(?:json)?\s*([\s\S]*?)```/);
343
+ if (jsonMatch) {
344
+ jsonText = jsonMatch[1].trim();
345
+ }
346
+
347
+ try {
348
+ const extracted = JSON.parse(jsonText);
349
+
350
+ // Normalize tasks
351
+ const tasks = (extracted.tasks || []).map((t, i) => ({
352
+ id: t.id || `T${i + 1}`,
353
+ title: t.title || 'Untitled task',
354
+ scope: t.scope || '',
355
+ action: validateAction(t.action) || 'Implement',
356
+ description: t.description || t.title,
357
+ files: (t.modification_points || []).map(mp => ({ path: mp.file, target: mp.target, change: mp.change })),
358
+ implementation: Array.isArray(t.implementation) ? t.implementation : [t.implementation || ''],
359
+ test: t.test || {},
360
+ convergence: {
361
+ criteria: Array.isArray(t.acceptance) ? t.acceptance : [t.acceptance || ''],
362
+ verification: t.verification || []
363
+ },
364
+ depends_on: t.depends_on || [],
365
+ priority: t.priority || 3
366
+ }));
367
+
368
+ return {
369
+ title: extracted.title || 'Extracted Plan',
370
+ description: extracted.summary || extracted.title,
371
+ approach: extracted.approach || '',
372
+ tasks: tasks,
373
+ metadata: {
374
+ source_type: 'markdown',
375
+ source_path: filePath,
376
+ extraction_method: 'gemini-ai'
377
+ }
378
+ };
379
+ } catch (e) {
380
+ // Provide more context for debugging
381
+ throw new Error(`E005: Failed to extract tasks from markdown. Gemini response was not valid JSON. Error: ${e.message}. Response preview: ${jsonText.substring(0, 200)}...`);
382
+ }
383
+ }
384
+
385
+ function validateAction(action) {
386
+ const validActions = ['Create', 'Update', 'Implement', 'Refactor', 'Add', 'Delete', 'Configure', 'Test', 'Fix'];
387
+ if (!action) return null;
388
+ const normalized = action.charAt(0).toUpperCase() + action.slice(1).toLowerCase();
389
+ return validActions.includes(normalized) ? normalized : null;
390
+ }
391
+ ```
392
+
393
+ #### Extractor: JSON File
394
+
395
+ ```javascript
396
+ function extractFromJsonFile(filePath) {
397
+ const content = Read(filePath);
398
+ const plan = JSON.parse(content);
399
+
400
+ // Detect if it's already solution format or plan format
401
+ if (plan.tasks && Array.isArray(plan.tasks)) {
402
+ // Map tasks to normalized format
403
+ const tasks = plan.tasks.map((t, i) => ({
404
+ id: t.id || `T${i + 1}`,
405
+ title: t.title,
406
+ scope: t.scope || '',
407
+ action: t.action || 'Implement',
408
+ description: t.description || t.title,
409
+ files: (t.modification_points || []).map(mp => ({ path: mp.file, target: mp.target, change: mp.change })),
410
+ implementation: Array.isArray(t.implementation) ? t.implementation : [t.implementation || ''],
411
+ test: t.test || t.verification || {},
412
+ convergence: normalizeConvergence(t.acceptance, t.convergence),
413
+ depends_on: t.depends_on || [],
414
+ priority: t.priority || 3
415
+ }));
416
+
417
+ return {
418
+ title: plan.summary?.split('.')[0] || plan.title || 'JSON Plan',
419
+ description: plan.summary || plan.description,
420
+ approach: plan.approach,
421
+ tasks: tasks,
422
+ metadata: {
423
+ source_type: 'json',
424
+ source_path: filePath,
425
+ complexity: plan.complexity,
426
+ original_metadata: plan._metadata
427
+ }
428
+ };
429
+ }
430
+
431
+ throw new Error('E002: JSON file does not contain valid plan structure (missing tasks array)');
432
+ }
433
+
434
+ function normalizeConvergence(acceptance, convergence) {
435
+ // Prefer new convergence field; fall back to legacy acceptance
436
+ const source = convergence || acceptance;
437
+ if (!source) return { criteria: [], verification: [] };
438
+ if (typeof source === 'object' && source.criteria) return source;
439
+ if (Array.isArray(source)) return { criteria: source, verification: [] };
440
+ return { criteria: [String(source)], verification: [] };
441
+ }
442
+ ```
443
+
444
+ ### Phase 3: Normalize Task IDs
445
+
446
+ ```javascript
447
+ function normalizeTaskIds(tasks) {
448
+ return tasks.map((t, i) => ({
449
+ ...t,
450
+ id: `T${i + 1}`,
451
+ // Also normalize depends_on references
452
+ depends_on: (t.depends_on || []).map(d => {
453
+ // Handle various ID formats: IMPL-001, T1, 1, etc.
454
+ const num = d.match(/\d+/)?.[0];
455
+ return num ? `T${parseInt(num)}` : d;
456
+ })
457
+ }));
458
+ }
459
+ ```
460
+
461
+ ### Phase 4: Resolve Issue (Create or Find)
462
+
463
+ ```javascript
464
+ let issueId = flags.issue;
465
+ let existingSolution = null;
466
+
467
+ if (issueId) {
468
+ // Validate issue exists
469
+ let issueCheck;
470
+ try {
471
+ issueCheck = Bash(`ccw issue status ${issueId} --json 2>/dev/null`).trim();
472
+ if (!issueCheck || issueCheck === '') {
473
+ throw new Error('empty response');
474
+ }
475
+ } catch (e) {
476
+ throw new Error(`E003: Issue not found: ${issueId}`);
477
+ }
478
+
479
+ const issue = JSON.parse(issueCheck);
480
+
481
+ // Check if issue already has bound solution
482
+ if (issue.bound_solution_id && !flags.supplement) {
483
+ throw new Error(`E004: Issue ${issueId} already has bound solution (${issue.bound_solution_id}). Use --supplement to add tasks.`);
484
+ }
485
+
486
+ // Load existing solution for supplement mode
487
+ if (flags.supplement && issue.bound_solution_id) {
488
+ try {
489
+ const solResult = Bash(`ccw issue solution ${issue.bound_solution_id} --json`).trim();
490
+ existingSolution = JSON.parse(solResult);
491
+ console.log(`Loaded existing solution with ${existingSolution.tasks.length} tasks`);
492
+ } catch (e) {
493
+ throw new Error(`Failed to load existing solution: ${e.message}`);
494
+ }
495
+ }
496
+ } else {
497
+ // Create new issue via ccw issue create (auto-generates correct ID)
498
+ // Smart extraction: title from content, priority from complexity
499
+ const title = extracted.title || 'Converted Plan';
500
+ const context = extracted.description || extracted.approach || title;
501
+
502
+ // Auto-determine priority based on complexity
503
+ const complexityMap = { high: 2, medium: 3, low: 4 };
504
+ const priority = complexityMap[extracted.metadata.complexity?.toLowerCase()] || 3;
505
+
506
+ try {
507
+ // Use heredoc to avoid shell escaping issues
508
+ const createResult = Bash(`ccw issue create << 'EOF'
509
+ {
510
+ "title": ${JSON.stringify(title)},
511
+ "context": ${JSON.stringify(context)},
512
+ "priority": ${priority},
513
+ "source": "converted"
514
+ }
515
+ EOF`).trim();
516
+
517
+ // Parse result to get created issue ID
518
+ const created = JSON.parse(createResult);
519
+ issueId = created.id;
520
+ console.log(`Created issue: ${issueId} (priority: ${priority})`);
521
+ } catch (e) {
522
+ throw new Error(`Failed to create issue: ${e.message}`);
523
+ }
524
+ }
525
+ ```
526
+
527
+ ### Phase 5: Generate Solution
528
+
529
+ ```javascript
530
+ // Generate solution ID
531
+ function generateSolutionId(issueId) {
532
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
533
+ let uid = '';
534
+ for (let i = 0; i < 4; i++) {
535
+ uid += chars[Math.floor(Math.random() * chars.length)];
536
+ }
537
+ return `SOL-${issueId}-${uid}`;
538
+ }
539
+
540
+ let solution;
541
+ const solutionId = generateSolutionId(issueId);
542
+
543
+ if (flags.supplement && existingSolution) {
544
+ // Supplement mode: merge with existing solution
545
+ const maxTaskId = Math.max(...existingSolution.tasks.map(t => parseInt(t.id.slice(1))));
546
+
547
+ const newTasks = extracted.tasks.map((t, i) => ({
548
+ ...t,
549
+ id: `T${maxTaskId + i + 1}`
550
+ }));
551
+
552
+ solution = {
553
+ ...existingSolution,
554
+ tasks: [...existingSolution.tasks, ...newTasks],
555
+ approach: existingSolution.approach + '\n\n[Supplementary] ' + (extracted.approach || ''),
556
+ updated_at: new Date().toISOString()
557
+ };
558
+
559
+ console.log(`Supplementing: ${existingSolution.tasks.length} existing + ${newTasks.length} new = ${solution.tasks.length} total tasks`);
560
+ } else {
561
+ // New solution
562
+ solution = {
563
+ id: solutionId,
564
+ description: extracted.description || extracted.title,
565
+ approach: extracted.approach,
566
+ tasks: extracted.tasks,
567
+ exploration_context: extracted.metadata.exploration_angles ? {
568
+ exploration_angles: extracted.metadata.exploration_angles
569
+ } : undefined,
570
+ analysis: {
571
+ risk: 'medium',
572
+ impact: 'medium',
573
+ complexity: extracted.metadata.complexity?.toLowerCase() || 'medium'
574
+ },
575
+ is_bound: false,
576
+ created_at: new Date().toISOString(),
577
+ _conversion_metadata: {
578
+ source_type: extracted.metadata.source_type,
579
+ source_path: extracted.metadata.source_path,
580
+ converted_at: new Date().toISOString()
581
+ }
582
+ };
583
+ }
584
+ ```
585
+
586
+ ### Phase 6: Confirm & Persist
587
+
588
+ ```javascript
589
+ // Display preview
590
+ console.log(`
591
+ ## Conversion Summary
592
+
593
+ **Issue**: ${issueId}
594
+ **Solution**: ${flags.supplement ? existingSolution.id : solutionId}
595
+ **Tasks**: ${solution.tasks.length}
596
+ **Mode**: ${flags.supplement ? 'Supplement' : 'New'}
597
+
598
+ ### Tasks:
599
+ ${solution.tasks.map(t => `- ${t.id}: ${t.title} [${t.action}]`).join('\n')}
600
+ `);
601
+
602
+ // Confirm if not auto mode
603
+ if (!flags.yes && !flags.y) {
604
+ const confirm = AskUserQuestion({
605
+ questions: [{
606
+ question: `Create solution for issue ${issueId} with ${solution.tasks.length} tasks?`,
607
+ header: 'Confirm',
608
+ multiSelect: false,
609
+ options: [
610
+ { label: 'Yes, create solution', description: 'Create and bind solution' },
611
+ { label: 'Cancel', description: 'Abort without changes' }
612
+ ]
613
+ }]
614
+ });
615
+
616
+ if (!confirm.answers?.['Confirm']?.includes('Yes')) {
617
+ console.log('Cancelled.');
618
+ return;
619
+ }
620
+ }
621
+
622
+ // Persist solution (following issue-plan-agent pattern)
623
+ Bash(`mkdir -p .workflow/issues/solutions`);
624
+
625
+ const solutionFile = `.workflow/issues/solutions/${issueId}.jsonl`;
626
+
627
+ if (flags.supplement) {
628
+ // Supplement mode: update existing solution line atomically
629
+ try {
630
+ const existingContent = Read(solutionFile);
631
+ const lines = existingContent.trim().split('\n').filter(l => l);
632
+ const updatedLines = lines.map(line => {
633
+ const sol = JSON.parse(line);
634
+ if (sol.id === existingSolution.id) {
635
+ return JSON.stringify(solution);
636
+ }
637
+ return line;
638
+ });
639
+ // Atomic write: write entire content at once
640
+ Write({ file_path: solutionFile, content: updatedLines.join('\n') + '\n' });
641
+ console.log(`✓ Updated solution: ${existingSolution.id}`);
642
+ } catch (e) {
643
+ throw new Error(`Failed to update solution: ${e.message}`);
644
+ }
645
+
646
+ // Note: No need to rebind - solution is already bound to issue
647
+ } else {
648
+ // New solution: append to JSONL file (following issue-plan-agent pattern)
649
+ try {
650
+ const solutionLine = JSON.stringify(solution);
651
+
652
+ // Read existing content, append new line, write atomically
653
+ const existing = Bash(`test -f "${solutionFile}" && cat "${solutionFile}" || echo ""`).trim();
654
+ const newContent = existing ? existing + '\n' + solutionLine + '\n' : solutionLine + '\n';
655
+ Write({ file_path: solutionFile, content: newContent });
656
+
657
+ console.log(`✓ Created solution: ${solutionId}`);
658
+ } catch (e) {
659
+ throw new Error(`Failed to write solution: ${e.message}`);
660
+ }
661
+
662
+ // Bind solution to issue
663
+ try {
664
+ Bash(`ccw issue bind ${issueId} ${solutionId}`);
665
+ console.log(`✓ Bound solution to issue`);
666
+ } catch (e) {
667
+ // Cleanup: remove solution file on bind failure
668
+ try {
669
+ Bash(`rm -f "${solutionFile}"`);
670
+ } catch (cleanupError) {
671
+ // Ignore cleanup errors
672
+ }
673
+ throw new Error(`Failed to bind solution: ${e.message}`);
674
+ }
675
+
676
+ // Update issue status to planned
677
+ try {
678
+ Bash(`ccw issue update ${issueId} --status planned`);
679
+ } catch (e) {
680
+ throw new Error(`Failed to update issue status: ${e.message}`);
681
+ }
682
+ }
683
+ ```
684
+
685
+ ### Phase 7: Summary
686
+
687
+ ```javascript
688
+ console.log(`
689
+ ## Done
690
+
691
+ **Issue**: ${issueId}
692
+ **Solution**: ${flags.supplement ? existingSolution.id : solutionId}
693
+ **Tasks**: ${solution.tasks.length}
694
+ **Status**: planned
695
+
696
+ ### Next Steps:
697
+ - \`/issue:queue\` → Form execution queue
698
+ - \`ccw issue status ${issueId}\` → View issue details
699
+ - \`ccw issue solution ${flags.supplement ? existingSolution.id : solutionId}\` → View solution
700
+ `);
701
+ ```
702
+
703
+ ## Error Handling
704
+
705
+ | Error | Code | Resolution |
706
+ |-------|------|------------|
707
+ | Source not found | E001 | Check path exists |
708
+ | Invalid source format | E002 | Verify file contains valid plan structure |
709
+ | Issue not found | E003 | Check issue ID or omit --issue to create new |
710
+ | Solution already bound | E004 | Use --supplement to add tasks |
711
+ | AI extraction failed | E005 | Check markdown structure, try simpler format |
712
+ | No tasks extracted | E006 | Source must contain at least 1 task |
713
+
714
+ ## Related Commands
715
+
716
+ - `/issue:plan` - Generate solutions from issue exploration
717
+ - `/issue:queue` - Form execution queue from bound solutions
718
+ - `/issue:execute` - Execute queue with DAG parallelism
719
+ - `ccw issue status <id>` - View issue details
720
+ - `ccw issue solution <id>` - View solution details