From 8c953b287d50f61b2cd824c13d68f313231d6888 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Sun, 1 Mar 2026 20:58:26 +0800 Subject: [PATCH] feat(idaw): add run-coordinate command for external CLI execution with hook callbacks --- .claude/commands/idaw/run-coordinate.md | 648 ++++++++++++++++++++++++ docs/commands/claude/idaw.md | 61 +++ docs/commands/claude/index.md | 3 +- docs/zh/commands/claude/idaw.md | 64 +++ docs/zh/commands/claude/index.md | 1 + 5 files changed, 776 insertions(+), 1 deletion(-) create mode 100644 .claude/commands/idaw/run-coordinate.md diff --git a/.claude/commands/idaw/run-coordinate.md b/.claude/commands/idaw/run-coordinate.md new file mode 100644 index 00000000..266831e3 --- /dev/null +++ b/.claude/commands/idaw/run-coordinate.md @@ -0,0 +1,648 @@ +--- +name: run-coordinate +description: IDAW coordinator - execute task skill chains via external CLI with hook callbacks and git checkpoints +argument-hint: "[-y|--yes] [--task [,,...]] [--dry-run] [--tool ]" +allowed-tools: Task(*), AskUserQuestion(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*) +--- + +# IDAW Run Coordinate Command (/idaw:run-coordinate) + +Coordinator variant of `/idaw:run`: external CLI execution with background tasks and hook callbacks. + +**Execution Model**: `ccw cli -p "..." --tool --mode write` in background → hook callback → next step. + +**vs `/idaw:run`**: Direct `Skill()` calls (blocking, main process) vs `ccw cli` (background, external process). + +## When to Use + +| Scenario | Use | +|----------|-----| +| Standard IDAW execution (main process) | `/idaw:run` | +| External CLI execution (background, hook-driven) | `/idaw:run-coordinate` | +| Need `claude` or `gemini` as execution tool | `/idaw:run-coordinate --tool claude` | +| Long-running tasks, avoid context window pressure | `/idaw:run-coordinate` | + +## Skill Chain Mapping + +```javascript +const SKILL_CHAIN_MAP = { + 'bugfix': ['workflow-lite-plan', 'workflow-test-fix'], + 'bugfix-hotfix': ['workflow-lite-plan'], + 'feature': ['workflow-lite-plan', 'workflow-test-fix'], + 'feature-complex': ['workflow-plan', 'workflow-execute', 'workflow-test-fix'], + 'refactor': ['workflow:refactor-cycle'], + 'tdd': ['workflow-tdd-plan', 'workflow-execute'], + 'test': ['workflow-test-fix'], + 'test-fix': ['workflow-test-fix'], + 'review': ['review-cycle'], + 'docs': ['workflow-lite-plan'] +}; +``` + +## Task Type Inference + +```javascript +function inferTaskType(title, description) { + const text = `${title} ${description}`.toLowerCase(); + if (/urgent|production|critical/.test(text) && /fix|bug/.test(text)) return 'bugfix-hotfix'; + if (/refactor|重构|tech.*debt/.test(text)) return 'refactor'; + if (/tdd|test-driven|test first/.test(text)) return 'tdd'; + if (/test fail|fix test|failing test/.test(text)) return 'test-fix'; + if (/generate test|写测试|add test/.test(text)) return 'test'; + if (/review|code review/.test(text)) return 'review'; + if (/docs|documentation|readme/.test(text)) return 'docs'; + if (/fix|bug|error|crash|fail/.test(text)) return 'bugfix'; + if (/complex|multi-module|architecture/.test(text)) return 'feature-complex'; + return 'feature'; +} +``` + +## 6-Phase Execution (Coordinator Model) + +### Phase 1: Load Tasks + +```javascript +const args = $ARGUMENTS; +const autoYes = /(-y|--yes)/.test(args); +const dryRun = /--dry-run/.test(args); +const taskFilter = args.match(/--task\s+([\w,-]+)/)?.[1]?.split(',') || null; +const cliTool = args.match(/--tool\s+(\w+)/)?.[1] || 'claude'; + +// Load task files +const taskFiles = Glob('.workflow/.idaw/tasks/IDAW-*.json') || []; + +if (taskFiles.length === 0) { + console.log('No IDAW tasks found. Use /idaw:add to create tasks.'); + return; +} + +// Parse and filter +let tasks = taskFiles.map(f => JSON.parse(Read(f))); + +if (taskFilter) { + tasks = tasks.filter(t => taskFilter.includes(t.id)); +} else { + tasks = tasks.filter(t => t.status === 'pending'); +} + +if (tasks.length === 0) { + console.log('No pending tasks to execute. Use /idaw:add to add tasks or --task to specify IDs.'); + return; +} + +// Sort: priority ASC (1=critical first), then ID ASC +tasks.sort((a, b) => { + if (a.priority !== b.priority) return a.priority - b.priority; + return a.id.localeCompare(b.id); +}); +``` + +### Phase 2: Session Setup + +```javascript +// Generate session ID: IDA-{slug}-YYYYMMDD +const slug = tasks[0].title + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .substring(0, 20) + .replace(/-$/, ''); +const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, ''); +let sessionId = `IDA-${slug}-${dateStr}`; + +// Check collision +const existingSession = Glob(`.workflow/.idaw/sessions/${sessionId}/session.json`); +if (existingSession?.length > 0) { + sessionId = `${sessionId}-2`; +} + +const sessionDir = `.workflow/.idaw/sessions/${sessionId}`; +Bash(`mkdir -p "${sessionDir}"`); + +const session = { + session_id: sessionId, + mode: 'coordinate', // ★ Marks this as coordinator-mode session + cli_tool: cliTool, + status: 'running', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + tasks: tasks.map(t => t.id), + current_task: null, + current_skill_index: 0, + completed: [], + failed: [], + skipped: [], + prompts_used: [] +}; + +Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + +// Initialize progress.md +const progressHeader = `# IDAW Progress — ${sessionId} (coordinate mode)\nStarted: ${session.created_at}\nCLI Tool: ${cliTool}\n\n`; +Write(`${sessionDir}/progress.md`, progressHeader); +``` + +### Phase 3: Startup Protocol + +```javascript +// Check for existing running sessions +const runningSessions = Glob('.workflow/.idaw/sessions/IDA-*/session.json') + ?.map(f => { try { return JSON.parse(Read(f)); } catch { return null; } }) + .filter(s => s && s.status === 'running' && s.session_id !== sessionId) || []; + +if (runningSessions.length > 0 && !autoYes) { + const answer = AskUserQuestion({ + questions: [{ + question: `Found running session: ${runningSessions[0].session_id}. How to proceed?`, + header: 'Conflict', + multiSelect: false, + options: [ + { label: 'Resume existing', description: 'Use /idaw:resume instead' }, + { label: 'Start fresh', description: 'Continue with new session' }, + { label: 'Abort', description: 'Cancel this run' } + ] + }] + }); + if (answer.answers?.Conflict === 'Resume existing') { + console.log(`Use: /idaw:resume ${runningSessions[0].session_id}`); + return; + } + if (answer.answers?.Conflict === 'Abort') return; +} + +// Check git status +const gitStatus = Bash('git status --porcelain 2>/dev/null'); +if (gitStatus?.trim() && !autoYes) { + const answer = AskUserQuestion({ + questions: [{ + question: 'Working tree has uncommitted changes. How to proceed?', + header: 'Git', + multiSelect: false, + options: [ + { label: 'Continue', description: 'Proceed with dirty tree' }, + { label: 'Stash', description: 'git stash before running' }, + { label: 'Abort', description: 'Stop and handle manually' } + ] + }] + }); + if (answer.answers?.Git === 'Stash') Bash('git stash push -m "idaw-pre-run"'); + if (answer.answers?.Git === 'Abort') return; +} + +// Dry run +if (dryRun) { + console.log(`# Dry Run — ${sessionId} (coordinate mode, tool: ${cliTool})\n`); + for (const task of tasks) { + const taskType = task.task_type || inferTaskType(task.title, task.description); + const chain = task.skill_chain || SKILL_CHAIN_MAP[taskType] || SKILL_CHAIN_MAP['feature']; + console.log(`## ${task.id}: ${task.title}`); + console.log(` Type: ${taskType} | Priority: ${task.priority}`); + console.log(` Chain: ${chain.join(' → ')}`); + console.log(` CLI: ccw cli --tool ${cliTool} --mode write\n`); + } + console.log(`Total: ${tasks.length} tasks`); + return; +} +``` + +### Phase 4: Launch First Task (then wait for hook) + +```javascript +// Start with the first task, first skill +const firstTask = tasks[0]; +const resolvedType = firstTask.task_type || inferTaskType(firstTask.title, firstTask.description); +const chain = firstTask.skill_chain || SKILL_CHAIN_MAP[resolvedType] || SKILL_CHAIN_MAP['feature']; + +// Update task → in_progress +firstTask.status = 'in_progress'; +firstTask.task_type = resolvedType; +firstTask.execution.started_at = new Date().toISOString(); +Write(`.workflow/.idaw/tasks/${firstTask.id}.json`, JSON.stringify(firstTask, null, 2)); + +// Update session +session.current_task = firstTask.id; +session.current_skill_index = 0; +session.updated_at = new Date().toISOString(); +Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + +// ━━━ Pre-Task CLI Context Analysis (for complex/bugfix tasks) ━━━ +if (['bugfix', 'bugfix-hotfix', 'feature-complex'].includes(resolvedType)) { + console.log(`Pre-analysis: gathering context for ${resolvedType} task...`); + const affectedFiles = (firstTask.context?.affected_files || []).join(', '); + const preAnalysisPrompt = `PURPOSE: Pre-analyze codebase context for IDAW task. +TASK: • Understand current state of: ${affectedFiles || 'files related to: ' + firstTask.title} • Identify dependencies and risk areas +MODE: analysis +CONTEXT: @**/* +EXPECTED: Brief context summary in 3-5 bullet points +CONSTRAINTS: Keep concise`; + Bash(`ccw cli -p '${preAnalysisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis 2>&1 || echo "Pre-analysis skipped"`); +} + +// Assemble prompt for first skill +const skillName = chain[0]; +const prompt = assembleCliPrompt(skillName, firstTask, null, autoYes); + +session.prompts_used.push({ + task_id: firstTask.id, + skill_index: 0, + skill: skillName, + prompt: prompt, + timestamp: new Date().toISOString() +}); +session.updated_at = new Date().toISOString(); +Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + +// Launch via ccw cli in background +console.log(`[1/${tasks.length}] ${firstTask.id}: ${firstTask.title}`); +console.log(` Chain: ${chain.join(' → ')}`); +console.log(` Launching: ${skillName} via ccw cli --tool ${cliTool}`); + +Bash( + `ccw cli -p "${escapeForShell(prompt)}" --tool ${cliTool} --mode write`, + { run_in_background: true } +); + +// ★ STOP HERE — wait for hook callback +// Hook callback will trigger handleStepCompletion() below +``` + +### Phase 5: Hook Callback Handler (per-step completion) + +```javascript +// Called by hook when background CLI completes +async function handleStepCompletion(sessionId, cliOutput) { + const sessionDir = `.workflow/.idaw/sessions/${sessionId}`; + const session = JSON.parse(Read(`${sessionDir}/session.json`)); + + const taskId = session.current_task; + const task = JSON.parse(Read(`.workflow/.idaw/tasks/${taskId}.json`)); + + const resolvedType = task.task_type || inferTaskType(task.title, task.description); + const chain = task.skill_chain || SKILL_CHAIN_MAP[resolvedType] || SKILL_CHAIN_MAP['feature']; + const skillIdx = session.current_skill_index; + const skillName = chain[skillIdx]; + + // Parse CLI output for session ID + const parsedOutput = parseCliOutput(cliOutput); + + // Record skill result + task.execution.skill_results.push({ + skill: skillName, + status: parsedOutput.success ? 'completed' : 'failed', + session_id: parsedOutput.sessionId, + timestamp: new Date().toISOString() + }); + + // ━━━ Handle failure with CLI diagnosis ━━━ + if (!parsedOutput.success) { + console.log(` ${skillName} failed. Running CLI diagnosis...`); + const diagnosisPrompt = `PURPOSE: Diagnose why skill "${skillName}" failed during IDAW task. +TASK: • Analyze error output • Check affected files: ${(task.context?.affected_files || []).join(', ') || 'unknown'} +MODE: analysis +CONTEXT: @**/* | Memory: IDAW task ${task.id}: ${task.title} +EXPECTED: Root cause + fix recommendation +CONSTRAINTS: Actionable diagnosis`; + Bash(`ccw cli -p '${diagnosisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis --rule analysis-diagnose-bug-root-cause 2>&1 || true`); + + task.execution.skill_results.push({ + skill: `cli-diagnosis:${skillName}`, + status: 'completed', + timestamp: new Date().toISOString() + }); + + // Retry once + console.log(` Retrying: ${skillName}`); + const retryPrompt = assembleCliPrompt(skillName, task, parsedOutput, true); + session.prompts_used.push({ + task_id: taskId, + skill_index: skillIdx, + skill: `${skillName}-retry`, + prompt: retryPrompt, + timestamp: new Date().toISOString() + }); + Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + Write(`.workflow/.idaw/tasks/${taskId}.json`, JSON.stringify(task, null, 2)); + + Bash( + `ccw cli -p "${escapeForShell(retryPrompt)}" --tool ${session.cli_tool} --mode write`, + { run_in_background: true } + ); + return; // Wait for retry hook + } + + // ━━━ Skill succeeded — advance ━━━ + const nextSkillIdx = skillIdx + 1; + + if (nextSkillIdx < chain.length) { + // More skills in this task's chain → launch next skill + session.current_skill_index = nextSkillIdx; + session.updated_at = new Date().toISOString(); + + const nextSkill = chain[nextSkillIdx]; + const nextPrompt = assembleCliPrompt(nextSkill, task, parsedOutput, true); + + session.prompts_used.push({ + task_id: taskId, + skill_index: nextSkillIdx, + skill: nextSkill, + prompt: nextPrompt, + timestamp: new Date().toISOString() + }); + Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + Write(`.workflow/.idaw/tasks/${taskId}.json`, JSON.stringify(task, null, 2)); + + console.log(` Next skill: ${nextSkill}`); + Bash( + `ccw cli -p "${escapeForShell(nextPrompt)}" --tool ${session.cli_tool} --mode write`, + { run_in_background: true } + ); + return; // Wait for next hook + } + + // ━━━ Task chain complete — git checkpoint ━━━ + const commitMsg = `feat(idaw): ${task.title} [${task.id}]`; + const diffCheck = Bash('git diff --stat HEAD 2>/dev/null || echo ""'); + const untrackedCheck = Bash('git ls-files --others --exclude-standard 2>/dev/null || echo ""'); + + if (diffCheck?.trim() || untrackedCheck?.trim()) { + Bash('git add -A'); + Bash(`git commit -m "$(cat <<'EOF'\n${commitMsg}\nEOF\n)"`); + const commitHash = Bash('git rev-parse --short HEAD 2>/dev/null')?.trim(); + task.execution.git_commit = commitHash; + } else { + task.execution.git_commit = 'no-commit'; + } + + task.status = 'completed'; + task.execution.completed_at = new Date().toISOString(); + task.updated_at = new Date().toISOString(); + Write(`.workflow/.idaw/tasks/${taskId}.json`, JSON.stringify(task, null, 2)); + + session.completed.push(taskId); + + // Append progress + const progressEntry = `## ${task.id} — ${task.title}\n` + + `- Status: completed\n` + + `- Type: ${task.task_type}\n` + + `- Chain: ${chain.join(' → ')}\n` + + `- Commit: ${task.execution.git_commit || '-'}\n` + + `- Mode: coordinate (${session.cli_tool})\n\n`; + const currentProgress = Read(`${sessionDir}/progress.md`); + Write(`${sessionDir}/progress.md`, currentProgress + progressEntry); + + // ━━━ Advance to next task ━━━ + const allTaskIds = session.tasks; + const completedSet = new Set([...session.completed, ...session.failed, ...session.skipped]); + const nextTaskId = allTaskIds.find(id => !completedSet.has(id)); + + if (nextTaskId) { + // Load next task + const nextTask = JSON.parse(Read(`.workflow/.idaw/tasks/${nextTaskId}.json`)); + const nextType = nextTask.task_type || inferTaskType(nextTask.title, nextTask.description); + const nextChain = nextTask.skill_chain || SKILL_CHAIN_MAP[nextType] || SKILL_CHAIN_MAP['feature']; + + nextTask.status = 'in_progress'; + nextTask.task_type = nextType; + nextTask.execution.started_at = new Date().toISOString(); + Write(`.workflow/.idaw/tasks/${nextTaskId}.json`, JSON.stringify(nextTask, null, 2)); + + session.current_task = nextTaskId; + session.current_skill_index = 0; + session.updated_at = new Date().toISOString(); + Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + + // Pre-analysis for complex tasks + if (['bugfix', 'bugfix-hotfix', 'feature-complex'].includes(nextType)) { + const affectedFiles = (nextTask.context?.affected_files || []).join(', '); + Bash(`ccw cli -p 'PURPOSE: Pre-analyze context for ${nextTask.title}. TASK: Check ${affectedFiles || "related files"}. MODE: analysis. EXPECTED: 3-5 bullet points.' --tool gemini --mode analysis 2>&1 || true`); + } + + const nextSkillName = nextChain[0]; + const nextPrompt = assembleCliPrompt(nextSkillName, nextTask, null, true); + + session.prompts_used.push({ + task_id: nextTaskId, + skill_index: 0, + skill: nextSkillName, + prompt: nextPrompt, + timestamp: new Date().toISOString() + }); + Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + + const taskNum = session.completed.length + 1; + const totalTasks = session.tasks.length; + console.log(`\n[${taskNum}/${totalTasks}] ${nextTaskId}: ${nextTask.title}`); + console.log(` Chain: ${nextChain.join(' → ')}`); + + Bash( + `ccw cli -p "${escapeForShell(nextPrompt)}" --tool ${session.cli_tool} --mode write`, + { run_in_background: true } + ); + return; // Wait for hook + } + + // ━━━ All tasks complete — Phase 6: Report ━━━ + session.status = session.failed.length > 0 && session.completed.length === 0 ? 'failed' : 'completed'; + session.current_task = null; + session.updated_at = new Date().toISOString(); + Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2)); + + const summary = `\n---\n## Summary (coordinate mode)\n` + + `- CLI Tool: ${session.cli_tool}\n` + + `- Completed: ${session.completed.length}\n` + + `- Failed: ${session.failed.length}\n` + + `- Skipped: ${session.skipped.length}\n` + + `- Total: ${session.tasks.length}\n`; + const finalProgress = Read(`${sessionDir}/progress.md`); + Write(`${sessionDir}/progress.md`, finalProgress + summary); + + console.log('\n=== IDAW Coordinate Complete ==='); + console.log(`Session: ${sessionId}`); + console.log(`Completed: ${session.completed.length}/${session.tasks.length}`); + if (session.failed.length > 0) console.log(`Failed: ${session.failed.join(', ')}`); +} +``` + +## Helper Functions + +### assembleCliPrompt + +```javascript +function assembleCliPrompt(skillName, task, previousResult, autoYes) { + let prompt = ''; + const yFlag = autoYes ? ' -y' : ''; + + // Map skill to command invocation + if (skillName === 'workflow-lite-plan') { + const goal = sanitize(`${task.title}\n${task.description}`); + prompt = `/workflow-lite-plan${yFlag} "${goal}"`; + if (task.task_type === 'bugfix') prompt = `/workflow-lite-plan${yFlag} --bugfix "${goal}"`; + if (task.task_type === 'bugfix-hotfix') prompt = `/workflow-lite-plan${yFlag} --hotfix "${goal}"`; + + } else if (skillName === 'workflow-plan') { + prompt = `/workflow-plan${yFlag} "${sanitize(task.title)}"`; + + } else if (skillName === 'workflow-execute') { + if (previousResult?.sessionId) { + prompt = `/workflow-execute${yFlag} --resume-session="${previousResult.sessionId}"`; + } else { + prompt = `/workflow-execute${yFlag}`; + } + + } else if (skillName === 'workflow-test-fix') { + if (previousResult?.sessionId) { + prompt = `/workflow-test-fix${yFlag} "${previousResult.sessionId}"`; + } else { + prompt = `/workflow-test-fix${yFlag} "${sanitize(task.title)}"`; + } + + } else if (skillName === 'workflow-tdd-plan') { + prompt = `/workflow-tdd-plan${yFlag} "${sanitize(task.title)}"`; + + } else if (skillName === 'workflow:refactor-cycle') { + prompt = `/workflow:refactor-cycle${yFlag} "${sanitize(task.title)}"`; + + } else if (skillName === 'review-cycle') { + if (previousResult?.sessionId) { + prompt = `/review-cycle${yFlag} --session="${previousResult.sessionId}"`; + } else { + prompt = `/review-cycle${yFlag}`; + } + + } else { + // Generic fallback + prompt = `/${skillName}${yFlag} "${sanitize(task.title)}"`; + } + + // Append task context + prompt += `\n\nTask: ${task.title}\nDescription: ${task.description}`; + if (task.context?.affected_files?.length > 0) { + prompt += `\nAffected files: ${task.context.affected_files.join(', ')}`; + } + if (task.context?.acceptance_criteria?.length > 0) { + prompt += `\nAcceptance criteria: ${task.context.acceptance_criteria.join('; ')}`; + } + + return prompt; +} +``` + +### sanitize & escapeForShell + +```javascript +function sanitize(text) { + return text + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\$/g, '\\$') + .replace(/`/g, '\\`'); +} + +function escapeForShell(prompt) { + return prompt.replace(/'/g, "'\\''"); +} +``` + +### parseCliOutput + +```javascript +function parseCliOutput(output) { + // Extract session ID from CLI output (e.g., WFS-xxx, session-xxx) + const sessionMatch = output.match(/(?:session|WFS|Session ID)[:\s]*([\w-]+)/i); + const success = !/(?:error|failed|fatal)/i.test(output) || /completed|success/i.test(output); + + return { + success, + sessionId: sessionMatch?.[1] || null, + raw: output?.substring(0, 500) + }; +} +``` + +## CLI-Assisted Analysis + +Same as `/idaw:run` — integrated at two points: + +### Pre-Task Context Analysis +For `bugfix`, `bugfix-hotfix`, `feature-complex` tasks: auto-invoke `ccw cli --tool gemini --mode analysis` before launching skill chain. + +### Error Recovery with CLI Diagnosis +When a skill's CLI execution fails: invoke diagnosis → retry once → if still fails, mark failed and advance. + +``` +Skill CLI fails → CLI diagnosis (gemini) → Retry CLI → Still fails → mark failed → next task +``` + +## State Flow + +``` +Phase 4: Launch first skill + ↓ + ccw cli --tool claude --mode write (background) + ↓ + ★ STOP — wait for hook callback + ↓ +Phase 5: handleStepCompletion() + ├─ Skill succeeded + more in chain → launch next skill → STOP + ├─ Skill succeeded + chain complete → git checkpoint → next task → STOP + ├─ Skill failed → CLI diagnosis → retry → STOP + └─ All tasks done → Phase 6: Report +``` + +## Session State (session.json) + +```json +{ + "session_id": "IDA-fix-login-20260301", + "mode": "coordinate", + "cli_tool": "claude", + "status": "running|waiting|completed|failed", + "created_at": "ISO", + "updated_at": "ISO", + "tasks": ["IDAW-001", "IDAW-002"], + "current_task": "IDAW-001", + "current_skill_index": 0, + "completed": [], + "failed": [], + "skipped": [], + "prompts_used": [ + { + "task_id": "IDAW-001", + "skill_index": 0, + "skill": "workflow-lite-plan", + "prompt": "/workflow-lite-plan -y \"Fix login timeout\"", + "timestamp": "ISO" + } + ] +} +``` + +## Differences from /idaw:run + +| Aspect | /idaw:run | /idaw:run-coordinate | +|--------|-----------|---------------------| +| Execution | `Skill()` blocking in main process | `ccw cli` background + hook callback | +| Context window | Shared (each skill uses main context) | Isolated (each CLI gets fresh context) | +| Concurrency | Sequential blocking | Sequential non-blocking (hook-driven) | +| State tracking | session.json + task.json | session.json + task.json + prompts_used | +| Tool selection | N/A (Skill native) | `--tool claude\|gemini\|qwen` | +| Resume | Via `/idaw:resume` (same) | Via `/idaw:resume` (same, detects mode) | +| Best for | Short chains, interactive | Long chains, autonomous, context-heavy | + +## Examples + +```bash +# Execute all pending tasks via claude CLI +/idaw:run-coordinate -y + +# Use specific CLI tool +/idaw:run-coordinate -y --tool gemini + +# Execute specific tasks +/idaw:run-coordinate --task IDAW-001,IDAW-003 --tool claude + +# Dry run (show plan without executing) +/idaw:run-coordinate --dry-run + +# Interactive mode +/idaw:run-coordinate +``` diff --git a/docs/commands/claude/idaw.md b/docs/commands/claude/idaw.md index bcdcffb3..24566f6b 100644 --- a/docs/commands/claude/idaw.md +++ b/docs/commands/claude/idaw.md @@ -29,6 +29,7 @@ |---------|----------|--------| | [`add`](#add) | Create tasks manually or import from issue | `/idaw:add [-y] [--from-issue ] "description" [--type ] [--priority 1-5]` | | [`run`](#run) | Execute task queue with git checkpoints | `/idaw:run [-y] [--task ] [--dry-run]` | +| [`run-coordinate`](#run-coordinate) | Execute via external CLI with hook callbacks | `/idaw:run-coordinate [-y] [--task ] [--tool ]` | | [`status`](#status) | View task and session progress | `/idaw:status [session-id]` | | [`resume`](#resume) | Resume interrupted session | `/idaw:resume [-y] [session-id]` | @@ -135,6 +136,66 @@ Phase 6: Report --- +### run-coordinate + +**Function**: Coordinator variant of `/idaw:run` — executes via external CLI with hook callbacks instead of blocking Skill() calls. + +**Syntax**: +```bash +/idaw:run-coordinate [-y|--yes] [--task [,,...]] [--dry-run] [--tool ] +``` + +**Options**: +- `--tool `: CLI tool to use (`claude`, `gemini`, `qwen`, default: `claude`) +- `--task `: Execute specific tasks +- `--dry-run`: Preview plan without executing +- `-y`: Auto mode + +**Execution Model**: + +``` +Launch skill via ccw cli --tool --mode write (background) + ↓ +★ STOP — wait for hook callback + ↓ +Hook fires → handleStepCompletion() + ├─ More skills in chain → launch next → STOP + ├─ Chain complete → git checkpoint → next task → STOP + └─ All done → Report +``` + +**When to Use**: + +| Scenario | Command | +|----------|---------| +| Standard execution (main process, blocking) | `/idaw:run` | +| External CLI, isolated context per skill | `/idaw:run-coordinate` | +| Long-running tasks, avoid context pressure | `/idaw:run-coordinate` | +| Need specific CLI tool (claude/gemini) | `/idaw:run-coordinate --tool gemini` | + +**Differences from `/idaw:run`**: + +| Aspect | `/idaw:run` | `/idaw:run-coordinate` | +|--------|-------------|----------------------| +| Execution | `Skill()` blocking | `ccw cli` background + hook | +| Context | Shared main context | Isolated per CLI call | +| Tool selection | N/A | `--tool claude\|gemini\|qwen` | +| State | session.json | session.json + prompts_used | + +**Examples**: +```bash +# Execute via claude CLI (default) +/idaw:run-coordinate -y + +# Use gemini as execution tool +/idaw:run-coordinate -y --tool gemini + +# Specific tasks +/idaw:run-coordinate --task IDAW-001,IDAW-003 --tool claude +``` + +--- + ### status **Function**: Read-only view of IDAW task queue and session progress. diff --git a/docs/commands/claude/index.md b/docs/commands/claude/index.md index a8302ec1..11df0353 100644 --- a/docs/commands/claude/index.md +++ b/docs/commands/claude/index.md @@ -12,7 +12,7 @@ | **Workflow** | 20+ | Planning, execution, review, TDD, testing workflows | | **Session Management** | 6 | Session creation, listing, resuming, completion | | **Issue Workflow** | 8 | Issue discovery, planning, queue, execution | -| **IDAW** | 4 | Batch autonomous task execution with git checkpoints | +| **IDAW** | 5 | Batch autonomous task execution with git checkpoints | | **Memory** | 8 | Memory capture, update, document generation | | **CLI Tools** | 2 | CLI initialization, Codex review | | **UI Design** | 10 | UI design prototype generation, style extraction | @@ -76,6 +76,7 @@ |---------|----------|------------| | [`/idaw:add`](./idaw.md#add) | Create tasks manually or import from ccw issue | Beginner | | [`/idaw:run`](./idaw.md#run) | Execute task queue with skill chains and git checkpoints | Intermediate | +| [`/idaw:run-coordinate`](./idaw.md#run-coordinate) | Execute via external CLI with hook callbacks | Intermediate | | [`/idaw:status`](./idaw.md#status) | View task and session progress | Beginner | | [`/idaw:resume`](./idaw.md#resume) | Resume interrupted session from last checkpoint | Intermediate | diff --git a/docs/zh/commands/claude/idaw.md b/docs/zh/commands/claude/idaw.md index ace0c646..a1776b62 100644 --- a/docs/zh/commands/claude/idaw.md +++ b/docs/zh/commands/claude/idaw.md @@ -29,6 +29,7 @@ |------|------|------| | [`add`](#add) | 手动创建或从 issue 导入任务 | `/idaw:add [-y] [--from-issue ] "描述" [--type <类型>] [--priority 1-5]` | | [`run`](#run) | 串行执行任务队列并 git checkpoint | `/idaw:run [-y] [--task ] [--dry-run]` | +| [`run-coordinate`](#run-coordinate) | 通过外部 CLI 和 hook 回调执行 | `/idaw:run-coordinate [-y] [--task ] [--tool ]` | | [`status`](#status) | 查看任务和会话进度 | `/idaw:status [session-id]` | | [`resume`](#resume) | 从断点恢复中断的会话 | `/idaw:resume [-y] [session-id]` | @@ -128,6 +129,69 @@ Phase 6: 报告 --- +### run-coordinate + +**功能**:`/idaw:run` 的协调器变体 — 通过外部 CLI 后台执行,使用 hook 回调驱动流程推进。 + +**语法**: +```bash +/idaw:run-coordinate [-y|--yes] [--task [,,...]] [--dry-run] [--tool ] +``` + +**参数**: +- `--tool `:使用的 CLI 工具(`claude`、`gemini`、`qwen`,默认:`claude`) +- `--task `:执行特定任务 +- `--dry-run`:预览计划,不实际执行 +- `-y`:自动模式 + +**执行模型**: + +``` +通过 ccw cli --tool --mode write 后台启动 Skill + ↓ +★ 暂停 — 等待 hook 回调 + ↓ +Hook 触发 → handleStepCompletion() + ├─ 链中还有 Skill → 启动下一个 → 暂停 + ├─ 链完成 → git checkpoint → 下一个任务 → 暂停 + └─ 全部完成 → 生成报告 +``` + +**适用场景**: + +| 场景 | 命令 | +|------|------| +| 标准执行(主进程,阻塞) | `/idaw:run` | +| 外部 CLI,每个 Skill 独立上下文 | `/idaw:run-coordinate` | +| 长时间任务,避免上下文压力 | `/idaw:run-coordinate` | +| 需要指定 CLI 工具 | `/idaw:run-coordinate --tool gemini` | + +**与 `/idaw:run` 的区别**: + +| 对比维度 | `/idaw:run` | `/idaw:run-coordinate` | +|----------|-------------|----------------------| +| 执行方式 | `Skill()` 阻塞调用 | `ccw cli` 后台 + hook | +| 上下文 | 共享主进程上下文 | 每次 CLI 调用独立上下文 | +| 工具选择 | N/A | `--tool claude\|gemini\|qwen` | +| 状态追踪 | session.json | session.json + prompts_used | + +**示例**: +```bash +# 通过 claude CLI 执行(默认) +/idaw:run-coordinate -y + +# 使用 gemini 作为执行工具 +/idaw:run-coordinate -y --tool gemini + +# 执行特定任务 +/idaw:run-coordinate --task IDAW-001,IDAW-003 --tool claude + +# 预览计划 +/idaw:run-coordinate --dry-run +``` + +--- + ### status **功能**:只读查看 IDAW 任务队列和会话进度。 diff --git a/docs/zh/commands/claude/index.md b/docs/zh/commands/claude/index.md index e9c95102..7ef1a727 100644 --- a/docs/zh/commands/claude/index.md +++ b/docs/zh/commands/claude/index.md @@ -74,6 +74,7 @@ | --- | --- | --- | | [`/idaw:add`](./idaw.md#add) | 手动创建或从 issue 导入任务 | Beginner | | [`/idaw:run`](./idaw.md#run) | 串行执行任务 Skill 链并 git checkpoint | Intermediate | +| [`/idaw:run-coordinate`](./idaw.md#run-coordinate) | 通过外部 CLI 和 hook 回调执行任务 | Intermediate | | [`/idaw:status`](./idaw.md#status) | 查看任务和会话进度 | Beginner | | [`/idaw:resume`](./idaw.md#resume) | 从断点恢复中断的会话 | Intermediate |