claude-code-workflow 6.3.48 → 6.3.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +6 -8
- package/.claude/agents/action-planning-agent.md +28 -45
- package/.claude/agents/cli-lite-planning-agent.md +93 -1
- package/.claude/agents/code-developer.md +144 -27
- package/.claude/commands/ccw-coordinator.md +175 -21
- package/.claude/commands/ccw-debug.md +832 -0
- package/.claude/commands/ccw.md +90 -9
- package/.claude/commands/cli/cli-init.md +1 -0
- package/.claude/commands/issue/convert-to-plan.md +718 -0
- package/.claude/commands/issue/from-brainstorm.md +382 -0
- package/.claude/commands/memory/tips.md +332 -0
- package/.claude/commands/workflow/analyze-with-file.md +804 -0
- package/.claude/commands/workflow/brainstorm/auto-parallel.md +18 -43
- package/.claude/commands/workflow/brainstorm/role-analysis.md +705 -0
- package/.claude/commands/workflow/brainstorm-with-file.md +1153 -0
- package/.claude/commands/workflow/debug-with-file.md +7 -5
- package/.claude/commands/workflow/execute.md +6 -4
- package/.claude/commands/workflow/lite-plan.md +2 -2
- package/.claude/commands/workflow/plan-verify.md +162 -327
- package/.claude/commands/workflow/plan.md +162 -26
- package/.claude/commands/workflow/replan.md +78 -2
- package/.claude/commands/workflow/{review-fix.md → review-cycle-fix.md} +6 -6
- package/.claude/commands/workflow/review-module-cycle.md +2 -2
- package/.claude/commands/workflow/review-session-cycle.md +2 -2
- package/.claude/commands/workflow/tools/conflict-resolution.md +16 -26
- package/.claude/commands/workflow/tools/context-gather.md +81 -118
- package/.claude/commands/workflow/tools/task-generate-agent.md +94 -10
- package/.claude/skills/ccw-help/command.json +4 -4
- package/.claude/skills/lite-skill-generator/SKILL.md +650 -0
- package/.claude/skills/lite-skill-generator/templates/simple-skill.md +68 -0
- package/.claude/skills/lite-skill-generator/templates/style-guide.md +64 -0
- package/.claude/skills/skill-generator/SKILL.md +277 -85
- package/.claude/skills/skill-generator/phases/01-requirements-discovery.md +4 -15
- package/.claude/skills/skill-generator/phases/02-structure-generation.md +72 -17
- package/.claude/skills/skill-generator/phases/03-phase-generation.md +218 -51
- package/.claude/skills/skill-generator/phases/04-specs-templates.md +111 -41
- package/.claude/skills/skill-generator/phases/05-validation.md +139 -56
- package/.claude/skills/skill-generator/templates/autonomous-action.md +78 -268
- package/.claude/skills/skill-generator/templates/autonomous-orchestrator.md +14 -0
- package/.claude/skills/skill-generator/templates/code-analysis-action.md +12 -0
- package/.claude/skills/skill-generator/templates/llm-action.md +12 -0
- package/.claude/skills/skill-generator/templates/script-template.md +368 -0
- package/.claude/skills/skill-generator/templates/sequential-phase.md +14 -0
- package/.claude/skills/skill-generator/templates/skill-md.md +14 -0
- package/.claude/skills/skill-tuning/SKILL.md +130 -266
- package/.claude/skills/skill-tuning/phases/orchestrator.md +95 -283
- package/.claude/skills/skill-tuning/specs/problem-taxonomy.md +90 -198
- package/.claude/skills/skill-tuning/specs/tuning-strategies.md +193 -1345
- package/.claude/workflows/cli-templates/schemas/plan-verify-agent-schema.json +47 -0
- package/.claude/workflows/cli-templates/schemas/verify-json-schema.json +158 -0
- package/.claude/workflows/cli-tools-usage.md +1 -1
- package/.codex/AGENTS.md +1 -3
- package/.codex/prompts/analyze-with-file.md +607 -0
- package/.codex/prompts/brainstorm-to-cycle.md +455 -0
- package/.codex/prompts/brainstorm-with-file.md +933 -0
- package/.codex/prompts/debug-with-file.md +15 -20
- package/.codex/skills/ccw-cli-tools/SKILL.md +559 -0
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +29 -5
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/issue.d.ts +2 -0
- package/ccw/dist/commands/issue.d.ts.map +1 -1
- package/ccw/dist/commands/issue.js +62 -20
- package/ccw/dist/commands/issue.js.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.js +5 -3
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -1
- package/ccw/dist/config/litellm-provider-models.d.ts +73 -0
- package/ccw/dist/config/litellm-provider-models.d.ts.map +1 -0
- package/ccw/dist/config/litellm-provider-models.js +172 -0
- package/ccw/dist/config/litellm-provider-models.js.map +1 -0
- package/ccw/dist/config/provider-models.d.ts +25 -51
- package/ccw/dist/config/provider-models.d.ts.map +1 -1
- package/ccw/dist/config/provider-models.js +84 -149
- package/ccw/dist/config/provider-models.js.map +1 -1
- package/ccw/dist/config/storage-paths.d.ts.map +1 -1
- package/ccw/dist/config/storage-paths.js +23 -5
- package/ccw/dist/config/storage-paths.js.map +1 -1
- package/ccw/dist/core/auth/csrf-middleware.js +3 -3
- package/ccw/dist/core/auth/csrf-middleware.js.map +1 -1
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
- package/ccw/dist/core/dashboard-generator.js +3 -1
- package/ccw/dist/core/dashboard-generator.js.map +1 -1
- package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/claude-routes.js +206 -14
- package/ccw/dist/core/routes/claude-routes.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/commands-routes.d.ts +7 -0
- package/ccw/dist/core/routes/commands-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/commands-routes.js +480 -0
- package/ccw/dist/core/routes/commands-routes.js.map +1 -0
- package/ccw/dist/core/routes/model-routes.d.ts +11 -0
- package/ccw/dist/core/routes/model-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/model-routes.js +112 -0
- package/ccw/dist/core/routes/model-routes.js.map +1 -0
- package/ccw/dist/core/routes/nav-status-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/nav-status-routes.js +84 -1
- package/ccw/dist/core/routes/nav-status-routes.js.map +1 -1
- package/ccw/dist/core/routes/provider-routes.d.ts +11 -0
- package/ccw/dist/core/routes/provider-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/provider-routes.js +67 -0
- package/ccw/dist/core/routes/provider-routes.js.map +1 -0
- package/ccw/dist/core/routes/skills-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/skills-routes.js +219 -7
- package/ccw/dist/core/routes/skills-routes.js.map +1 -1
- package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/system-routes.js +58 -6
- package/ccw/dist/core/routes/system-routes.js.map +1 -1
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +13 -0
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/mcp-server/index.js +2 -2
- package/ccw/dist/mcp-server/index.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +48 -11
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +146 -50
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/cli-config-manager.d.ts +1 -13
- package/ccw/dist/tools/cli-config-manager.d.ts.map +1 -1
- package/ccw/dist/tools/cli-config-manager.js +3 -27
- package/ccw/dist/tools/cli-config-manager.js.map +1 -1
- package/ccw/dist/tools/cli-executor-core.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor-core.js +7 -2
- package/ccw/dist/tools/cli-executor-core.js.map +1 -1
- package/ccw/dist/tools/cli-executor-state.d.ts.map +1 -1
- package/ccw/dist/tools/cli-history-store.d.ts +11 -0
- package/ccw/dist/tools/cli-history-store.d.ts.map +1 -1
- package/ccw/dist/tools/cli-history-store.js +82 -2
- package/ccw/dist/tools/cli-history-store.js.map +1 -1
- package/ccw/dist/tools/command-registry.d.ts +7 -0
- package/ccw/dist/tools/command-registry.d.ts.map +1 -1
- package/ccw/dist/tools/command-registry.js +14 -1
- package/ccw/dist/tools/command-registry.js.map +1 -1
- package/ccw/dist/tools/generate-module-docs.d.ts.map +1 -1
- package/ccw/dist/tools/generate-module-docs.js +11 -7
- package/ccw/dist/tools/generate-module-docs.js.map +1 -1
- package/ccw/dist/tools/litellm-executor.d.ts +1 -0
- package/ccw/dist/tools/litellm-executor.d.ts.map +1 -1
- package/ccw/dist/tools/litellm-executor.js +11 -9
- package/ccw/dist/tools/litellm-executor.js.map +1 -1
- package/ccw/dist/types/skill-types.d.ts +97 -0
- package/ccw/dist/types/skill-types.d.ts.map +1 -0
- package/ccw/dist/types/skill-types.js +6 -0
- package/ccw/dist/types/skill-types.js.map +1 -0
- package/ccw/src/commands/cli.ts +36 -5
- package/ccw/src/commands/issue.ts +81 -26
- package/ccw/src/config/litellm-api-config-manager.ts +5 -3
- package/ccw/src/config/litellm-provider-models.ts +222 -0
- package/ccw/src/config/provider-models.ts +91 -190
- package/ccw/src/config/storage-paths.ts +20 -5
- package/ccw/src/core/auth/csrf-middleware.ts +3 -3
- package/ccw/src/core/dashboard-generator.ts +3 -1
- package/ccw/src/core/routes/claude-routes.ts +233 -15
- package/ccw/src/core/routes/cli-routes.ts +2 -3
- package/ccw/src/core/routes/commands-routes.ts +620 -0
- package/ccw/src/core/routes/nav-status-routes.ts +95 -1
- package/ccw/src/core/routes/provider-routes.ts +78 -0
- package/ccw/src/core/routes/skills-routes.ts +266 -45
- package/ccw/src/core/routes/system-routes.ts +102 -50
- package/ccw/src/core/server.ts +13 -0
- package/ccw/src/mcp-server/index.ts +2 -2
- package/ccw/src/templates/dashboard-css/18-cli-settings.css +35 -0
- package/ccw/src/templates/dashboard-css/37-commands.css +193 -0
- package/ccw/src/templates/dashboard-js/components/navigation.js +4 -0
- package/ccw/src/templates/dashboard-js/i18n.js +116 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +249 -4
- package/ccw/src/templates/dashboard-js/views/commands-manager.js +503 -0
- package/ccw/src/templates/dashboard-js/views/issue-manager.js +7 -7
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2 -7
- package/ccw/src/templates/dashboard-js/views/skills-manager.js +164 -23
- package/ccw/src/templates/dashboard.html +7 -0
- package/ccw/src/tools/claude-cli-tools.ts +170 -56
- package/ccw/src/tools/cli-config-manager.ts +2 -33
- package/ccw/src/tools/cli-executor-core.ts +8 -2
- package/ccw/src/tools/cli-history-store.ts +92 -2
- package/ccw/src/tools/command-registry.ts +16 -1
- package/ccw/src/tools/generate-module-docs.ts +11 -7
- package/ccw/src/tools/litellm-executor.ts +13 -9
- package/ccw/src/types/skill-types.ts +99 -0
- package/package.json +1 -1
- package/.claude/commands/enhance-prompt.md +0 -93
- package/.claude/commands/memory/code-map-memory.md +0 -687
- package/.claude/commands/memory/docs.md +0 -615
- package/.claude/commands/memory/load-skill-memory.md +0 -182
- package/.claude/commands/memory/skill-memory.md +0 -525
- package/.claude/commands/memory/swagger-docs.md +0 -773
- package/.claude/commands/memory/tech-research-rules.md +0 -310
- package/.claude/commands/memory/workflow-skill-memory.md +0 -517
- package/.claude/commands/task/breakdown.md +0 -208
- package/.claude/commands/task/create.md +0 -152
- package/.claude/commands/task/execute.md +0 -270
- package/.claude/commands/task/replan.md +0 -441
- package/.claude/commands/version.md +0 -254
- package/.claude/commands/workflow/action-plan-verify.md +0 -485
- package/.claude/commands/workflow/brainstorm/api-designer.md +0 -587
- package/.claude/commands/workflow/brainstorm/data-architect.md +0 -220
- package/.claude/commands/workflow/brainstorm/product-manager.md +0 -200
- package/.claude/commands/workflow/brainstorm/product-owner.md +0 -200
- package/.claude/commands/workflow/brainstorm/scrum-master.md +0 -200
- package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +0 -200
- package/.claude/commands/workflow/brainstorm/system-architect.md +0 -389
- package/.claude/commands/workflow/brainstorm/ui-designer.md +0 -221
- package/.claude/commands/workflow/brainstorm/ux-expert.md +0 -221
- package/.claude/commands/workflow/debug.md +0 -331
- package/.claude/commands/workflow/develop-with-file.md +0 -1044
- package/.claude/skills/ccw-loop/README.md +0 -303
- package/.claude/skills/skill-generator/templates/script-bash.md +0 -277
- package/.claude/skills/skill-generator/templates/script-python.md +0 -198
- package/.codex/prompts/debug.md +0 -318
- package/ccw/src/core/routes/mcp-routes.ts.backup +0 -549
|
@@ -0,0 +1,718 @@
|
|
|
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 solutions --issue <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
|
+
modification_points?: Array<{file, target, change}>;
|
|
85
|
+
implementation: string[]; // Required: step-by-step guide
|
|
86
|
+
test?: { unit?, integration?, commands?, coverage_target? };
|
|
87
|
+
acceptance: { 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
|
+
modification_points: t.modification_points || [],
|
|
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
|
+
acceptance: {
|
|
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
|
+
modification_points: task.implementation?.modification_points || [],
|
|
262
|
+
implementation: task.implementation?.steps || [],
|
|
263
|
+
test: task.implementation?.test || {},
|
|
264
|
+
acceptance: {
|
|
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.implementation?.modification_points?.length) {
|
|
290
|
+
const files = task.implementation.modification_points.map(m => m.file);
|
|
291
|
+
// Find common directory prefix
|
|
292
|
+
const dirs = files.map(f => f.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
|
+
modification_points: t.modification_points || [],
|
|
358
|
+
implementation: Array.isArray(t.implementation) ? t.implementation : [t.implementation || ''],
|
|
359
|
+
test: t.test || {},
|
|
360
|
+
acceptance: {
|
|
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
|
+
modification_points: t.modification_points || [],
|
|
410
|
+
implementation: Array.isArray(t.implementation) ? t.implementation : [t.implementation || ''],
|
|
411
|
+
test: t.test || t.verification || {},
|
|
412
|
+
acceptance: normalizeAcceptance(t.acceptance),
|
|
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 normalizeAcceptance(acceptance) {
|
|
435
|
+
if (!acceptance) return { criteria: [], verification: [] };
|
|
436
|
+
if (typeof acceptance === 'object' && acceptance.criteria) return acceptance;
|
|
437
|
+
if (Array.isArray(acceptance)) return { criteria: acceptance, verification: [] };
|
|
438
|
+
return { criteria: [String(acceptance)], verification: [] };
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Phase 3: Normalize Task IDs
|
|
443
|
+
|
|
444
|
+
```javascript
|
|
445
|
+
function normalizeTaskIds(tasks) {
|
|
446
|
+
return tasks.map((t, i) => ({
|
|
447
|
+
...t,
|
|
448
|
+
id: `T${i + 1}`,
|
|
449
|
+
// Also normalize depends_on references
|
|
450
|
+
depends_on: (t.depends_on || []).map(d => {
|
|
451
|
+
// Handle various ID formats: IMPL-001, T1, 1, etc.
|
|
452
|
+
const num = d.match(/\d+/)?.[0];
|
|
453
|
+
return num ? `T${parseInt(num)}` : d;
|
|
454
|
+
})
|
|
455
|
+
}));
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Phase 4: Resolve Issue (Create or Find)
|
|
460
|
+
|
|
461
|
+
```javascript
|
|
462
|
+
let issueId = flags.issue;
|
|
463
|
+
let existingSolution = null;
|
|
464
|
+
|
|
465
|
+
if (issueId) {
|
|
466
|
+
// Validate issue exists
|
|
467
|
+
let issueCheck;
|
|
468
|
+
try {
|
|
469
|
+
issueCheck = Bash(`ccw issue status ${issueId} --json 2>/dev/null`).trim();
|
|
470
|
+
if (!issueCheck || issueCheck === '') {
|
|
471
|
+
throw new Error('empty response');
|
|
472
|
+
}
|
|
473
|
+
} catch (e) {
|
|
474
|
+
throw new Error(`E003: Issue not found: ${issueId}`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const issue = JSON.parse(issueCheck);
|
|
478
|
+
|
|
479
|
+
// Check if issue already has bound solution
|
|
480
|
+
if (issue.bound_solution_id && !flags.supplement) {
|
|
481
|
+
throw new Error(`E004: Issue ${issueId} already has bound solution (${issue.bound_solution_id}). Use --supplement to add tasks.`);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Load existing solution for supplement mode
|
|
485
|
+
if (flags.supplement && issue.bound_solution_id) {
|
|
486
|
+
try {
|
|
487
|
+
const solResult = Bash(`ccw issue solution ${issue.bound_solution_id} --json`).trim();
|
|
488
|
+
existingSolution = JSON.parse(solResult);
|
|
489
|
+
console.log(`Loaded existing solution with ${existingSolution.tasks.length} tasks`);
|
|
490
|
+
} catch (e) {
|
|
491
|
+
throw new Error(`Failed to load existing solution: ${e.message}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
} else {
|
|
495
|
+
// Create new issue via ccw issue create (auto-generates correct ID)
|
|
496
|
+
// Smart extraction: title from content, priority from complexity
|
|
497
|
+
const title = extracted.title || 'Converted Plan';
|
|
498
|
+
const context = extracted.description || extracted.approach || title;
|
|
499
|
+
|
|
500
|
+
// Auto-determine priority based on complexity
|
|
501
|
+
const complexityMap = { high: 2, medium: 3, low: 4 };
|
|
502
|
+
const priority = complexityMap[extracted.metadata.complexity?.toLowerCase()] || 3;
|
|
503
|
+
|
|
504
|
+
try {
|
|
505
|
+
// Use heredoc to avoid shell escaping issues
|
|
506
|
+
const createResult = Bash(`ccw issue create << 'EOF'
|
|
507
|
+
{
|
|
508
|
+
"title": ${JSON.stringify(title)},
|
|
509
|
+
"context": ${JSON.stringify(context)},
|
|
510
|
+
"priority": ${priority},
|
|
511
|
+
"source": "converted"
|
|
512
|
+
}
|
|
513
|
+
EOF`).trim();
|
|
514
|
+
|
|
515
|
+
// Parse result to get created issue ID
|
|
516
|
+
const created = JSON.parse(createResult);
|
|
517
|
+
issueId = created.id;
|
|
518
|
+
console.log(`Created issue: ${issueId} (priority: ${priority})`);
|
|
519
|
+
} catch (e) {
|
|
520
|
+
throw new Error(`Failed to create issue: ${e.message}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Phase 5: Generate Solution
|
|
526
|
+
|
|
527
|
+
```javascript
|
|
528
|
+
// Generate solution ID
|
|
529
|
+
function generateSolutionId(issueId) {
|
|
530
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
531
|
+
let uid = '';
|
|
532
|
+
for (let i = 0; i < 4; i++) {
|
|
533
|
+
uid += chars[Math.floor(Math.random() * chars.length)];
|
|
534
|
+
}
|
|
535
|
+
return `SOL-${issueId}-${uid}`;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
let solution;
|
|
539
|
+
const solutionId = generateSolutionId(issueId);
|
|
540
|
+
|
|
541
|
+
if (flags.supplement && existingSolution) {
|
|
542
|
+
// Supplement mode: merge with existing solution
|
|
543
|
+
const maxTaskId = Math.max(...existingSolution.tasks.map(t => parseInt(t.id.slice(1))));
|
|
544
|
+
|
|
545
|
+
const newTasks = extracted.tasks.map((t, i) => ({
|
|
546
|
+
...t,
|
|
547
|
+
id: `T${maxTaskId + i + 1}`
|
|
548
|
+
}));
|
|
549
|
+
|
|
550
|
+
solution = {
|
|
551
|
+
...existingSolution,
|
|
552
|
+
tasks: [...existingSolution.tasks, ...newTasks],
|
|
553
|
+
approach: existingSolution.approach + '\n\n[Supplementary] ' + (extracted.approach || ''),
|
|
554
|
+
updated_at: new Date().toISOString()
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
console.log(`Supplementing: ${existingSolution.tasks.length} existing + ${newTasks.length} new = ${solution.tasks.length} total tasks`);
|
|
558
|
+
} else {
|
|
559
|
+
// New solution
|
|
560
|
+
solution = {
|
|
561
|
+
id: solutionId,
|
|
562
|
+
description: extracted.description || extracted.title,
|
|
563
|
+
approach: extracted.approach,
|
|
564
|
+
tasks: extracted.tasks,
|
|
565
|
+
exploration_context: extracted.metadata.exploration_angles ? {
|
|
566
|
+
exploration_angles: extracted.metadata.exploration_angles
|
|
567
|
+
} : undefined,
|
|
568
|
+
analysis: {
|
|
569
|
+
risk: 'medium',
|
|
570
|
+
impact: 'medium',
|
|
571
|
+
complexity: extracted.metadata.complexity?.toLowerCase() || 'medium'
|
|
572
|
+
},
|
|
573
|
+
is_bound: false,
|
|
574
|
+
created_at: new Date().toISOString(),
|
|
575
|
+
_conversion_metadata: {
|
|
576
|
+
source_type: extracted.metadata.source_type,
|
|
577
|
+
source_path: extracted.metadata.source_path,
|
|
578
|
+
converted_at: new Date().toISOString()
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Phase 6: Confirm & Persist
|
|
585
|
+
|
|
586
|
+
```javascript
|
|
587
|
+
// Display preview
|
|
588
|
+
console.log(`
|
|
589
|
+
## Conversion Summary
|
|
590
|
+
|
|
591
|
+
**Issue**: ${issueId}
|
|
592
|
+
**Solution**: ${flags.supplement ? existingSolution.id : solutionId}
|
|
593
|
+
**Tasks**: ${solution.tasks.length}
|
|
594
|
+
**Mode**: ${flags.supplement ? 'Supplement' : 'New'}
|
|
595
|
+
|
|
596
|
+
### Tasks:
|
|
597
|
+
${solution.tasks.map(t => `- ${t.id}: ${t.title} [${t.action}]`).join('\n')}
|
|
598
|
+
`);
|
|
599
|
+
|
|
600
|
+
// Confirm if not auto mode
|
|
601
|
+
if (!flags.yes && !flags.y) {
|
|
602
|
+
const confirm = AskUserQuestion({
|
|
603
|
+
questions: [{
|
|
604
|
+
question: `Create solution for issue ${issueId} with ${solution.tasks.length} tasks?`,
|
|
605
|
+
header: 'Confirm',
|
|
606
|
+
multiSelect: false,
|
|
607
|
+
options: [
|
|
608
|
+
{ label: 'Yes, create solution', description: 'Create and bind solution' },
|
|
609
|
+
{ label: 'Cancel', description: 'Abort without changes' }
|
|
610
|
+
]
|
|
611
|
+
}]
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
if (!confirm.answers?.['Confirm']?.includes('Yes')) {
|
|
615
|
+
console.log('Cancelled.');
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Persist solution (following issue-plan-agent pattern)
|
|
621
|
+
Bash(`mkdir -p .workflow/issues/solutions`);
|
|
622
|
+
|
|
623
|
+
const solutionFile = `.workflow/issues/solutions/${issueId}.jsonl`;
|
|
624
|
+
|
|
625
|
+
if (flags.supplement) {
|
|
626
|
+
// Supplement mode: update existing solution line atomically
|
|
627
|
+
try {
|
|
628
|
+
const existingContent = Read(solutionFile);
|
|
629
|
+
const lines = existingContent.trim().split('\n').filter(l => l);
|
|
630
|
+
const updatedLines = lines.map(line => {
|
|
631
|
+
const sol = JSON.parse(line);
|
|
632
|
+
if (sol.id === existingSolution.id) {
|
|
633
|
+
return JSON.stringify(solution);
|
|
634
|
+
}
|
|
635
|
+
return line;
|
|
636
|
+
});
|
|
637
|
+
// Atomic write: write entire content at once
|
|
638
|
+
Write({ file_path: solutionFile, content: updatedLines.join('\n') + '\n' });
|
|
639
|
+
console.log(`✓ Updated solution: ${existingSolution.id}`);
|
|
640
|
+
} catch (e) {
|
|
641
|
+
throw new Error(`Failed to update solution: ${e.message}`);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Note: No need to rebind - solution is already bound to issue
|
|
645
|
+
} else {
|
|
646
|
+
// New solution: append to JSONL file (following issue-plan-agent pattern)
|
|
647
|
+
try {
|
|
648
|
+
const solutionLine = JSON.stringify(solution);
|
|
649
|
+
|
|
650
|
+
// Read existing content, append new line, write atomically
|
|
651
|
+
const existing = Bash(`test -f "${solutionFile}" && cat "${solutionFile}" || echo ""`).trim();
|
|
652
|
+
const newContent = existing ? existing + '\n' + solutionLine + '\n' : solutionLine + '\n';
|
|
653
|
+
Write({ file_path: solutionFile, content: newContent });
|
|
654
|
+
|
|
655
|
+
console.log(`✓ Created solution: ${solutionId}`);
|
|
656
|
+
} catch (e) {
|
|
657
|
+
throw new Error(`Failed to write solution: ${e.message}`);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Bind solution to issue
|
|
661
|
+
try {
|
|
662
|
+
Bash(`ccw issue bind ${issueId} ${solutionId}`);
|
|
663
|
+
console.log(`✓ Bound solution to issue`);
|
|
664
|
+
} catch (e) {
|
|
665
|
+
// Cleanup: remove solution file on bind failure
|
|
666
|
+
try {
|
|
667
|
+
Bash(`rm -f "${solutionFile}"`);
|
|
668
|
+
} catch (cleanupError) {
|
|
669
|
+
// Ignore cleanup errors
|
|
670
|
+
}
|
|
671
|
+
throw new Error(`Failed to bind solution: ${e.message}`);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Update issue status to planned
|
|
675
|
+
try {
|
|
676
|
+
Bash(`ccw issue update ${issueId} --status planned`);
|
|
677
|
+
} catch (e) {
|
|
678
|
+
throw new Error(`Failed to update issue status: ${e.message}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### Phase 7: Summary
|
|
684
|
+
|
|
685
|
+
```javascript
|
|
686
|
+
console.log(`
|
|
687
|
+
## Done
|
|
688
|
+
|
|
689
|
+
**Issue**: ${issueId}
|
|
690
|
+
**Solution**: ${flags.supplement ? existingSolution.id : solutionId}
|
|
691
|
+
**Tasks**: ${solution.tasks.length}
|
|
692
|
+
**Status**: planned
|
|
693
|
+
|
|
694
|
+
### Next Steps:
|
|
695
|
+
- \`/issue:queue\` → Form execution queue
|
|
696
|
+
- \`ccw issue status ${issueId}\` → View issue details
|
|
697
|
+
- \`ccw issue solution ${flags.supplement ? existingSolution.id : solutionId}\` → View solution
|
|
698
|
+
`);
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
## Error Handling
|
|
702
|
+
|
|
703
|
+
| Error | Code | Resolution |
|
|
704
|
+
|-------|------|------------|
|
|
705
|
+
| Source not found | E001 | Check path exists |
|
|
706
|
+
| Invalid source format | E002 | Verify file contains valid plan structure |
|
|
707
|
+
| Issue not found | E003 | Check issue ID or omit --issue to create new |
|
|
708
|
+
| Solution already bound | E004 | Use --supplement to add tasks |
|
|
709
|
+
| AI extraction failed | E005 | Check markdown structure, try simpler format |
|
|
710
|
+
| No tasks extracted | E006 | Source must contain at least 1 task |
|
|
711
|
+
|
|
712
|
+
## Related Commands
|
|
713
|
+
|
|
714
|
+
- `/issue:plan` - Generate solutions from issue exploration
|
|
715
|
+
- `/issue:queue` - Form execution queue from bound solutions
|
|
716
|
+
- `/issue:execute` - Execute queue with DAG parallelism
|
|
717
|
+
- `ccw issue status <id>` - View issue details
|
|
718
|
+
- `ccw issue solution <id>` - View solution details
|