claude-code-workflow 6.3.37 → 6.3.39
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/commands/workflow/lite-execute.md +2 -0
- package/.codex/agents/action-planning-agent.md +885 -0
- package/.codex/agents/ccw-loop-b-complete.md +227 -0
- package/.codex/agents/ccw-loop-b-debug.md +172 -0
- package/.codex/agents/ccw-loop-b-develop.md +147 -0
- package/.codex/agents/ccw-loop-b-init.md +82 -0
- package/.codex/agents/ccw-loop-b-validate.md +204 -0
- package/.codex/agents/ccw-loop-executor.md +260 -0
- package/.codex/agents/cli-discuss-agent.md +391 -0
- package/.codex/agents/cli-execution-agent.md +333 -0
- package/.codex/agents/cli-explore-agent.md +186 -0
- package/.codex/agents/cli-lite-planning-agent.md +736 -0
- package/.codex/agents/cli-planning-agent.md +562 -0
- package/.codex/agents/code-developer.md +408 -0
- package/.codex/agents/conceptual-planning-agent.md +321 -0
- package/.codex/agents/context-search-agent.md +585 -0
- package/.codex/agents/debug-explore-agent.md +436 -0
- package/.codex/agents/doc-generator.md +334 -0
- package/.codex/agents/issue-plan-agent.md +417 -0
- package/.codex/agents/issue-queue-agent.md +311 -0
- package/.codex/agents/memory-bridge.md +96 -0
- package/.codex/agents/test-context-search-agent.md +402 -0
- package/.codex/agents/test-fix-agent.md +359 -0
- package/.codex/agents/ui-design-agent.md +595 -0
- package/.codex/agents/universal-executor.md +135 -0
- package/.codex/prompts/clean.md +409 -0
- package/.codex/prompts/issue-discover-by-prompt.md +364 -0
- package/.codex/prompts/issue-discover.md +261 -0
- package/.codex/prompts/issue-execute.md +10 -0
- package/.codex/prompts/issue-new.md +285 -0
- package/.codex/prompts/issue-plan.md +161 -63
- package/.codex/prompts/issue-queue.md +298 -288
- package/.codex/prompts/lite-execute.md +627 -133
- package/.codex/prompts/lite-fix.md +670 -0
- package/.codex/prompts/lite-plan-a.md +337 -0
- package/.codex/prompts/lite-plan-b.md +485 -0
- package/.codex/prompts/{lite-plan.md → lite-plan-c.md} +601 -469
- package/.codex/skills/ccw-loop/README.md +171 -0
- package/.codex/skills/ccw-loop/SKILL.md +349 -0
- package/.codex/skills/ccw-loop/phases/actions/action-complete.md +269 -0
- package/.codex/skills/ccw-loop/phases/actions/action-debug.md +286 -0
- package/.codex/skills/ccw-loop/phases/actions/action-develop.md +183 -0
- package/.codex/skills/ccw-loop/phases/actions/action-init.md +164 -0
- package/.codex/skills/ccw-loop/phases/actions/action-menu.md +205 -0
- package/.codex/skills/ccw-loop/phases/actions/action-validate.md +250 -0
- package/.codex/skills/ccw-loop/phases/orchestrator.md +416 -0
- package/.codex/skills/ccw-loop/phases/state-schema.md +388 -0
- package/.codex/skills/ccw-loop/specs/action-catalog.md +182 -0
- package/.codex/skills/ccw-loop-b/README.md +301 -0
- package/.codex/skills/ccw-loop-b/SKILL.md +322 -0
- package/.codex/skills/ccw-loop-b/phases/orchestrator.md +257 -0
- package/.codex/skills/ccw-loop-b/phases/state-schema.md +181 -0
- package/.codex/skills/ccw-loop-b/specs/action-catalog.md +383 -0
- package/.codex/skills/parallel-dev-cycle/README.md +382 -0
- package/.codex/skills/parallel-dev-cycle/SKILL.md +512 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/code-developer.md +242 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/exploration-planner.md +285 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/requirements-analyst.md +285 -0
- package/.codex/skills/parallel-dev-cycle/phases/agents/validation-archivist.md +381 -0
- package/.codex/skills/parallel-dev-cycle/phases/orchestrator.md +696 -0
- package/.codex/skills/parallel-dev-cycle/phases/state-schema.md +436 -0
- package/.codex/skills/parallel-dev-cycle/specs/communication-optimization.md +423 -0
- package/.codex/skills/parallel-dev-cycle/specs/coordination-protocol.md +391 -0
- package/.codex/skills/parallel-dev-cycle/specs/versioning-strategy.md +330 -0
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +4 -0
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/install.d.ts.map +1 -1
- package/ccw/dist/commands/install.js +39 -8
- package/ccw/dist/commands/install.js.map +1 -1
- package/ccw/dist/commands/issue.d.ts +3 -0
- package/ccw/dist/commands/issue.d.ts.map +1 -1
- package/ccw/dist/commands/issue.js +107 -0
- package/ccw/dist/commands/issue.js.map +1 -1
- package/ccw/dist/commands/upgrade.js +1 -1
- package/ccw/dist/commands/upgrade.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 +3 -2
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -1
- package/ccw/dist/core/memory-embedder-bridge.d.ts.map +1 -1
- package/ccw/dist/core/memory-embedder-bridge.js +2 -5
- package/ccw/dist/core/memory-embedder-bridge.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/codexlens/config-handlers.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens/config-handlers.js +7 -6
- package/ccw/dist/core/routes/codexlens/config-handlers.js.map +1 -1
- package/ccw/dist/core/routes/codexlens/semantic-handlers.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens/semantic-handlers.js +2 -2
- package/ccw/dist/core/routes/codexlens/semantic-handlers.js.map +1 -1
- package/ccw/dist/core/routes/graph-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/graph-routes.js +17 -2
- package/ccw/dist/core/routes/graph-routes.js.map +1 -1
- package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/issue-routes.js +280 -33
- package/ccw/dist/core/routes/issue-routes.js.map +1 -1
- package/ccw/dist/core/routes/loop-v2-routes.d.ts +9 -0
- package/ccw/dist/core/routes/loop-v2-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/loop-v2-routes.js +56 -4
- package/ccw/dist/core/routes/loop-v2-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 +3 -2
- 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 +5 -3
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +4 -3
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/cli-config-manager.d.ts +1 -0
- package/ccw/dist/tools/cli-config-manager.d.ts.map +1 -1
- package/ccw/dist/tools/cli-config-manager.js +2 -1
- package/ccw/dist/tools/cli-config-manager.js.map +1 -1
- package/ccw/dist/tools/codex-lens-lsp.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens-lsp.js +2 -5
- package/ccw/dist/tools/codex-lens-lsp.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +22 -32
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts +6 -0
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -1
- package/ccw/dist/tools/litellm-client.js +15 -2
- package/ccw/dist/tools/litellm-client.js.map +1 -1
- package/ccw/dist/tools/loop-task-manager.d.ts +13 -2
- package/ccw/dist/tools/loop-task-manager.d.ts.map +1 -1
- package/ccw/dist/tools/loop-task-manager.js.map +1 -1
- package/ccw/dist/tools/native-session-discovery.d.ts.map +1 -1
- package/ccw/dist/tools/native-session-discovery.js +35 -7
- package/ccw/dist/tools/native-session-discovery.js.map +1 -1
- package/ccw/dist/utils/codexlens-path.d.ts +36 -0
- package/ccw/dist/utils/codexlens-path.d.ts.map +1 -0
- package/ccw/dist/utils/codexlens-path.js +56 -0
- package/ccw/dist/utils/codexlens-path.js.map +1 -0
- package/ccw/dist/utils/uv-manager.d.ts.map +1 -1
- package/ccw/dist/utils/uv-manager.js +3 -2
- package/ccw/dist/utils/uv-manager.js.map +1 -1
- package/ccw/src/cli.ts +4 -0
- package/ccw/src/commands/install.ts +51 -8
- package/ccw/src/commands/issue.ts +119 -0
- package/ccw/src/commands/upgrade.ts +1 -1
- package/ccw/src/config/litellm-api-config-manager.ts +3 -2
- package/ccw/src/core/memory-embedder-bridge.ts +2 -6
- package/ccw/src/core/routes/cli-routes.ts +1 -1
- package/ccw/src/core/routes/codexlens/config-handlers.ts +7 -6
- package/ccw/src/core/routes/codexlens/semantic-handlers.ts +2 -2
- package/ccw/src/core/routes/graph-routes.ts +18 -2
- package/ccw/src/core/routes/issue-routes.ts +308 -33
- package/ccw/src/core/routes/loop-v2-routes.ts +64 -6
- package/ccw/src/core/routes/system-routes.ts +3 -2
- package/ccw/src/core/server.ts +6 -3
- package/ccw/src/templates/dashboard-css/02-session.css +2 -0
- package/ccw/src/templates/dashboard-css/04-lite-tasks.css +103 -1
- package/ccw/src/templates/dashboard-css/32-issue-manager.css +32 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +48 -48
- package/ccw/src/templates/dashboard-js/components/navigation.js +6 -0
- package/ccw/src/templates/dashboard-js/components/notifications.js +6 -0
- package/ccw/src/templates/dashboard-js/components/version-check.js +38 -0
- package/ccw/src/templates/dashboard-js/i18n.js +126 -0
- package/ccw/src/templates/dashboard-js/state.js +2 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +1 -1
- package/ccw/src/templates/dashboard-js/views/issue-manager.js +183 -1
- package/ccw/src/templates/dashboard-js/views/lite-tasks.js +55 -11
- package/ccw/src/templates/dashboard-js/views/loop-monitor.js +112 -11
- package/ccw/src/templates/dashboard.html +48 -2
- package/ccw/src/tools/claude-cli-tools.ts +4 -3
- package/ccw/src/tools/cli-config-manager.ts +3 -1
- package/ccw/src/tools/codex-lens-lsp.ts +2 -5
- package/ccw/src/tools/codex-lens.ts +27 -38
- package/ccw/src/tools/litellm-client.ts +16 -2
- package/ccw/src/tools/loop-task-manager.ts +13 -2
- package/ccw/src/tools/native-session-discovery.ts +38 -7
- package/ccw/src/utils/codexlens-path.ts +60 -0
- package/ccw/src/utils/uv-manager.ts +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Action: VALIDATE
|
|
2
|
+
|
|
3
|
+
Run tests and verify implementation, record results to validate.md.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
- Run unit tests
|
|
8
|
+
- Run integration tests
|
|
9
|
+
- Check code coverage
|
|
10
|
+
- Generate validation report
|
|
11
|
+
- Determine pass/fail status
|
|
12
|
+
|
|
13
|
+
## Preconditions
|
|
14
|
+
|
|
15
|
+
- [ ] state.status === 'running'
|
|
16
|
+
- [ ] state.skill_state !== null
|
|
17
|
+
- [ ] (develop.completed > 0) OR (debug.confirmed_hypothesis !== null)
|
|
18
|
+
|
|
19
|
+
## Execution Steps
|
|
20
|
+
|
|
21
|
+
### Step 1: Verify Control Signals
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
|
|
25
|
+
|
|
26
|
+
if (state.status !== 'running') {
|
|
27
|
+
return {
|
|
28
|
+
action: 'VALIDATE',
|
|
29
|
+
status: 'failed',
|
|
30
|
+
message: `Cannot validate: status is ${state.status}`,
|
|
31
|
+
next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Step 2: Detect Test Framework
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const packageJson = JSON.parse(Read('package.json') || '{}')
|
|
40
|
+
const testScript = packageJson.scripts?.test || 'npm test'
|
|
41
|
+
const coverageScript = packageJson.scripts?.['test:coverage']
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Step 3: Run Tests
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
const testResult = await Bash({
|
|
48
|
+
command: testScript,
|
|
49
|
+
timeout: 300000 // 5 minutes
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Parse test output based on framework
|
|
53
|
+
const testResults = parseTestOutput(testResult.stdout, testResult.stderr)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 4: Run Coverage (if available)
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
let coverageData = null
|
|
60
|
+
|
|
61
|
+
if (coverageScript) {
|
|
62
|
+
const coverageResult = await Bash({
|
|
63
|
+
command: coverageScript,
|
|
64
|
+
timeout: 300000
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
coverageData = parseCoverageReport(coverageResult.stdout)
|
|
68
|
+
Write(`${progressDir}/coverage.json`, JSON.stringify(coverageData, null, 2))
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Step 5: Generate Validation Report
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
const timestamp = getUtc8ISOString()
|
|
76
|
+
const iteration = (state.skill_state.validate.test_results?.length || 0) + 1
|
|
77
|
+
|
|
78
|
+
const validationReport = `# Validation Report
|
|
79
|
+
|
|
80
|
+
**Loop ID**: ${loopId}
|
|
81
|
+
**Task**: ${state.description}
|
|
82
|
+
**Validated**: ${timestamp}
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Iteration ${iteration} - Validation Run
|
|
87
|
+
|
|
88
|
+
### Test Execution Summary
|
|
89
|
+
|
|
90
|
+
| Metric | Value |
|
|
91
|
+
|--------|-------|
|
|
92
|
+
| Total Tests | ${testResults.total} |
|
|
93
|
+
| Passed | ${testResults.passed} |
|
|
94
|
+
| Failed | ${testResults.failed} |
|
|
95
|
+
| Skipped | ${testResults.skipped} |
|
|
96
|
+
| Duration | ${testResults.duration_ms}ms |
|
|
97
|
+
| **Pass Rate** | **${((testResults.passed / testResults.total) * 100).toFixed(1)}%** |
|
|
98
|
+
|
|
99
|
+
### Coverage Report
|
|
100
|
+
|
|
101
|
+
${coverageData ? `
|
|
102
|
+
| File | Statements | Branches | Functions | Lines |
|
|
103
|
+
|------|------------|----------|-----------|-------|
|
|
104
|
+
${coverageData.files.map(f => `| ${f.path} | ${f.statements}% | ${f.branches}% | ${f.functions}% | ${f.lines}% |`).join('\n')}
|
|
105
|
+
|
|
106
|
+
**Overall Coverage**: ${coverageData.overall.statements}%
|
|
107
|
+
` : '_No coverage data available_'}
|
|
108
|
+
|
|
109
|
+
### Failed Tests
|
|
110
|
+
|
|
111
|
+
${testResults.failed > 0 ? testResults.failures.map(f => `
|
|
112
|
+
#### ${f.test_name}
|
|
113
|
+
|
|
114
|
+
- **Suite**: ${f.suite}
|
|
115
|
+
- **Error**: ${f.error_message}
|
|
116
|
+
`).join('\n') : '_All tests passed_'}
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Validation Decision
|
|
121
|
+
|
|
122
|
+
**Result**: ${testResults.failed === 0 ? 'PASS' : 'FAIL'}
|
|
123
|
+
|
|
124
|
+
${testResults.failed > 0 ? `
|
|
125
|
+
### Next Actions
|
|
126
|
+
|
|
127
|
+
1. Review failed tests
|
|
128
|
+
2. Debug failures using DEBUG action
|
|
129
|
+
3. Fix issues and re-run validation
|
|
130
|
+
` : `
|
|
131
|
+
### Next Actions
|
|
132
|
+
|
|
133
|
+
1. Consider code review
|
|
134
|
+
2. Complete loop
|
|
135
|
+
`}
|
|
136
|
+
`
|
|
137
|
+
|
|
138
|
+
Write(`${progressDir}/validate.md`, validationReport)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Step 6: Save Test Results
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
const testResultsData = {
|
|
145
|
+
iteration,
|
|
146
|
+
timestamp,
|
|
147
|
+
summary: {
|
|
148
|
+
total: testResults.total,
|
|
149
|
+
passed: testResults.passed,
|
|
150
|
+
failed: testResults.failed,
|
|
151
|
+
skipped: testResults.skipped,
|
|
152
|
+
pass_rate: ((testResults.passed / testResults.total) * 100).toFixed(1),
|
|
153
|
+
duration_ms: testResults.duration_ms
|
|
154
|
+
},
|
|
155
|
+
tests: testResults.tests,
|
|
156
|
+
failures: testResults.failures,
|
|
157
|
+
coverage: coverageData?.overall || null
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Write(`${progressDir}/test-results.json`, JSON.stringify(testResultsData, null, 2))
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Step 7: Update State
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const validationPassed = testResults.failed === 0 && testResults.passed > 0
|
|
167
|
+
|
|
168
|
+
state.skill_state.validate.test_results.push(testResultsData)
|
|
169
|
+
state.skill_state.validate.pass_rate = parseFloat(testResultsData.summary.pass_rate)
|
|
170
|
+
state.skill_state.validate.coverage = coverageData?.overall?.statements || 0
|
|
171
|
+
state.skill_state.validate.passed = validationPassed
|
|
172
|
+
state.skill_state.validate.failed_tests = testResults.failures.map(f => f.test_name)
|
|
173
|
+
state.skill_state.validate.last_run_at = timestamp
|
|
174
|
+
|
|
175
|
+
state.skill_state.last_action = 'VALIDATE'
|
|
176
|
+
state.updated_at = timestamp
|
|
177
|
+
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Output Format
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
ACTION_RESULT:
|
|
184
|
+
- action: VALIDATE
|
|
185
|
+
- status: success
|
|
186
|
+
- message: Validation {PASSED | FAILED} - {pass_count}/{total_count} tests passed
|
|
187
|
+
- state_updates: {
|
|
188
|
+
"validate.passed": {true | false},
|
|
189
|
+
"validate.pass_rate": {N},
|
|
190
|
+
"validate.failed_tests": [{list}]
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
FILES_UPDATED:
|
|
194
|
+
- .workflow/.loop/{loopId}.progress/validate.md: Validation report created
|
|
195
|
+
- .workflow/.loop/{loopId}.progress/test-results.json: Test results saved
|
|
196
|
+
- .workflow/.loop/{loopId}.progress/coverage.json: Coverage data saved (if available)
|
|
197
|
+
|
|
198
|
+
NEXT_ACTION_NEEDED: {COMPLETE | DEBUG | DEVELOP | MENU}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Next Action Selection
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
if (validationPassed) {
|
|
205
|
+
const pendingTasks = state.skill_state.develop.tasks.filter(t => t.status === 'pending')
|
|
206
|
+
if (pendingTasks.length === 0) {
|
|
207
|
+
return 'COMPLETE'
|
|
208
|
+
} else {
|
|
209
|
+
return 'DEVELOP'
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
// Tests failed - need debugging
|
|
213
|
+
return 'DEBUG'
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Test Output Parsers
|
|
218
|
+
|
|
219
|
+
### Jest/Vitest Parser
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
function parseJestOutput(stdout) {
|
|
223
|
+
const summaryMatch = stdout.match(/Tests:\s+(\d+)\s+passed.*?(\d+)\s+failed.*?(\d+)\s+total/)
|
|
224
|
+
// ... implementation
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Pytest Parser
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
function parsePytestOutput(stdout) {
|
|
232
|
+
const summaryMatch = stdout.match(/(\d+)\s+passed.*?(\d+)\s+failed/)
|
|
233
|
+
// ... implementation
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Error Handling
|
|
238
|
+
|
|
239
|
+
| Error Type | Recovery |
|
|
240
|
+
|------------|----------|
|
|
241
|
+
| Tests don't run | Check test script config, report error |
|
|
242
|
+
| All tests fail | Suggest DEBUG action |
|
|
243
|
+
| Coverage tool missing | Skip coverage, run tests only |
|
|
244
|
+
| Timeout | Increase timeout or split tests |
|
|
245
|
+
|
|
246
|
+
## Next Actions
|
|
247
|
+
|
|
248
|
+
- Validation passed, no pending: `COMPLETE`
|
|
249
|
+
- Validation passed, has pending: `DEVELOP`
|
|
250
|
+
- Validation failed: `DEBUG`
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# Orchestrator (Codex Pattern)
|
|
2
|
+
|
|
3
|
+
Orchestrate CCW Loop using Codex subagent pattern: `spawn_agent -> wait -> send_input -> close_agent`.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Check control signals -> Read file state -> Select action -> Execute via agent -> Update files -> Loop until complete or paused/stopped.
|
|
8
|
+
|
|
9
|
+
## Codex Pattern Overview
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
+-- spawn_agent (ccw-loop-executor role) --+
|
|
13
|
+
| |
|
|
14
|
+
| Phase 1: INIT or first action |
|
|
15
|
+
| | |
|
|
16
|
+
| v |
|
|
17
|
+
| wait() -> get result |
|
|
18
|
+
| | |
|
|
19
|
+
| v |
|
|
20
|
+
| [If needs input] Collect user input |
|
|
21
|
+
| | |
|
|
22
|
+
| v |
|
|
23
|
+
| send_input(user choice + next action) |
|
|
24
|
+
| | |
|
|
25
|
+
| v |
|
|
26
|
+
| wait() -> get result |
|
|
27
|
+
| | |
|
|
28
|
+
| v |
|
|
29
|
+
| [Loop until COMPLETED/PAUSED/STOPPED] |
|
|
30
|
+
| | |
|
|
31
|
+
+----------v-------------------------------+
|
|
32
|
+
|
|
|
33
|
+
close_agent()
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## State Management (Unified Location)
|
|
37
|
+
|
|
38
|
+
### Read State
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Read loop state (unified location)
|
|
45
|
+
* @param loopId - Loop ID (e.g., "loop-v2-20260122-abc123")
|
|
46
|
+
*/
|
|
47
|
+
function readLoopState(loopId) {
|
|
48
|
+
const stateFile = `.workflow/.loop/${loopId}.json`
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(stateFile)) {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const state = JSON.parse(Read(stateFile))
|
|
55
|
+
return state
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Create New Loop State (Direct Call)
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
/**
|
|
63
|
+
* Create new loop state (only for direct calls, API triggers have existing state)
|
|
64
|
+
*/
|
|
65
|
+
function createLoopState(loopId, taskDescription) {
|
|
66
|
+
const stateFile = `.workflow/.loop/${loopId}.json`
|
|
67
|
+
const now = getUtc8ISOString()
|
|
68
|
+
|
|
69
|
+
const state = {
|
|
70
|
+
// API compatible fields
|
|
71
|
+
loop_id: loopId,
|
|
72
|
+
title: taskDescription.substring(0, 100),
|
|
73
|
+
description: taskDescription,
|
|
74
|
+
max_iterations: 10,
|
|
75
|
+
status: 'running', // Direct call sets to running
|
|
76
|
+
current_iteration: 0,
|
|
77
|
+
created_at: now,
|
|
78
|
+
updated_at: now,
|
|
79
|
+
|
|
80
|
+
// Skill extension fields
|
|
81
|
+
skill_state: null // Initialized by INIT action
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Ensure directories exist
|
|
85
|
+
mkdir -p ".loop"
|
|
86
|
+
mkdir -p ".workflow/.loop/${loopId}.progress"
|
|
87
|
+
|
|
88
|
+
Write(stateFile, JSON.stringify(state, null, 2))
|
|
89
|
+
return state
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Main Execution Flow (Codex Subagent)
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
/**
|
|
97
|
+
* Run CCW Loop orchestrator using Codex subagent pattern
|
|
98
|
+
* @param options.loopId - Existing Loop ID (API trigger)
|
|
99
|
+
* @param options.task - Task description (direct call)
|
|
100
|
+
* @param options.mode - 'interactive' | 'auto'
|
|
101
|
+
*/
|
|
102
|
+
async function runOrchestrator(options = {}) {
|
|
103
|
+
const { loopId: existingLoopId, task, mode = 'interactive' } = options
|
|
104
|
+
|
|
105
|
+
console.log('=== CCW Loop Orchestrator (Codex) Started ===')
|
|
106
|
+
|
|
107
|
+
// 1. Determine loopId and initial state
|
|
108
|
+
let loopId
|
|
109
|
+
let state
|
|
110
|
+
|
|
111
|
+
if (existingLoopId) {
|
|
112
|
+
// API trigger: use existing loopId
|
|
113
|
+
loopId = existingLoopId
|
|
114
|
+
state = readLoopState(loopId)
|
|
115
|
+
|
|
116
|
+
if (!state) {
|
|
117
|
+
console.error(`Loop not found: ${loopId}`)
|
|
118
|
+
return { status: 'error', message: 'Loop not found' }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(`Resuming loop: ${loopId}`)
|
|
122
|
+
console.log(`Status: ${state.status}`)
|
|
123
|
+
|
|
124
|
+
} else if (task) {
|
|
125
|
+
// Direct call: create new loopId
|
|
126
|
+
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
|
|
127
|
+
const random = Math.random().toString(36).substring(2, 10)
|
|
128
|
+
loopId = `loop-v2-${timestamp}-${random}`
|
|
129
|
+
|
|
130
|
+
console.log(`Creating new loop: ${loopId}`)
|
|
131
|
+
console.log(`Task: ${task}`)
|
|
132
|
+
|
|
133
|
+
state = createLoopState(loopId, task)
|
|
134
|
+
|
|
135
|
+
} else {
|
|
136
|
+
console.error('Either --loop-id or task description is required')
|
|
137
|
+
return { status: 'error', message: 'Missing loopId or task' }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const progressDir = `.workflow/.loop/${loopId}.progress`
|
|
141
|
+
|
|
142
|
+
// 2. Create executor agent (single agent for entire loop)
|
|
143
|
+
const agent = spawn_agent({
|
|
144
|
+
message: `
|
|
145
|
+
## TASK ASSIGNMENT
|
|
146
|
+
|
|
147
|
+
### MANDATORY FIRST STEPS (Agent Execute)
|
|
148
|
+
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md (MUST read first)
|
|
149
|
+
2. Read: .workflow/project-tech.json (if exists)
|
|
150
|
+
3. Read: .workflow/project-guidelines.json (if exists)
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## LOOP CONTEXT
|
|
155
|
+
|
|
156
|
+
- **Loop ID**: ${loopId}
|
|
157
|
+
- **State File**: .workflow/.loop/${loopId}.json
|
|
158
|
+
- **Progress Dir**: ${progressDir}
|
|
159
|
+
- **Mode**: ${mode}
|
|
160
|
+
|
|
161
|
+
## CURRENT STATE
|
|
162
|
+
|
|
163
|
+
${JSON.stringify(state, null, 2)}
|
|
164
|
+
|
|
165
|
+
## TASK DESCRIPTION
|
|
166
|
+
|
|
167
|
+
${state.description || task}
|
|
168
|
+
|
|
169
|
+
## FIRST ACTION
|
|
170
|
+
|
|
171
|
+
${!state.skill_state ? 'Execute: INIT' : mode === 'auto' ? 'Auto-select next action' : 'Show MENU'}
|
|
172
|
+
|
|
173
|
+
Read the role definition first, then execute the appropriate action.
|
|
174
|
+
`
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
// 3. Main orchestration loop
|
|
178
|
+
let iteration = state.current_iteration || 0
|
|
179
|
+
const maxIterations = state.max_iterations || 10
|
|
180
|
+
let continueLoop = true
|
|
181
|
+
|
|
182
|
+
while (continueLoop && iteration < maxIterations) {
|
|
183
|
+
iteration++
|
|
184
|
+
|
|
185
|
+
// Wait for agent output
|
|
186
|
+
const result = wait({ ids: [agent], timeout_ms: 600000 })
|
|
187
|
+
|
|
188
|
+
// Check for timeout
|
|
189
|
+
if (result.timed_out) {
|
|
190
|
+
console.log('Agent timeout, requesting convergence...')
|
|
191
|
+
send_input({
|
|
192
|
+
id: agent,
|
|
193
|
+
message: `
|
|
194
|
+
## TIMEOUT NOTIFICATION
|
|
195
|
+
|
|
196
|
+
Execution timeout reached. Please:
|
|
197
|
+
1. Output current progress
|
|
198
|
+
2. Save any pending state updates
|
|
199
|
+
3. Return ACTION_RESULT with current status
|
|
200
|
+
`
|
|
201
|
+
})
|
|
202
|
+
continue
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const output = result.status[agent].completed
|
|
206
|
+
|
|
207
|
+
// Parse action result
|
|
208
|
+
const actionResult = parseActionResult(output)
|
|
209
|
+
|
|
210
|
+
console.log(`\n[Iteration ${iteration}] Action: ${actionResult.action}, Status: ${actionResult.status}`)
|
|
211
|
+
|
|
212
|
+
// Update iteration in state
|
|
213
|
+
state = readLoopState(loopId)
|
|
214
|
+
state.current_iteration = iteration
|
|
215
|
+
state.updated_at = getUtc8ISOString()
|
|
216
|
+
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
|
|
217
|
+
|
|
218
|
+
// Handle different outcomes
|
|
219
|
+
switch (actionResult.next_action) {
|
|
220
|
+
case 'COMPLETED':
|
|
221
|
+
console.log('Loop completed successfully')
|
|
222
|
+
continueLoop = false
|
|
223
|
+
break
|
|
224
|
+
|
|
225
|
+
case 'PAUSED':
|
|
226
|
+
console.log('Loop paused by API, exiting gracefully')
|
|
227
|
+
continueLoop = false
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
case 'STOPPED':
|
|
231
|
+
console.log('Loop stopped by API')
|
|
232
|
+
continueLoop = false
|
|
233
|
+
break
|
|
234
|
+
|
|
235
|
+
case 'WAITING_INPUT':
|
|
236
|
+
// Interactive mode: display menu, get user choice
|
|
237
|
+
if (mode === 'interactive') {
|
|
238
|
+
const userChoice = await displayMenuAndGetChoice(actionResult)
|
|
239
|
+
|
|
240
|
+
// Send user choice back to agent
|
|
241
|
+
send_input({
|
|
242
|
+
id: agent,
|
|
243
|
+
message: `
|
|
244
|
+
## USER INPUT RECEIVED
|
|
245
|
+
|
|
246
|
+
Action selected: ${userChoice.action}
|
|
247
|
+
${userChoice.data ? `Additional data: ${JSON.stringify(userChoice.data)}` : ''}
|
|
248
|
+
|
|
249
|
+
## EXECUTE SELECTED ACTION
|
|
250
|
+
|
|
251
|
+
Read action instructions and execute: ${userChoice.action}
|
|
252
|
+
Update state and progress files accordingly.
|
|
253
|
+
Output ACTION_RESULT when complete.
|
|
254
|
+
`
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
break
|
|
258
|
+
|
|
259
|
+
default:
|
|
260
|
+
// Continue with next action
|
|
261
|
+
if (actionResult.next_action && actionResult.next_action !== 'NONE') {
|
|
262
|
+
send_input({
|
|
263
|
+
id: agent,
|
|
264
|
+
message: `
|
|
265
|
+
## CONTINUE EXECUTION
|
|
266
|
+
|
|
267
|
+
Previous action completed: ${actionResult.action}
|
|
268
|
+
Result: ${actionResult.status}
|
|
269
|
+
${actionResult.message ? `Message: ${actionResult.message}` : ''}
|
|
270
|
+
|
|
271
|
+
## EXECUTE NEXT ACTION
|
|
272
|
+
|
|
273
|
+
Continue with: ${actionResult.next_action}
|
|
274
|
+
Read action instructions and execute.
|
|
275
|
+
Output ACTION_RESULT when complete.
|
|
276
|
+
`
|
|
277
|
+
})
|
|
278
|
+
} else {
|
|
279
|
+
// No next action specified, check if should continue
|
|
280
|
+
if (actionResult.status === 'failed') {
|
|
281
|
+
console.log(`Action failed: ${actionResult.message}`)
|
|
282
|
+
}
|
|
283
|
+
continueLoop = false
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 4. Check iteration limit
|
|
289
|
+
if (iteration >= maxIterations) {
|
|
290
|
+
console.log(`\nReached maximum iterations (${maxIterations})`)
|
|
291
|
+
console.log('Consider breaking down the task or taking a break.')
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 5. Cleanup
|
|
295
|
+
close_agent({ id: agent })
|
|
296
|
+
|
|
297
|
+
console.log('\n=== CCW Loop Orchestrator (Codex) Finished ===')
|
|
298
|
+
|
|
299
|
+
// Return final state
|
|
300
|
+
const finalState = readLoopState(loopId)
|
|
301
|
+
return {
|
|
302
|
+
status: finalState.status,
|
|
303
|
+
loop_id: loopId,
|
|
304
|
+
iterations: iteration,
|
|
305
|
+
final_state: finalState
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Parse action result from agent output
|
|
311
|
+
*/
|
|
312
|
+
function parseActionResult(output) {
|
|
313
|
+
const result = {
|
|
314
|
+
action: 'unknown',
|
|
315
|
+
status: 'unknown',
|
|
316
|
+
message: '',
|
|
317
|
+
state_updates: {},
|
|
318
|
+
next_action: 'NONE'
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Parse ACTION_RESULT block
|
|
322
|
+
const actionMatch = output.match(/ACTION_RESULT:\s*([\s\S]*?)(?:FILES_UPDATED:|NEXT_ACTION_NEEDED:|$)/)
|
|
323
|
+
if (actionMatch) {
|
|
324
|
+
const lines = actionMatch[1].split('\n')
|
|
325
|
+
for (const line of lines) {
|
|
326
|
+
const match = line.match(/^-\s*(\w+):\s*(.+)$/)
|
|
327
|
+
if (match) {
|
|
328
|
+
const [, key, value] = match
|
|
329
|
+
if (key === 'state_updates') {
|
|
330
|
+
try {
|
|
331
|
+
result.state_updates = JSON.parse(value)
|
|
332
|
+
} catch (e) {
|
|
333
|
+
// Try parsing multi-line JSON
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
result[key] = value.trim()
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Parse NEXT_ACTION_NEEDED
|
|
343
|
+
const nextMatch = output.match(/NEXT_ACTION_NEEDED:\s*(\S+)/)
|
|
344
|
+
if (nextMatch) {
|
|
345
|
+
result.next_action = nextMatch[1]
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return result
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Display menu and get user choice (interactive mode)
|
|
353
|
+
*/
|
|
354
|
+
async function displayMenuAndGetChoice(actionResult) {
|
|
355
|
+
// Parse MENU_OPTIONS from output
|
|
356
|
+
const menuMatch = actionResult.message.match(/MENU_OPTIONS:\s*([\s\S]*?)(?:WAITING_INPUT:|$)/)
|
|
357
|
+
|
|
358
|
+
if (menuMatch) {
|
|
359
|
+
console.log('\n' + menuMatch[1])
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Use AskUserQuestion to get choice
|
|
363
|
+
const response = await AskUserQuestion({
|
|
364
|
+
questions: [{
|
|
365
|
+
question: "Select next action:",
|
|
366
|
+
header: "Action",
|
|
367
|
+
multiSelect: false,
|
|
368
|
+
options: [
|
|
369
|
+
{ label: "develop", description: "Continue development" },
|
|
370
|
+
{ label: "debug", description: "Start debugging" },
|
|
371
|
+
{ label: "validate", description: "Run validation" },
|
|
372
|
+
{ label: "complete", description: "Complete loop" },
|
|
373
|
+
{ label: "exit", description: "Exit and save" }
|
|
374
|
+
]
|
|
375
|
+
}]
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
return { action: response["Action"] }
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Action Catalog
|
|
383
|
+
|
|
384
|
+
| Action | Purpose | Preconditions | Effects |
|
|
385
|
+
|--------|---------|---------------|---------|
|
|
386
|
+
| INIT | Initialize session | status=running, skill_state=null | skill_state initialized |
|
|
387
|
+
| MENU | Display menu | skill_state != null, mode=interactive | Wait for user input |
|
|
388
|
+
| DEVELOP | Execute dev task | pending tasks > 0 | Update progress.md |
|
|
389
|
+
| DEBUG | Hypothesis debug | needs debugging | Update understanding.md |
|
|
390
|
+
| VALIDATE | Run tests | needs validation | Update validation.md |
|
|
391
|
+
| COMPLETE | Finish loop | all done | status=completed |
|
|
392
|
+
|
|
393
|
+
## Termination Conditions
|
|
394
|
+
|
|
395
|
+
1. **API Paused**: `state.status === 'paused'` (Skill exits, wait for resume)
|
|
396
|
+
2. **API Stopped**: `state.status === 'failed'` (Skill terminates)
|
|
397
|
+
3. **Task Complete**: `NEXT_ACTION_NEEDED === 'COMPLETED'`
|
|
398
|
+
4. **Iteration Limit**: `current_iteration >= max_iterations`
|
|
399
|
+
5. **User Exit**: User selects 'exit' in interactive mode
|
|
400
|
+
|
|
401
|
+
## Error Recovery
|
|
402
|
+
|
|
403
|
+
| Error Type | Recovery Strategy |
|
|
404
|
+
|------------|-------------------|
|
|
405
|
+
| Agent timeout | send_input requesting convergence |
|
|
406
|
+
| Action failed | Log error, continue or prompt user |
|
|
407
|
+
| State corrupted | Rebuild from progress files |
|
|
408
|
+
| Agent closed unexpectedly | Re-spawn with previous output in message |
|
|
409
|
+
|
|
410
|
+
## Codex Best Practices Applied
|
|
411
|
+
|
|
412
|
+
1. **Single Agent Pattern**: One agent handles entire loop lifecycle
|
|
413
|
+
2. **Deep Interaction via send_input**: Multi-phase without context loss
|
|
414
|
+
3. **Delayed close_agent**: Only after confirming no more interaction
|
|
415
|
+
4. **Explicit wait()**: Always get results before proceeding
|
|
416
|
+
5. **Role Path Passing**: Agent reads role file, no content embedding
|