feat: prioritize user config, do not merge default tools

Changed loadClaudeCliTools() to only load tools explicitly defined
in user config. Previously, DEFAULT_TOOLS_CONFIG.tools was spread
before user tools, causing all default tools to be loaded even if
not present in user config.

User config now has complete control over which tools are loaded.
This commit is contained in:
catlog22
2026-01-22 23:37:42 +08:00
parent ac62bf70db
commit 2fb1d1243c
17 changed files with 79 additions and 79 deletions

View File

@@ -41,8 +41,8 @@ PROJECT CONTEXT:
## Execution Steps ## Execution Steps
1. **Read all progress** 1. **Read all progress**
- Load worker outputs from `.loop/{loopId}.workers/` - Load worker outputs from `.workflow/.loop/{loopId}.workers/`
- Read progress files from `.loop/{loopId}.progress/` - Read progress files from `.workflow/.loop/{loopId}.progress/`
- Consolidate findings - Consolidate findings
2. **Verify completeness** 2. **Verify completeness**

View File

@@ -39,7 +39,7 @@ You are a CCW Loop Executor - a stateless iterative development specialist that
```javascript ```javascript
// Read current state // Read current state
const state = JSON.parse(Read('.loop/{loopId}.json')) const state = JSON.parse(Read('.workflow/.loop/{loopId}.json'))
// Check control signals // Check control signals
if (state.status === 'paused') { if (state.status === 'paused') {
@@ -110,7 +110,7 @@ NEXT_ACTION_NEEDED: {action_name} | WAITING_INPUT | COMPLETED | PAUSED
```javascript ```javascript
function updateState(loopId, skillStateUpdates) { function updateState(loopId, skillStateUpdates) {
const state = JSON.parse(Read(`.loop/${loopId}.json`)) const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
state.updated_at = getUtc8ISOString() state.updated_at = getUtc8ISOString()
state.skill_state = { state.skill_state = {
...state.skill_state, ...state.skill_state,
@@ -118,7 +118,7 @@ function updateState(loopId, skillStateUpdates) {
last_action: currentAction, last_action: currentAction,
completed_actions: [...state.skill_state.completed_actions, currentAction] completed_actions: [...state.skill_state.completed_actions, currentAction]
} }
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
} }
``` ```
@@ -136,7 +136,7 @@ function updateState(loopId, skillStateUpdates) {
5. Update state with skill_state 5. Update state with skill_state
**Output**: **Output**:
- `.loop/{loopId}.progress/develop.md` (initialized) - `.workflow/.loop/{loopId}.progress/develop.md` (initialized)
- State: skill_state populated with tasks - State: skill_state populated with tasks
### DEVELOP Action ### DEVELOP Action

View File

@@ -91,7 +91,7 @@ Spawn multiple workers simultaneously, batch wait for results.
## Session Structure ## Session Structure
``` ```
.loop/ .workflow/.loop/
+-- {loopId}.json # Master state +-- {loopId}.json # Master state
+-- {loopId}.workers/ # Worker outputs +-- {loopId}.workers/ # Worker outputs
| +-- init.output.json | +-- init.output.json
@@ -184,7 +184,7 @@ Coordinator adapts to mode:
### 3. State Management ### 3. State Management
Unified state at `.loop/{loopId}.json`: Unified state at `.workflow/.loop/{loopId}.json`:
- **API compatible**: Works with CCW API - **API compatible**: Works with CCW API
- **Extension fields**: Skill-specific data in `skill_state` - **Extension fields**: Skill-specific data in `skill_state`
- **Worker outputs**: Structured JSON for each action - **Worker outputs**: Structured JSON for each action

View File

@@ -61,7 +61,7 @@ Coordinator -> spawn [develop, debug, validate] in parallel -> wait({ ids: all }
## Session Structure ## Session Structure
``` ```
.loop/ .workflow/.loop/
+-- {loopId}.json # Master state +-- {loopId}.json # Master state
+-- {loopId}.workers/ # Worker outputs +-- {loopId}.workers/ # Worker outputs
| +-- develop.output.json | +-- develop.output.json
@@ -242,9 +242,9 @@ function buildWorkerPrompt(action, loopId, state) {
- **Loop ID**: ${loopId} - **Loop ID**: ${loopId}
- **Action**: ${action} - **Action**: ${action}
- **State File**: .loop/${loopId}.json - **State File**: .workflow/.loop/${loopId}.json
- **Output File**: .loop/${loopId}.workers/${action}.output.json - **Output File**: .workflow/.loop/${loopId}.workers/${action}.output.json
- **Progress File**: .loop/${loopId}.progress/${action}.md - **Progress File**: .workflow/.loop/${loopId}.progress/${action}.md
## CURRENT STATE ## CURRENT STATE

View File

@@ -14,7 +14,7 @@ Read state -> Select mode -> Spawn workers -> Wait results -> Merge -> Update st
```javascript ```javascript
function readState(loopId) { function readState(loopId) {
const stateFile = `.loop/${loopId}.json` const stateFile = `.workflow/.loop/${loopId}.json`
return fs.existsSync(stateFile) return fs.existsSync(stateFile)
? JSON.parse(Read(stateFile)) ? JSON.parse(Read(stateFile))
: null : null
@@ -252,6 +252,6 @@ function parseWorkerResult(output) {
## Best Practices ## Best Practices
1. **Worker 生命周期**: spawn → wait → close不保留 worker 1. **Worker 生命周期**: spawn → wait → close不保留 worker
2. **结果持久化**: Worker 输出写入 `.loop/{loopId}.workers/` 2. **结果持久化**: Worker 输出写入 `.workflow/.loop/{loopId}.workers/`
3. **状态同步**: 每次 worker 完成后更新 state 3. **状态同步**: 每次 worker 完成后更新 state
4. **超时处理**: send_input 请求收敛,再超时则跳过 4. **超时处理**: send_input 请求收敛,再超时则跳过

View File

@@ -56,7 +56,7 @@
## Worker Output Structure ## Worker Output Structure
Each worker writes to `.loop/{loopId}.workers/{action}.output.json`: Each worker writes to `.workflow/.loop/{loopId}.workers/{action}.output.json`:
```json ```json
{ {
@@ -81,7 +81,7 @@ Each worker writes to `.loop/{loopId}.workers/{action}.output.json`:
## Progress File Structure ## Progress File Structure
Human-readable progress in `.loop/{loopId}.progress/{action}.md`: Human-readable progress in `.workflow/.loop/{loopId}.progress/{action}.md`:
```markdown ```markdown
# Develop Progress # Develop Progress
@@ -165,7 +165,7 @@ When `mode === 'parallel'`:
## Directory Structure ## Directory Structure
``` ```
.loop/ .workflow/.loop/
+-- loop-b-20260122-abc123.json # Master state +-- loop-b-20260122-abc123.json # Master state
+-- loop-b-20260122-abc123.workers/ +-- loop-b-20260122-abc123.workers/
| +-- init.output.json | +-- init.output.json

View File

@@ -68,7 +68,7 @@ Files are in `.codex/skills/ccw-loop/`:
``` ```
1. Parse arguments (task or --loop-id) 1. Parse arguments (task or --loop-id)
2. Create/read state from .loop/{loopId}.json 2. Create/read state from .workflow/.loop/{loopId}.json
3. spawn_agent with ccw-loop-executor role 3. spawn_agent with ccw-loop-executor role
4. Main loop: 4. Main loop:
a. wait() for agent output a. wait() for agent output
@@ -84,7 +84,7 @@ Files are in `.codex/skills/ccw-loop/`:
## Session Files ## Session Files
``` ```
.loop/ .workflow/.loop/
+-- {loopId}.json # Master state (API + Skill) +-- {loopId}.json # Master state (API + Skill)
+-- {loopId}.progress/ +-- {loopId}.progress/
+-- develop.md # Development timeline +-- develop.md # Development timeline
@@ -157,7 +157,7 @@ spawn_agent({
Works with CCW Dashboard Loop Monitor: Works with CCW Dashboard Loop Monitor:
- Dashboard creates loop via API - Dashboard creates loop via API
- API triggers this skill with `--loop-id` - API triggers this skill with `--loop-id`
- Skill reads/writes `.loop/{loopId}.json` - Skill reads/writes `.workflow/.loop/{loopId}.json`
- Dashboard polls state for real-time updates - Dashboard polls state for real-time updates
### Control Signals ### Control Signals

View File

@@ -27,8 +27,8 @@ Stateless iterative development loop using Codex subagent pattern. Supports deve
+-------------------------------------------------------------+ +-------------------------------------------------------------+
| loop-v2-routes.ts (Control Plane) | | loop-v2-routes.ts (Control Plane) |
| | | |
| State: .loop/{loopId}.json (MASTER) | | State: .workflow/.loop/{loopId}.json (MASTER) |
| Tasks: .loop/{loopId}.tasks.jsonl | | Tasks: .workflow/.loop/{loopId}.tasks.jsonl |
| | | |
| /start -> Trigger ccw-loop skill with --loop-id | | /start -> Trigger ccw-loop skill with --loop-id |
| /pause -> Set status='paused' (skill checks before action) | | /pause -> Set status='paused' (skill checks before action) |
@@ -42,8 +42,8 @@ Stateless iterative development loop using Codex subagent pattern. Supports deve
| | | |
| Codex Pattern: spawn_agent -> wait -> send_input -> close | | Codex Pattern: spawn_agent -> wait -> send_input -> close |
| | | |
| Reads/Writes: .loop/{loopId}.json (unified state) | | Reads/Writes: .workflow/.loop/{loopId}.json (unified state) |
| Writes: .loop/{loopId}.progress/* (progress files) | | Writes: .workflow/.loop/{loopId}.progress/* (progress files) |
| | | |
| BEFORE each action: | | BEFORE each action: |
| -> Check status: paused/stopped -> exit gracefully | | -> Check status: paused/stopped -> exit gracefully |
@@ -55,9 +55,9 @@ Stateless iterative development loop using Codex subagent pattern. Supports deve
## Key Design Principles (Codex Adaptation) ## Key Design Principles (Codex Adaptation)
1. **Unified State**: API and Skill share `.loop/{loopId}.json` state file 1. **Unified State**: API and Skill share `.workflow/.loop/{loopId}.json` state file
2. **Control Signals**: Skill checks status field before each action (paused/stopped) 2. **Control Signals**: Skill checks status field before each action (paused/stopped)
3. **File-Driven**: All progress documented in `.loop/{loopId}.progress/` 3. **File-Driven**: All progress documented in `.workflow/.loop/{loopId}.progress/`
4. **Resumable**: Continue any loop with `--loop-id` 4. **Resumable**: Continue any loop with `--loop-id`
5. **Dual Trigger**: Supports API trigger (`--loop-id`) and direct call (task description) 5. **Dual Trigger**: Supports API trigger (`--loop-id`) and direct call (task description)
6. **Single Agent Deep Interaction**: Use send_input for multi-phase execution instead of multiple agents 6. **Single Agent Deep Interaction**: Use send_input for multi-phase execution instead of multiple agents
@@ -100,7 +100,7 @@ Develop -> Debug -> Validate -> (if issues) -> Develop -> ...
## Session Structure (Unified Location) ## Session Structure (Unified Location)
``` ```
.loop/ .workflow/.loop/
+-- {loopId}.json # Master state file (API + Skill shared) +-- {loopId}.json # Master state file (API + Skill shared)
+-- {loopId}.tasks.jsonl # Task list (API managed) +-- {loopId}.tasks.jsonl # Task list (API managed)
+-- {loopId}.progress/ # Skill progress files +-- {loopId}.progress/ # Skill progress files
@@ -129,8 +129,8 @@ const loopId = args['--loop-id'] || (() => {
return `loop-v2-${timestamp}-${random}` return `loop-v2-${timestamp}-${random}`
})() })()
const loopFile = `.loop/${loopId}.json` const loopFile = `.workflow/.loop/${loopId}.json`
const progressDir = `.loop/${loopId}.progress` const progressDir = `.workflow/.loop/${loopId}.progress`
// Create progress directory // Create progress directory
mkdir -p "${progressDir}" mkdir -p "${progressDir}"
@@ -144,14 +144,14 @@ mkdir -p "${progressDir}"
// Step 1: Read or create initial state // Step 1: Read or create initial state
let state = null let state = null
if (existingLoopId) { if (existingLoopId) {
state = JSON.parse(Read(`.loop/${loopId}.json`)) state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
if (!state) { if (!state) {
console.error(`Loop not found: ${loopId}`) console.error(`Loop not found: ${loopId}`)
return return
} }
} else { } else {
state = createInitialState(loopId, taskDescription) state = createInitialState(loopId, taskDescription)
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
} }
// Step 2: Create orchestrator agent (single agent handles all phases) // Step 2: Create orchestrator agent (single agent handles all phases)
@@ -169,7 +169,7 @@ const agent = spawn_agent({
## LOOP CONTEXT ## LOOP CONTEXT
- **Loop ID**: ${loopId} - **Loop ID**: ${loopId}
- **State File**: .loop/${loopId}.json - **State File**: .workflow/.loop/${loopId}.json
- **Progress Dir**: ${progressDir} - **Progress Dir**: ${progressDir}
- **Mode**: ${mode} // 'interactive' or 'auto' - **Mode**: ${mode} // 'interactive' or 'auto'
@@ -186,7 +186,7 @@ ${taskDescription}
You are executing CCW Loop orchestrator. Your job: You are executing CCW Loop orchestrator. Your job:
1. **Check Control Signals** 1. **Check Control Signals**
- Read .loop/${loopId}.json - Read .workflow/.loop/${loopId}.json
- If status === 'paused' -> Output "PAUSED" and stop - If status === 'paused' -> Output "PAUSED" and stop
- If status === 'failed' -> Output "STOPPED" and stop - If status === 'failed' -> Output "STOPPED" and stop
- If status === 'running' -> Continue - If status === 'running' -> Continue
@@ -200,7 +200,7 @@ You are executing CCW Loop orchestrator. Your job:
3. **Execute Action** 3. **Execute Action**
- Follow action instructions from ~/.codex/skills/ccw-loop/phases/actions/ - Follow action instructions from ~/.codex/skills/ccw-loop/phases/actions/
- Update progress files in ${progressDir}/ - Update progress files in ${progressDir}/
- Update state in .loop/${loopId}.json - Update state in .workflow/.loop/${loopId}.json
4. **Output Format** 4. **Output Format**
\`\`\` \`\`\`
@@ -283,10 +283,10 @@ Continue with: ${actionResult.next_action}
} }
// Update iteration count in state // Update iteration count in state
const currentState = JSON.parse(Read(`.loop/${loopId}.json`)) const currentState = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
currentState.current_iteration = iteration currentState.current_iteration = iteration
currentState.updated_at = getUtc8ISOString() currentState.updated_at = getUtc8ISOString()
Write(`.loop/${loopId}.json`, JSON.stringify(currentState, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(currentState, null, 2))
} }
// Step 4: Cleanup // Step 4: Cleanup

View File

@@ -20,7 +20,7 @@ Complete CCW Loop session and generate summary report.
### Step 1: Verify Control Signals ### Step 1: Verify Control Signals
```javascript ```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`)) const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
if (state.status !== 'running') { if (state.status !== 'running') {
return { return {
@@ -174,7 +174,7 @@ state.updated_at = timestamp
state.skill_state.last_action = 'COMPLETE' state.skill_state.last_action = 'COMPLETE'
state.skill_state.summary = stats state.skill_state.summary = stats
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
``` ```
## Output Format ## Output Format
@@ -190,8 +190,8 @@ ACTION_RESULT:
} }
FILES_UPDATED: FILES_UPDATED:
- .loop/{loopId}.json: Status set to completed - .workflow/.loop/{loopId}.json: Status set to completed
- .loop/{loopId}.progress/summary.md: Summary report generated - .workflow/.loop/{loopId}.progress/summary.md: Summary report generated
NEXT_ACTION_NEEDED: COMPLETED NEXT_ACTION_NEEDED: COMPLETED
``` ```

View File

@@ -233,7 +233,7 @@ if (confirmedHypothesis) {
state.skill_state.last_action = 'DEBUG' state.skill_state.last_action = 'DEBUG'
state.updated_at = timestamp state.updated_at = timestamp
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
``` ```
## Output Format ## Output Format
@@ -249,8 +249,8 @@ ACTION_RESULT:
} }
FILES_UPDATED: FILES_UPDATED:
- .loop/{loopId}.progress/debug.md: Understanding updated - .workflow/.loop/{loopId}.progress/debug.md: Understanding updated
- .loop/{loopId}.progress/hypotheses.json: Hypotheses updated - .workflow/.loop/{loopId}.progress/hypotheses.json: Hypotheses updated
- [Source files]: Instrumentation added - [Source files]: Instrumentation added
NEXT_ACTION_NEEDED: {DEBUG | VALIDATE | DEVELOP | MENU} NEXT_ACTION_NEEDED: {DEBUG | VALIDATE | DEVELOP | MENU}

View File

@@ -20,7 +20,7 @@ Execute development task and record progress to develop.md.
### Step 1: Verify Control Signals ### Step 1: Verify Control Signals
```javascript ```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`)) const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
if (state.status !== 'running') { if (state.status !== 'running') {
return { return {
@@ -133,7 +133,7 @@ state.skill_state.last_action = 'DEVELOP'
state.skill_state.completed_actions.push('DEVELOP') state.skill_state.completed_actions.push('DEVELOP')
state.updated_at = timestamp state.updated_at = timestamp
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
``` ```
## Output Format ## Output Format
@@ -149,9 +149,9 @@ ACTION_RESULT:
} }
FILES_UPDATED: FILES_UPDATED:
- .loop/{loopId}.json: Task status updated - .workflow/.loop/{loopId}.json: Task status updated
- .loop/{loopId}.progress/develop.md: Progress entry added - .workflow/.loop/{loopId}.progress/develop.md: Progress entry added
- .loop/{loopId}.progress/changes.log: Change entry added - .workflow/.loop/{loopId}.progress/changes.log: Change entry added
NEXT_ACTION_NEEDED: {DEVELOP | DEBUG | VALIDATE | MENU} NEXT_ACTION_NEEDED: {DEVELOP | DEBUG | VALIDATE | MENU}
``` ```

View File

@@ -19,7 +19,7 @@ Initialize CCW Loop session, create directory structure and initial state.
### Step 1: Verify Control Signals ### Step 1: Verify Control Signals
```javascript ```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`)) const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
if (state.status !== 'running') { if (state.status !== 'running') {
return { return {
@@ -34,7 +34,7 @@ if (state.status !== 'running') {
### Step 2: Create Directory Structure ### Step 2: Create Directory Structure
```javascript ```javascript
const progressDir = `.loop/${loopId}.progress` const progressDir = `.workflow/.loop/${loopId}.progress`
// Directories created by orchestrator, verify they exist // Directories created by orchestrator, verify they exist
// mkdir -p ${progressDir} // mkdir -p ${progressDir}
@@ -131,7 +131,7 @@ const skillState = {
state.skill_state = skillState state.skill_state = skillState
state.updated_at = getUtc8ISOString() state.updated_at = getUtc8ISOString()
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
``` ```
## Output Format ## Output Format
@@ -143,8 +143,8 @@ ACTION_RESULT:
- message: Session initialized with {N} development tasks - message: Session initialized with {N} development tasks
FILES_UPDATED: FILES_UPDATED:
- .loop/{loopId}.json: skill_state initialized - .workflow/.loop/{loopId}.json: skill_state initialized
- .loop/{loopId}.progress/develop.md: Progress document created - .workflow/.loop/{loopId}.progress/develop.md: Progress document created
NEXT_ACTION_NEEDED: {DEVELOP (auto) | MENU (interactive)} NEXT_ACTION_NEEDED: {DEVELOP (auto) | MENU (interactive)}
``` ```

View File

@@ -20,7 +20,7 @@ Display interactive action menu for user selection.
### Step 1: Verify Control Signals ### Step 1: Verify Control Signals
```javascript ```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`)) const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
if (state.status !== 'running') { if (state.status !== 'running') {
return { return {
@@ -172,7 +172,7 @@ If user selects "exit":
// Save current state // Save current state
state.status = 'user_exit' state.status = 'user_exit'
state.updated_at = getUtc8ISOString() state.updated_at = getUtc8ISOString()
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
return { return {
action: 'MENU', action: 'MENU',

View File

@@ -21,7 +21,7 @@ Run tests and verify implementation, record results to validate.md.
### Step 1: Verify Control Signals ### Step 1: Verify Control Signals
```javascript ```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`)) const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
if (state.status !== 'running') { if (state.status !== 'running') {
return { return {
@@ -174,7 +174,7 @@ state.skill_state.validate.last_run_at = timestamp
state.skill_state.last_action = 'VALIDATE' state.skill_state.last_action = 'VALIDATE'
state.updated_at = timestamp state.updated_at = timestamp
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
``` ```
## Output Format ## Output Format
@@ -191,9 +191,9 @@ ACTION_RESULT:
} }
FILES_UPDATED: FILES_UPDATED:
- .loop/{loopId}.progress/validate.md: Validation report created - .workflow/.loop/{loopId}.progress/validate.md: Validation report created
- .loop/{loopId}.progress/test-results.json: Test results saved - .workflow/.loop/{loopId}.progress/test-results.json: Test results saved
- .loop/{loopId}.progress/coverage.json: Coverage data saved (if available) - .workflow/.loop/{loopId}.progress/coverage.json: Coverage data saved (if available)
NEXT_ACTION_NEEDED: {COMPLETE | DEBUG | DEVELOP | MENU} NEXT_ACTION_NEEDED: {COMPLETE | DEBUG | DEVELOP | MENU}
``` ```

View File

@@ -45,7 +45,7 @@ const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOSt
* @param loopId - Loop ID (e.g., "loop-v2-20260122-abc123") * @param loopId - Loop ID (e.g., "loop-v2-20260122-abc123")
*/ */
function readLoopState(loopId) { function readLoopState(loopId) {
const stateFile = `.loop/${loopId}.json` const stateFile = `.workflow/.loop/${loopId}.json`
if (!fs.existsSync(stateFile)) { if (!fs.existsSync(stateFile)) {
return null return null
@@ -63,7 +63,7 @@ function readLoopState(loopId) {
* Create new loop state (only for direct calls, API triggers have existing state) * Create new loop state (only for direct calls, API triggers have existing state)
*/ */
function createLoopState(loopId, taskDescription) { function createLoopState(loopId, taskDescription) {
const stateFile = `.loop/${loopId}.json` const stateFile = `.workflow/.loop/${loopId}.json`
const now = getUtc8ISOString() const now = getUtc8ISOString()
const state = { const state = {
@@ -83,7 +83,7 @@ function createLoopState(loopId, taskDescription) {
// Ensure directories exist // Ensure directories exist
mkdir -p ".loop" mkdir -p ".loop"
mkdir -p ".loop/${loopId}.progress" mkdir -p ".workflow/.loop/${loopId}.progress"
Write(stateFile, JSON.stringify(state, null, 2)) Write(stateFile, JSON.stringify(state, null, 2))
return state return state
@@ -137,7 +137,7 @@ async function runOrchestrator(options = {}) {
return { status: 'error', message: 'Missing loopId or task' } return { status: 'error', message: 'Missing loopId or task' }
} }
const progressDir = `.loop/${loopId}.progress` const progressDir = `.workflow/.loop/${loopId}.progress`
// 2. Create executor agent (single agent for entire loop) // 2. Create executor agent (single agent for entire loop)
const agent = spawn_agent({ const agent = spawn_agent({
@@ -154,7 +154,7 @@ async function runOrchestrator(options = {}) {
## LOOP CONTEXT ## LOOP CONTEXT
- **Loop ID**: ${loopId} - **Loop ID**: ${loopId}
- **State File**: .loop/${loopId}.json - **State File**: .workflow/.loop/${loopId}.json
- **Progress Dir**: ${progressDir} - **Progress Dir**: ${progressDir}
- **Mode**: ${mode} - **Mode**: ${mode}
@@ -213,7 +213,7 @@ Execution timeout reached. Please:
state = readLoopState(loopId) state = readLoopState(loopId)
state.current_iteration = iteration state.current_iteration = iteration
state.updated_at = getUtc8ISOString() state.updated_at = getUtc8ISOString()
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2)) Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
// Handle different outcomes // Handle different outcomes
switch (actionResult.next_action) { switch (actionResult.next_action) {

View File

@@ -4,7 +4,7 @@ CCW Loop state structure definition for Codex subagent pattern.
## State File ## State File
**Location**: `.loop/{loopId}.json` (unified location, API + Skill shared) **Location**: `.workflow/.loop/{loopId}.json` (unified location, API + Skill shared)
## Structure Definition ## Structure Definition
@@ -208,7 +208,7 @@ Agent checks control signals at start of every action:
* @returns { continue: boolean, action: 'pause_exit' | 'stop_exit' | 'continue' } * @returns { continue: boolean, action: 'pause_exit' | 'stop_exit' | 'continue' }
*/ */
function checkControlSignals(loopId) { function checkControlSignals(loopId) {
const state = JSON.parse(Read(`.loop/${loopId}.json`)) const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
switch (state.status) { switch (state.status) {
case 'paused': case 'paused':
@@ -338,17 +338,17 @@ State-to-file mapping:
| State Field | Sync File | Sync Timing | | State Field | Sync File | Sync Timing |
|-------------|-----------|-------------| |-------------|-----------|-------------|
| Entire LoopState | `.loop/{loopId}.json` | Every state change (master) | | Entire LoopState | `.workflow/.loop/{loopId}.json` | Every state change (master) |
| `skill_state.develop` | `.loop/{loopId}.progress/develop.md` | After each dev operation | | `skill_state.develop` | `.workflow/.loop/{loopId}.progress/develop.md` | After each dev operation |
| `skill_state.debug` | `.loop/{loopId}.progress/debug.md` | After each debug operation | | `skill_state.debug` | `.workflow/.loop/{loopId}.progress/debug.md` | After each debug operation |
| `skill_state.validate` | `.loop/{loopId}.progress/validate.md` | After each validation | | `skill_state.validate` | `.workflow/.loop/{loopId}.progress/validate.md` | After each validation |
| Code changes log | `.loop/{loopId}.progress/changes.log` | Each file modification (NDJSON) | | Code changes log | `.workflow/.loop/{loopId}.progress/changes.log` | Each file modification (NDJSON) |
| Debug log | `.loop/{loopId}.progress/debug.log` | Each debug log (NDJSON) | | Debug log | `.workflow/.loop/{loopId}.progress/debug.log` | Each debug log (NDJSON) |
### File Structure ### File Structure
``` ```
.loop/ .workflow/.loop/
+-- loop-v2-20260122-abc123.json # Master state file (API + Skill) +-- loop-v2-20260122-abc123.json # Master state file (API + Skill)
+-- loop-v2-20260122-abc123.tasks.jsonl # Task list (API managed) +-- loop-v2-20260122-abc123.tasks.jsonl # Task list (API managed)
+-- loop-v2-20260122-abc123.progress/ # Skill progress files +-- loop-v2-20260122-abc123.progress/ # Skill progress files
@@ -366,7 +366,7 @@ If master state file corrupted, rebuild skill_state from progress files:
```javascript ```javascript
function rebuildSkillStateFromProgress(loopId) { function rebuildSkillStateFromProgress(loopId) {
const progressDir = `.loop/${loopId}.progress` const progressDir = `.workflow/.loop/${loopId}.progress`
// Parse progress files to rebuild state // Parse progress files to rebuild state
const skill_state = { const skill_state = {
@@ -381,7 +381,7 @@ function rebuildSkillStateFromProgress(loopId) {
## Codex Pattern Notes ## Codex Pattern Notes
1. **Agent reads state**: Agent reads `.loop/{loopId}.json` at action start 1. **Agent reads state**: Agent reads `.workflow/.loop/{loopId}.json` at action start
2. **Agent writes state**: Agent updates state after action completion 2. **Agent writes state**: Agent updates state after action completion
3. **Orchestrator tracks iterations**: Main loop tracks `current_iteration` 3. **Orchestrator tracks iterations**: Main loop tracks `current_iteration`
4. **Single agent context**: All state updates in same agent conversation via send_input 4. **Single agent context**: All state updates in same agent conversation via send_input

View File

@@ -439,9 +439,9 @@ export function loadClaudeCliTools(projectDir: string): ClaudeCliToolsConfig & {
const migrated = migrateConfig(parsed, projectDir); const migrated = migrateConfig(parsed, projectDir);
const needsSave = migrated.version !== parsed.version; const needsSave = migrated.version !== parsed.version;
// Merge tools with defaults and ensure required fields exist // Load user-configured tools only (defaults NOT merged)
const mergedTools: Record<string, ClaudeCliTool> = {}; const mergedTools: Record<string, ClaudeCliTool> = {};
for (const [key, tool] of Object.entries({ ...DEFAULT_TOOLS_CONFIG.tools, ...(migrated.tools || {}) })) { for (const [key, tool] of Object.entries(migrated.tools || {})) {
mergedTools[key] = { mergedTools[key] = {
...ensureToolTags(tool), ...ensureToolTags(tool),
type: tool.type ?? 'builtin', type: tool.type ?? 'builtin',