From e727a07fc5ddd009effe48789678f281292e4476 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Fri, 23 Jan 2026 23:39:16 +0800 Subject: [PATCH] feat: Implement CCW Coordinator for interactive command orchestration - Add action files for session management, command selection, building, execution, and completion. - Introduce orchestrator logic to drive state transitions and action selection. - Create state schema to define session state structure. - Develop command registry and validation tools for command metadata extraction and chain validation. - Establish skill configuration and specifications for command library and validation rules. - Implement tools for command registry and chain validation with CLI support. --- .claude/skills/ccw-coordinator/README.md | 45 +++ .claude/skills/ccw-coordinator/SKILL.md | 320 ++++++++++++++++ .../phases/actions/action-abort.md | 9 + .../phases/actions/action-command-build.md | 40 ++ .../phases/actions/action-command-execute.md | 124 ++++++ .../actions/action-command-selection.md | 48 +++ .../phases/actions/action-complete.md | 25 ++ .../phases/actions/action-init.md | 25 ++ .../ccw-coordinator/phases/orchestrator.md | 59 +++ .../ccw-coordinator/phases/state-schema.md | 66 ++++ .../skills/ccw-coordinator/skill-config.json | 66 ++++ .claude/skills/ccw-coordinator/specs/specs.md | 362 ++++++++++++++++++ .../skills/ccw-coordinator/tools/README.md | 77 ++++ .../ccw-coordinator/tools/chain-validate.js | 281 ++++++++++++++ .../ccw-coordinator/tools/command-registry.js | 181 +++++++++ 15 files changed, 1728 insertions(+) create mode 100644 .claude/skills/ccw-coordinator/README.md create mode 100644 .claude/skills/ccw-coordinator/SKILL.md create mode 100644 .claude/skills/ccw-coordinator/phases/actions/action-abort.md create mode 100644 .claude/skills/ccw-coordinator/phases/actions/action-command-build.md create mode 100644 .claude/skills/ccw-coordinator/phases/actions/action-command-execute.md create mode 100644 .claude/skills/ccw-coordinator/phases/actions/action-command-selection.md create mode 100644 .claude/skills/ccw-coordinator/phases/actions/action-complete.md create mode 100644 .claude/skills/ccw-coordinator/phases/actions/action-init.md create mode 100644 .claude/skills/ccw-coordinator/phases/orchestrator.md create mode 100644 .claude/skills/ccw-coordinator/phases/state-schema.md create mode 100644 .claude/skills/ccw-coordinator/skill-config.json create mode 100644 .claude/skills/ccw-coordinator/specs/specs.md create mode 100644 .claude/skills/ccw-coordinator/tools/README.md create mode 100644 .claude/skills/ccw-coordinator/tools/chain-validate.js create mode 100644 .claude/skills/ccw-coordinator/tools/command-registry.js diff --git a/.claude/skills/ccw-coordinator/README.md b/.claude/skills/ccw-coordinator/README.md new file mode 100644 index 00000000..f2a4fcaf --- /dev/null +++ b/.claude/skills/ccw-coordinator/README.md @@ -0,0 +1,45 @@ +# CCW Coordinator + +交互式命令编排工具 + +## 使用 + +``` +/ccw-coordinator +或 +/coordinator +``` + +## 流程 + +1. 用户描述任务 +2. Claude推荐命令链 +3. 用户确认或调整 +4. 执行命令链 +5. 生成报告 + +## 示例 + +**Bug修复** +``` +任务: 修复登录bug +推荐: lite-fix → test-cycle-execute +``` + +**新功能** +``` +任务: 实现注册功能 +推荐: plan → execute → test-cycle-execute +``` + +## 文件说明 + +| 文件 | 用途 | +|------|------| +| SKILL.md | Skill入口 | +| phases/orchestrator.md | 编排逻辑 | +| phases/state-schema.md | 状态定义 | +| phases/actions/*.md | 动作实现 | +| specs/chain-registry.json | 命令元数据 | +| specs/chain-validation-rules.md | 验证规则 | +| tools/chain-validate.js | 验证工具 | diff --git a/.claude/skills/ccw-coordinator/SKILL.md b/.claude/skills/ccw-coordinator/SKILL.md new file mode 100644 index 00000000..a934f60e --- /dev/null +++ b/.claude/skills/ccw-coordinator/SKILL.md @@ -0,0 +1,320 @@ +--- +name: ccw-coordinator +description: Interactive command orchestration tool for building and executing Claude CLI command chains. Triggers on "coordinator", "ccw-coordinator", "命令编排", "command chain", "orchestrate commands", "编排CLI命令". +allowed-tools: Task, AskUserQuestion, Read, Write, Bash, Glob, Grep +--- + +# CCW Coordinator + +交互式命令编排工具:允许用户依次选择命令,形成命令串,然后依次调用claude cli执行整个命令串。 + +支持灵活的工作流组合,提供交互式界面用于命令选择、编排和执行管理。 + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Orchestrator (状态驱动决策) │ +│ 根据用户选择编排命令和执行流程 │ +└───────────────┬─────────────────────────────────────────────────┘ + │ + ┌───────────┼───────────┬───────────────┐ + ↓ ↓ ↓ ↓ +┌─────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────┐ +│ Init │ │ Command │ │ Command │ │ Execute │ +│ │ │ Selection │ │ Build │ │ │ +│ │ │ │ │ │ │ │ +│ 初始化 │ │ 选择命令 │ │ 编排调整 │ │ 执行链 │ +└─────────┘ └──────────────┘ └────────────┘ └──────────┘ + │ │ │ │ + └───────────────┼──────────────┴────────────┘ + │ + ↓ + ┌──────────────┐ + │ Complete │ + │ 生成报告 │ + └──────────────┘ +``` + +## Key Design Principles + +1. **智能推荐**: Claude 根据用户任务描述,自动推荐最优命令链 +2. **交互式编排**: 用户通过交互式界面选择和编排命令,实时反馈 +3. **无状态动作**: 每个动作独立执行,通过共享状态进行通信 +4. **灵活的命令库**: 支持ccw workflow命令和标准claude cli命令 +5. **执行透明性**: 展示执行进度、结果和可能的错误 +6. **会话持久化**: 保存编排会话,支持中途暂停和恢复 +7. **智能提示词生成**: 根据任务上下文和前序产物自动生成 ccw cli 提示词 + +## Intelligent Prompt Generation + +执行命令时,系统根据以下信息智能生成 `ccw cli -p` 提示词: + +### 提示词构成 + +```javascript +// 集成命令注册表 (~/.claude/tools/command-registry.js) +const registry = new CommandRegistry(); +registry.buildRegistry(); + +function generatePrompt(cmd, state) { + const cmdMeta = registry.getCommand(cmd.command); + + let prompt = `任务: ${state.task_description}\n`; + + if (state.execution_results.length > 0) { + const previousOutputs = state.execution_results + .filter(r => r.status === 'success') + .map(r => { + if (r.summary?.session) { + return `- ${r.command}: ${r.summary.session} (${r.summary.files?.join(', ')})`; + } + return `- ${r.command}: 已完成`; + }) + .join('\n'); + + prompt += `\n前序完成:\n${previousOutputs}\n`; + } + + // 从 YAML 头提取命令元数据 + if (cmdMeta) { + prompt += `\n命令: ${cmd.command}`; + if (cmdMeta.argumentHint) { + prompt += ` ${cmdMeta.argumentHint}`; + } + } + + return prompt; +} +``` + +### 产物追踪 + +每个命令执行后自动提取关键产物: + +```javascript +{ + command: "/workflow:lite-plan", + status: "success", + output: "...", + summary: { + session: "WFS-plan-20250123", // 会话ID + files: [".workflow/IMPL_PLAN.md"], // 产物文件 + timestamp: "2025-01-23T10:30:00Z" + } +} +``` + +### 命令调用示例 + +```bash +# 自动生成的智能提示词 +ccw cli -p "任务: 实现用户认证功能 + +前序完成: +- /workflow:lite-plan: WFS-plan-20250123 (.workflow/IMPL_PLAN.md) + +命令: /workflow:lite-execute [--resume-session=\"session-id\"]" /workflow:lite-execute +``` + +### 命令注册表集成 + +- **位置**: `tools/command-registry.js` (skill 内置) +- **工作模式**: 按需提取(只提取用户任务链中的命令) +- **功能**: 自动查找全局 `.claude/commands/workflow` 目录,解析命令 YAML 头元数据 +- **作用**: 确保提示词包含准确的命令参数和上下文 + +详见 `tools/README.md` + +--- + +## Execution Flow + +### Orchestrator Execution Loop + +```javascript +1. 初始化会话 + ↓ +2. 循环执行直到完成 + ├─ 读取当前状态 + ├─ 选择下一个动作(根据状态和用户意图) + ├─ 执行动作,更新状态 + └─ 检查终止条件 + ↓ +3. 生成最终报告 +``` + +### Action Sequence (Typical) + +``` +action-init + ↓ (status: pending → running) +action-command-selection (可重复) + ↓ (添加命令到链) +action-command-build (可选) + ↓ (调整命令顺序) +action-command-execute + ↓ (依次执行所有命令) +action-complete + ↓ (status: running → completed) +``` + +## State Management + +### Initial State + +```json +{ + "status": "pending", + "task_description": "", + "command_chain": [], + "confirmed": false, + "error_count": 0, + "execution_results": [], + "current_command_index": 0, + "started_at": null +} +``` + +### State Transitions + +``` +pending → running (init) → running → completed (execute) + ↓ + aborted (error or user exit) +``` + +## Directory Setup + +```javascript +const timestamp = new Date().toISOString().slice(0,19).replace(/[-:T]/g, ''); +const workDir = `.workflow/.ccw-coordinator/${timestamp}`; + +Bash(`mkdir -p "${workDir}"`); +Bash(`mkdir -p "${workDir}/commands"`); +Bash(`mkdir -p "${workDir}/logs"`); +``` + +## Output Structure + +``` +.workflow/.ccw-coordinator/{timestamp}/ +├── state.json # 当前会话状态 +├── command-chain.json # 编排的完整命令链 +├── execution-log.md # 执行日志 +├── final-summary.md # 最终报告 +├── commands/ # 各命令执行详情 +│ ├── 01-command.log +│ ├── 02-command.log +│ └── ... +└── logs/ # 错误和警告日志 + ├── errors.log + └── warnings.log +``` + +## Reference Documents + +| Document | Purpose | +|----------|---------| +| [phases/orchestrator.md](phases/orchestrator.md) | 编排器实现 | +| [phases/state-schema.md](phases/state-schema.md) | 状态结构定义 | +| [phases/actions/action-init.md](phases/actions/action-init.md) | 初始化动作 | +| [phases/actions/action-command-selection.md](phases/actions/action-command-selection.md) | 命令选择动作 | +| [phases/actions/action-command-build.md](phases/actions/action-command-build.md) | 命令编排动作 | +| [phases/actions/action-command-execute.md](phases/actions/action-command-execute.md) | 命令执行动作 | +| [phases/actions/action-complete.md](phases/actions/action-complete.md) | 完成动作 | +| [phases/actions/action-abort.md](phases/actions/action-abort.md) | 中止动作 | +| [specs/command-library.md](specs/command-library.md) | 命令库 | +| [specs/chain-registry.json](specs/chain-registry.json) | 命令元数据 | +| [specs/chain-validation-rules.md](specs/chain-validation-rules.md) | 验证规则 | +| [tools/chain-validate.js](tools/chain-validate.js) | 验证工具 | + +--- + +## Usage Examples + +### 快速命令链 + +用户想要执行:规划 → 执行 → 测试 + +``` +1. 触发 /ccw-coordinator +2. 描述任务:"实现用户注册功能" +3. Claude推荐: plan → execute → test-cycle-execute +4. 用户确认 +5. 执行命令链 +``` + +### 复杂工作流 + +用户想要执行:规划 → 执行 → 审查 → 修复 + +``` +1. 触发 /ccw-coordinator +2. 描述任务:"重构认证模块" +3. Claude推荐: plan → execute → review-session-cycle → review-fix +4. 用户可调整命令顺序 +5. 确认执行 +6. 实时查看执行进度 +``` + +### 紧急修复 + +用户想要快速修复bug + +``` +1. 触发 /ccw-coordinator +2. 描述任务:"修复生产环境登录bug" +3. Claude推荐: lite-fix --hotfix → test-cycle-execute +4. 用户确认 +5. 快速执行修复 +``` + +## Constraints and Rules + +### 1. 命令推荐约束 + +- **智能推荐优先**: 必须先基于用户任务描述进行智能推荐,而非直接展示命令库 +- **不使用静态映射**: 禁止使用查表或硬编码的推荐逻辑(如 `if task=bug则推荐lite-fix`) +- **推荐必须说明理由**: Claude 推荐命令链时必须解释为什么这样推荐 +- **用户保留选择权**: 推荐后,用户可选择:使用推荐/调整/手动选择 + +### 2. 验证约束 + +- **执行前必须验证**: 使用 `chain-validate.js` 验证命令链合法性 +- **不合法必须提示**: 如果验证失败,必须明确告知用户错误原因和修复方法 +- **允许用户覆盖**: 验证失败时,询问用户是否仍要执行(警告模式) + +### 3. 执行约束 + +- **顺序执行**: 命令必须严格按照 command_chain 中的 order 顺序执行 +- **错误处理**: 单个命令失败时,询问用户:重试/跳过/中止 +- **错误上限**: 连续 3 次错误自动中止会话 +- **实时反馈**: 每个命令执行时显示进度(如 `[2/5] 执行: lite-execute`) + +### 4. 状态管理约束 + +- **状态持久化**: 每次状态更新必须立即写入磁盘 +- **单一数据源**: 状态只保存在 `state.json`,禁止多个状态文件 +- **原子操作**: 状态更新必须使用 read-modify-write 模式,避免并发冲突 + +### 5. 用户体验约束 + +- **最小交互**: 默认使用智能推荐 + 一次确认,避免多次询问 +- **清晰输出**: 每个步骤输出必须包含:当前状态、可用选项、建议操作 +- **可恢复性**: 会话中断后,用户可从上次状态恢复 + +### 6. 禁止行为 + +- ❌ **禁止跳过推荐步骤**: 不能直接进入手动选择,必须先尝试推荐 +- ❌ **禁止静态推荐**: 不能使用 recommended-chains.json 查表 +- ❌ **禁止无验证执行**: 不能跳过链条验证直接执行 +- ❌ **禁止静默失败**: 错误必须明确报告,不能静默跳过 + +## Notes + +- 编排器使用状态机模式,确保执行流程的可靠性 +- 所有命令链和执行结果都被持久化保存,支持后续查询和调试 +- 支持用户中途修改命令链(在执行前) +- 执行错误会自动记录,支持重试机制 +- Claude 智能推荐基于任务分析,非查表静态推荐 diff --git a/.claude/skills/ccw-coordinator/phases/actions/action-abort.md b/.claude/skills/ccw-coordinator/phases/actions/action-abort.md new file mode 100644 index 00000000..d1b54dab --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/actions/action-abort.md @@ -0,0 +1,9 @@ +# action-abort + +中止会话,保存状态 + +```javascript +updateState({ status: 'aborted' }); + +console.log(`会话已中止: ${workDir}`); +``` diff --git a/.claude/skills/ccw-coordinator/phases/actions/action-command-build.md b/.claude/skills/ccw-coordinator/phases/actions/action-command-build.md new file mode 100644 index 00000000..a71e9989 --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/actions/action-command-build.md @@ -0,0 +1,40 @@ +# action-command-build + +调整命令链顺序或删除命令 + +## 流程 + +1. 显示当前命令链 +2. 让用户调整(重新排序、删除) +3. 确认执行 + +## 伪代码 + +```javascript +// 显示链 +console.log('命令链:'); +state.command_chain.forEach((cmd, i) => { + console.log(`${i+1}. ${cmd.command}`); +}); + +// 询问用户 +const action = await AskUserQuestion({ + options: [ + '继续执行', + '删除命令', + '重新排序', + '返回选择' + ] +}); + +// 处理用户操作 +if (action === '继续执行') { + updateState({confirmed: true, status: 'executing'}); +} +// ... 其他操作 +``` + +## 状态更新 + +- command_chain (可能修改) +- confirmed = true 时状态转为 executing diff --git a/.claude/skills/ccw-coordinator/phases/actions/action-command-execute.md b/.claude/skills/ccw-coordinator/phases/actions/action-command-execute.md new file mode 100644 index 00000000..387791f6 --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/actions/action-command-execute.md @@ -0,0 +1,124 @@ +# action-command-execute + +依次执行命令链,智能生成 ccw cli 提示词 + +## 命令注册表集成 + +```javascript +// 从 ./tools/command-registry.js 按需提取命令元数据 +const CommandRegistry = require('./tools/command-registry.js'); +const registry = new CommandRegistry(); + +// 只提取当前任务链中的命令 +const commandNames = command_chain.map(cmd => cmd.command); +const commandMeta = registry.getCommands(commandNames); +``` + +## 提示词生成策略 + +```javascript +function generatePrompt(cmd, state, commandMeta) { + const { task_description, execution_results } = state; + + // 获取命令元数据(从已提取的 commandMeta) + const cmdInfo = commandMeta[cmd.command]; + + // 提取前序产物信息 + const previousOutputs = execution_results + .filter(r => r.status === 'success') + .map(r => { + const summary = r.summary; + if (summary?.session) { + return `- ${r.command}: ${summary.session} (${summary.files?.join(', ') || '完成'})`; + } + return `- ${r.command}: 已完成`; + }) + .join('\n'); + + // 根据命令类型构建提示词 + let prompt = `任务: ${task_description}\n`; + + if (previousOutputs) { + prompt += `\n前序完成:\n${previousOutputs}\n`; + } + + // 添加命令元数据上下文 + if (cmdInfo) { + prompt += `\n命令: ${cmd.command}`; + if (cmdInfo.argumentHint) { + prompt += ` ${cmdInfo.argumentHint}`; + } + } + + return prompt; +} +``` + +## 执行逻辑 + +```javascript +for (let i = current_command_index; i < command_chain.length; i++) { + const cmd = command_chain[i]; + + console.log(`[${i+1}/${command_chain.length}] 执行: ${cmd.command}`); + + // 生成智能提示词 + const prompt = generatePrompt(cmd, state, commandMeta); + + try { + // 使用 ccw cli 执行 + const result = Bash(`ccw cli -p "${prompt.replace(/"/g, '\\"')}" ${cmd.command}`, { + run_in_background: true + }); + + execution_results.push({ + command: cmd.command, + status: result.exit_code === 0 ? 'success' : 'failed', + exit_code: result.exit_code, + output: result.stdout, + summary: extractSummary(result.stdout) // 提取关键产物 + }); + + command_chain[i].status = 'completed'; + current_command_index = i + 1; + + } catch (error) { + error_count++; + command_chain[i].status = 'failed'; + + if (error_count >= 3) break; + + const action = await AskUserQuestion({ + options: ['重试', '跳过', '中止'] + }); + + if (action === '重试') i--; + if (action === '中止') break; + } + + updateState({ command_chain, execution_results, current_command_index, error_count }); +} +``` + +## 产物提取 + +```javascript +function extractSummary(output) { + // 从输出提取关键产物信息 + // 例如: 会话ID, 文件路径, 任务完成状态等 + const sessionMatch = output.match(/WFS-\w+-\d+/); + const fileMatch = output.match(/\.workflow\/[^\s]+/g); + + return { + session: sessionMatch?.[0], + files: fileMatch || [], + timestamp: new Date().toISOString() + }; +} +``` + +## 状态更新 + +- execution_results (包含 summary 产物信息) +- command_chain[].status +- current_command_index diff --git a/.claude/skills/ccw-coordinator/phases/actions/action-command-selection.md b/.claude/skills/ccw-coordinator/phases/actions/action-command-selection.md new file mode 100644 index 00000000..35cc1d2e --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/actions/action-command-selection.md @@ -0,0 +1,48 @@ +# action-command-selection + +## 流程 + +1. 问用户任务 +2. Claude推荐命令链 +3. 用户确认/手动选择 +4. 添加到command_chain + +## 伪代码 + +```javascript +// 1. 获取用户任务描述 +const taskInput = await AskUserQuestion({ + question: '请描述您的任务', + options: [ + { label: '手动选择命令', value: 'manual' } + ] +}); + +// 保存任务描述到状态 +updateState({ task_description: taskInput.text || taskInput.value }); + +// 2. 若用户描述任务,Claude推荐 +if (taskInput.text) { + console.log('推荐: ', recommendChain(taskInput.text)); + const confirm = await AskUserQuestion({ + question: '是否使用推荐链?', + options: ['使用推荐', '调整', '手动选择'] + }); + if (confirm === '使用推荐') { + addCommandsToChain(recommendedChain); + updateState({ confirmed: true }); + return; + } +} + +// 3. 手动选择 +const commands = loadCommandLibrary(); +const selected = await AskUserQuestion(commands); +addToChain(selected); +``` + +## 状态更新 + +- task_description = 用户任务描述 +- command_chain.push(newCommand) +- 如果用户确认: confirmed = true diff --git a/.claude/skills/ccw-coordinator/phases/actions/action-complete.md b/.claude/skills/ccw-coordinator/phases/actions/action-complete.md new file mode 100644 index 00000000..3d005289 --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/actions/action-complete.md @@ -0,0 +1,25 @@ +# action-complete + +生成执行报告 + +```javascript +const success = execution_results.filter(r => r.status === 'success').length; +const failed = execution_results.filter(r => r.status === 'failed').length; +const duration = Date.now() - new Date(started_at).getTime(); + +const report = ` +# 执行报告 + +- 会话: ${session_id} +- 耗时: ${Math.round(duration/1000)}s +- 成功: ${success} +- 失败: ${failed} + +## 命令详情 + +${command_chain.map((c, i) => `${i+1}. ${c.command} - ${c.status}`).join('\n')} +`; + +Write(`${workDir}/final-report.md`, report); +updateState({ status: 'completed' }); +``` diff --git a/.claude/skills/ccw-coordinator/phases/actions/action-init.md b/.claude/skills/ccw-coordinator/phases/actions/action-init.md new file mode 100644 index 00000000..1a4ca211 --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/actions/action-init.md @@ -0,0 +1,25 @@ +# action-init + +初始化编排会话 + +```javascript +const timestamp = Date.now(); +const workDir = `.workflow/.ccw-coordinator/${timestamp}`; + +Bash(`mkdir -p "${workDir}"`); + +const state = { + session_id: `coord-${timestamp}`, + status: 'running', + started_at: new Date().toISOString(), + command_chain: [], + current_command_index: 0, + execution_results: [], + confirmed: false, + error_count: 0 +}; + +Write(`${workDir}/state.json`, JSON.stringify(state, null, 2)); + +console.log(`会话已初始化: ${workDir}`); +``` diff --git a/.claude/skills/ccw-coordinator/phases/orchestrator.md b/.claude/skills/ccw-coordinator/phases/orchestrator.md new file mode 100644 index 00000000..dc0614b7 --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/orchestrator.md @@ -0,0 +1,59 @@ +# Orchestrator + +状态驱动编排:读状态 → 选动作 → 执行 → 更新状态 + +## 决策逻辑 + +```javascript +function selectNextAction(state) { + if (['completed', 'aborted'].includes(state.status)) return null; + if (state.error_count >= 3) return 'action-abort'; + + switch (state.status) { + case 'pending': + return 'action-init'; + case 'running': + return state.confirmed && state.command_chain.length > 0 + ? 'action-command-execute' + : 'action-command-selection'; + case 'executing': + const pending = state.command_chain.filter(c => c.status === 'pending'); + return pending.length === 0 ? 'action-complete' : 'action-command-execute'; + default: + return 'action-abort'; + } +} +``` + +## 执行循环 + +```javascript +const timestamp = Date.now(); +const workDir = `.workflow/.ccw-coordinator/${timestamp}`; +Bash(`mkdir -p "${workDir}"`); + +const state = { + session_id: `coord-${timestamp}`, + status: 'pending', + started_at: new Date().toISOString(), + task_description: '', // 从 action-command-selection 获取 + command_chain: [], + current_command_index: 0, + execution_results: [], + confirmed: false, + error_count: 0 +}; +Write(`${workDir}/state.json`, JSON.stringify(state, null, 2)); + +let iterations = 0; +while (iterations < 50) { + const state = JSON.parse(Read(`${workDir}/state.json`)); + const nextAction = selectNextAction(state); + if (!nextAction) break; + + console.log(`[${nextAction}]`); + // 执行 phases/actions/{nextAction}.md + + iterations++; +} +``` diff --git a/.claude/skills/ccw-coordinator/phases/state-schema.md b/.claude/skills/ccw-coordinator/phases/state-schema.md new file mode 100644 index 00000000..95bf3f07 --- /dev/null +++ b/.claude/skills/ccw-coordinator/phases/state-schema.md @@ -0,0 +1,66 @@ +# State Schema + +```typescript +interface State { + session_id: string; + status: 'pending' | 'running' | 'executing' | 'completed' | 'aborted'; + started_at: string; + task_description: string; // 用户任务描述 + command_chain: Command[]; + current_command_index: number; + execution_results: ExecutionResult[]; + confirmed: boolean; + error_count: number; +} + +interface Command { + id: string; + order: number; + command: string; + status: 'pending' | 'running' | 'completed' | 'failed'; + result?: ExecutionResult; +} + +interface ExecutionResult { + command: string; + status: 'success' | 'failed'; + exit_code: number; + output?: string; + summary?: { // 提取的关键产物 + session?: string; + files?: string[]; + timestamp: string; + }; +} +``` + +## 状态转移 + +``` +pending → running → executing → completed + ↓ ↓ + (abort) (error → abort) +``` + +## 初始化 + +```javascript +{ + session_id: generateId(), + status: 'pending', + started_at: new Date().toISOString(), + task_description: '', // 从用户输入获取 + command_chain: [], + current_command_index: 0, + execution_results: [], + confirmed: false, + error_count: 0 +} +``` + +## 更新 + +- 添加命令: `command_chain.push(cmd)` +- 确认执行: `confirmed = true, status = 'executing'` +- 记录执行: `execution_results.push(...), current_command_index++` +- 错误计数: `error_count++` diff --git a/.claude/skills/ccw-coordinator/skill-config.json b/.claude/skills/ccw-coordinator/skill-config.json new file mode 100644 index 00000000..70c4f9e8 --- /dev/null +++ b/.claude/skills/ccw-coordinator/skill-config.json @@ -0,0 +1,66 @@ +{ + "skill_name": "ccw-coordinator", + "display_name": "CCW Coordinator", + "description": "Interactive command orchestration - select, build, and execute workflow command chains", + "execution_mode": "autonomous", + "version": "1.0.0", + "triggers": [ + "coordinator", + "ccw-coordinator", + "命令编排", + "command chain" + ], + "allowed_tools": [ + "Task", + "AskUserQuestion", + "Read", + "Write", + "Bash" + ], + "actions": [ + { + "id": "action-init", + "name": "Init", + "description": "Initialize orchestration session" + }, + { + "id": "action-command-selection", + "name": "Select Commands", + "description": "Interactive command selection from library" + }, + { + "id": "action-command-build", + "name": "Build Chain", + "description": "Adjust and confirm command chain" + }, + { + "id": "action-command-execute", + "name": "Execute", + "description": "Execute command chain sequentially" + }, + { + "id": "action-complete", + "name": "Complete", + "description": "Generate final report" + }, + { + "id": "action-abort", + "name": "Abort", + "description": "Abort session and save state" + } + ], + "termination_conditions": [ + "user_exit", + "task_completed", + "error" + ], + "output": { + "location": ".workflow/.ccw-coordinator/{timestamp}", + "artifacts": [ + "state.json", + "command-chain.json", + "execution-log.md", + "final-report.md" + ] + } +} diff --git a/.claude/skills/ccw-coordinator/specs/specs.md b/.claude/skills/ccw-coordinator/specs/specs.md new file mode 100644 index 00000000..882eaab1 --- /dev/null +++ b/.claude/skills/ccw-coordinator/specs/specs.md @@ -0,0 +1,362 @@ +# CCW Coordinator Specifications + +命令库、验证规则和注册表一体化规范。 + +--- + +## 命令库 + +### Planning Commands + +| Command | Description | Level | +|---------|-------------|-------| +| `/workflow:lite-plan` | 轻量级规划 | L2 | +| `/workflow:plan` | 标准规划 | L3 | +| `/workflow:multi-cli-plan` | 多CLI协作规划 | L2 | +| `/workflow:brainstorm:auto-parallel` | 头脑风暴规划 | L4 | +| `/workflow:tdd-plan` | TDD规划 | L3 | + +### Execution Commands + +| Command | Description | Level | +|---------|-------------|-------| +| `/workflow:lite-execute` | 轻量级执行 | L2 | +| `/workflow:execute` | 标准执行 | L3 | +| `/workflow:test-cycle-execute` | 测试循环执行 | L3 | + +### BugFix Commands + +| Command | Description | Level | +|---------|-------------|-------| +| `/workflow:lite-fix` | 轻量级修复 | L2 | +| `/workflow:lite-fix --hotfix` | 紧急修复 | L2 | + +### Testing Commands + +| Command | Description | Level | +|---------|-------------|-------| +| `/workflow:test-gen` | 测试生成 | L3 | +| `/workflow:test-fix-gen` | 测试修复生成 | L3 | +| `/workflow:tdd-verify` | TDD验证 | L3 | + +### Review Commands + +| Command | Description | Level | +|---------|-------------|-------| +| `/workflow:review-session-cycle` | 会话审查 | L3 | +| `/workflow:review-module-cycle` | 模块审查 | L3 | +| `/workflow:review-fix` | 审查修复 | L3 | +| `/workflow:action-plan-verify` | 计划验证 | L3 | + +### Documentation Commands + +| Command | Description | Level | +|---------|-------------|-------| +| `/memory:docs` | 生成文档 | L2 | +| `/memory:update-related` | 更新相关文档 | L2 | +| `/memory:update-full` | 全面更新文档 | L2 | + +### Issue Commands + +| Command | Description | +|---------|-------------| +| `/issue:discover` | 发现Issue | +| `/issue:discover-by-prompt` | 基于提示发现Issue | +| `/issue:plan --all-pending` | 规划所有待处理Issue | +| `/issue:queue` | 排队Issue | +| `/issue:execute` | 执行Issue | + +--- + +## 命令链推荐 + +### 标准开发流程 + +``` +1. /workflow:lite-plan +2. /workflow:lite-execute +3. /workflow:test-cycle-execute +``` + +### 完整规划流程 + +``` +1. /workflow:plan +2. /workflow:action-plan-verify +3. /workflow:execute +4. /workflow:review-session-cycle +``` + +### TDD 流程 + +``` +1. /workflow:tdd-plan +2. /workflow:execute +3. /workflow:tdd-verify +``` + +### Issue 批处理流程 + +``` +1. /issue:plan --all-pending +2. /issue:queue +3. /issue:execute +``` + +--- + +## 验证规则 + +### Rule 1: Single Planning Command + +每条链最多包含一个规划命令。 + +| 有效 | 无效 | +|------|------| +| `plan → execute` | `plan → lite-plan → execute` | + +### Rule 2: Compatible Pairs + +规划和执行命令必须兼容。 + +| Planning | Execution | 兼容 | +|----------|-----------|------| +| lite-plan | lite-execute | ✓ | +| lite-plan | execute | ✗ | +| multi-cli-plan | lite-execute | ✓ | +| multi-cli-plan | execute | ✓ | +| plan | execute | ✓ | +| plan | lite-execute | ✗ | +| tdd-plan | execute | ✓ | +| tdd-plan | lite-execute | ✗ | + +### Rule 3: Testing After Execution + +测试命令必须在执行命令之后。 + +| 有效 | 无效 | +|------|------| +| `execute → test-cycle-execute` | `test-cycle-execute → execute` | + +### Rule 4: Review After Execution + +审查命令必须在执行命令之后。 + +| 有效 | 无效 | +|------|------| +| `execute → review-session-cycle` | `review-session-cycle → execute` | + +### Rule 5: BugFix Standalone + +`lite-fix` 必须单独执行,不能与其他命令组合。 + +| 有效 | 无效 | +|------|------| +| `lite-fix` | `plan → lite-fix → execute` | +| `lite-fix --hotfix` | `lite-fix → test-cycle-execute` | + +### Rule 6: Dependency Satisfaction + +每个命令的依赖必须在前面执行。 + +```javascript +test-fix-gen → test-cycle-execute ✓ +test-cycle-execute ✗ +``` + +### Rule 7: No Redundancy + +链条中不能有重复的命令。 + +| 有效 | 无效 | +|------|------| +| `plan → execute → test` | `plan → plan → execute` | + +### Rule 8: Command Exists + +所有命令必须在此规范中定义。 + +--- + +## 反模式(避免) + +### ❌ Pattern 1: Multiple Planning + +``` +plan → lite-plan → execute +``` +**问题**: 重复分析,浪费时间 +**修复**: 选一个规划命令 + +### ❌ Pattern 2: Test Without Context + +``` +test-cycle-execute (独立执行) +``` +**问题**: 没有执行上下文,无法工作 +**修复**: 先执行 `execute` 或 `test-fix-gen` + +### ❌ Pattern 3: BugFix with Planning + +``` +plan → execute → lite-fix +``` +**问题**: lite-fix 是独立命令,不应与规划混合 +**修复**: 用 `lite-fix` 单独修复,或用 `plan → execute` 做大改 + +### ❌ Pattern 4: Review Without Changes + +``` +review-session-cycle (独立执行) +``` +**问题**: 没有 git 改动可审查 +**修复**: 先执行 `execute` 生成改动 + +### ❌ Pattern 5: TDD Misuse + +``` +tdd-plan → lite-execute +``` +**问题**: lite-execute 无法处理 TDD 任务结构 +**修复**: 用 `tdd-plan → execute → tdd-verify` + +--- + +## 命令注册表 + +### 命令元数据结构 + +```json +{ + "command_name": { + "category": "Planning|Execution|Testing|Review|BugFix|Maintenance", + "level": "L0|L1|L2|L3", + "description": "命令描述", + "inputs": ["input1", "input2"], + "outputs": ["output1", "output2"], + "dependencies": ["依赖命令"], + "parameters": [ + {"name": "--flag", "type": "string|boolean|number", "default": "value"} + ], + "chain_position": "start|middle|middle_or_end|end|standalone", + "next_recommended": ["推荐的下一个命令"] + } +} +``` + +### 命令分组 + +| Group | Commands | +|-------|----------| +| planning | lite-plan, multi-cli-plan, plan, tdd-plan | +| execution | lite-execute, execute, develop-with-file | +| testing | test-gen, test-fix-gen, test-cycle-execute, tdd-verify | +| review | review-session-cycle, review-module-cycle, review-fix | +| bugfix | lite-fix, debug, debug-with-file | +| maintenance | clean, replan | +| verification | action-plan-verify, tdd-verify | + +### 兼容性矩阵 + +| 组合 | 状态 | +|------|------| +| lite-plan + lite-execute | ✓ compatible | +| lite-plan + execute | ✗ incompatible - use plan | +| multi-cli-plan + lite-execute | ✓ compatible | +| plan + execute | ✓ compatible | +| plan + lite-execute | ✗ incompatible - use lite-plan | +| tdd-plan + execute | ✓ compatible | +| execute + test-cycle-execute | ✓ compatible | +| lite-execute + test-cycle-execute | ✓ compatible | +| test-fix-gen + test-cycle-execute | ✓ required | +| review-session-cycle + review-fix | ✓ compatible | +| lite-fix + test-cycle-execute | ✗ incompatible - lite-fix standalone | + +--- + +## 验证工具 + +### chain-validate.js + +位置: `tools/chain-validate.js` + +验证命令链合法性: + +```bash +node tools/chain-validate.js plan execute test-cycle-execute +``` + +输出: +``` +{ + "valid": true, + "errors": [], + "warnings": [] +} +``` + +## 命令注册表 + +### 工具位置 + +位置: `tools/command-registry.js` (skill 内置) + +### 工作模式 + +**按需提取**: 只提取用户确定的任务链中的命令,不是全量扫描。 + +```javascript +// 用户任务链: [lite-plan, lite-execute] +const commandNames = command_chain.map(cmd => cmd.command); +const commandMeta = registry.getCommands(commandNames); +// 只提取这 2 个命令的元数据 +``` + +### 功能 + +- 自动查找全局 `.claude/commands/workflow` 目录(相对路径 > 用户 home) +- 按需提取指定命令的 YAML 头元数据 +- 缓存机制避免重复读取 +- 提供批量查询接口 + +### 集成方式 + +在 action-command-execute 中自动集成: + +```javascript +const CommandRegistry = require('./tools/command-registry.js'); +const registry = new CommandRegistry(); + +// 只提取任务链中的命令 +const commandNames = command_chain.map(cmd => cmd.command); +const commandMeta = registry.getCommands(commandNames); + +// 使用元数据生成提示词 +const cmdInfo = commandMeta[cmd.command]; +// { +// name: 'lite-plan', +// description: '轻量级规划...', +// argumentHint: '[-e|--explore] "task description"', +// allowedTools: [...], +// filePath: '...' +// } +``` + +### 提示词生成 + +智能提示词自动包含: + +1. **任务上下文**: 用户任务描述 +2. **前序产物**: 已完成命令的产物信息 +3. **命令元数据**: 命令的参数提示和描述 + +``` +任务: 实现用户注册功能 + +前序完成: +- /workflow:lite-plan: WFS-plan-001 (IMPL_PLAN.md) + +命令: /workflow:lite-execute [--resume-session="session-id"] +``` + +详见 `tools/README.md`。 diff --git a/.claude/skills/ccw-coordinator/tools/README.md b/.claude/skills/ccw-coordinator/tools/README.md new file mode 100644 index 00000000..d5a37949 --- /dev/null +++ b/.claude/skills/ccw-coordinator/tools/README.md @@ -0,0 +1,77 @@ +# CCW Coordinator Tools + +## command-registry.js + +命令注册表工具:按需查找并提取命令 YAML 头元数据。 + +### 功能 + +- **按需提取**: 只提取用户任务链中的命令(不是全量扫描) +- **自动查找**: 从全局 `.claude/commands/workflow` 目录读取(项目相对路径 > 用户 home) +- **解析 YAML 头**: 提取 name, description, argument-hint, allowed-tools +- **缓存机制**: 避免重复读取文件 + +### 编程接口 + +```javascript +const CommandRegistry = require('./tools/command-registry.js'); +const registry = new CommandRegistry(); + +// 按需提取命令链中的命令元数据 +const commandNames = ['/workflow:lite-plan', '/workflow:lite-execute']; +const commands = registry.getCommands(commandNames); + +// 输出: +// { +// "/workflow:lite-plan": { +// name: 'lite-plan', +// command: '/workflow:lite-plan', +// description: '...', +// argumentHint: '[-e|--explore] "task description"', +// allowedTools: [...], +// filePath: '...' +// }, +// "/workflow:lite-execute": { ... } +// } +``` + +### 命令行接口 + +```bash +# 提取指定命令 +node .claude/skills/ccw-coordinator/tools/command-registry.js lite-plan lite-execute + +# 输出 JSON +node .claude/skills/ccw-coordinator/tools/command-registry.js /workflow:lite-plan +``` + +### 集成用途 + +在 `action-command-execute` 中使用: + +```javascript +// 1. 只提取任务链中的命令 +const commandNames = command_chain.map(cmd => cmd.command); +const commandMeta = registry.getCommands(commandNames); + +// 2. 生成提示词时使用 +function generatePrompt(cmd, state, commandMeta) { + const cmdInfo = commandMeta[cmd.command]; + let prompt = `任务: ${state.task_description}\n`; + + if (cmdInfo?.argumentHint) { + prompt += `命令: ${cmd.command} ${cmdInfo.argumentHint}`; + } + + return prompt; +} +``` + +确保 `ccw cli -p "..."` 提示词包含准确的命令参数提示。 + +### 目录查找逻辑 + +自动查找顺序: +1. `.claude/commands/workflow` (相对于当前工作目录) +2. `~/.claude/commands/workflow` (用户 home 目录) + diff --git a/.claude/skills/ccw-coordinator/tools/chain-validate.js b/.claude/skills/ccw-coordinator/tools/chain-validate.js new file mode 100644 index 00000000..2f3f119d --- /dev/null +++ b/.claude/skills/ccw-coordinator/tools/chain-validate.js @@ -0,0 +1,281 @@ +#!/usr/bin/env node + +/** + * Chain Validation Tool + * + * Validates workflow command chains against defined rules. + * + * Usage: + * node chain-validate.js plan execute test-cycle-execute + * node chain-validate.js --json "plan,execute,test-cycle-execute" + * node chain-validate.js --file custom-chain.json + */ + +const fs = require('fs'); +const path = require('path'); + +// Load registry +const registryPath = path.join(__dirname, '..', 'specs', 'chain-registry.json'); +const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8')); + +class ChainValidator { + constructor(registry) { + this.registry = registry; + this.errors = []; + this.warnings = []; + } + + validate(chain) { + this.errors = []; + this.warnings = []; + + this.validateSinglePlanning(chain); + this.validateCompatiblePairs(chain); + this.validateTestingPosition(chain); + this.validateReviewPosition(chain); + this.validateBugfixStandalone(chain); + this.validateDependencies(chain); + this.validateNoRedundancy(chain); + this.validateCommandExistence(chain); + + return { + valid: this.errors.length === 0, + errors: this.errors, + warnings: this.warnings + }; + } + + validateSinglePlanning(chain) { + const planningCommands = chain.filter(cmd => + ['plan', 'lite-plan', 'multi-cli-plan', 'tdd-plan'].includes(cmd) + ); + + if (planningCommands.length > 1) { + this.errors.push({ + rule: 'Single Planning Command', + message: `Too many planning commands: ${planningCommands.join(', ')}`, + severity: 'error' + }); + } + } + + validateCompatiblePairs(chain) { + const compatibility = { + 'lite-plan': ['lite-execute'], + 'multi-cli-plan': ['lite-execute', 'execute'], + 'plan': ['execute'], + 'tdd-plan': ['execute'] + }; + + const planningCmd = chain.find(cmd => + ['plan', 'lite-plan', 'multi-cli-plan', 'tdd-plan'].includes(cmd) + ); + + const executionCmd = chain.find(cmd => + ['execute', 'lite-execute'].includes(cmd) + ); + + if (planningCmd && executionCmd) { + const compatible = compatibility[planningCmd] || []; + if (!compatible.includes(executionCmd)) { + this.errors.push({ + rule: 'Compatible Pairs', + message: `${planningCmd} incompatible with ${executionCmd}`, + fix: `Use ${planningCmd} with ${compatible.join(' or ')}`, + severity: 'error' + }); + } + } + } + + validateTestingPosition(chain) { + const executionIdx = chain.findIndex(cmd => + ['execute', 'lite-execute', 'develop-with-file'].includes(cmd) + ); + + const testingIdx = chain.findIndex(cmd => + ['test-cycle-execute', 'tdd-verify', 'test-gen', 'test-fix-gen'].includes(cmd) + ); + + if (testingIdx !== -1 && executionIdx !== -1 && executionIdx > testingIdx) { + this.errors.push({ + rule: 'Testing After Execution', + message: 'Testing commands must come after execution', + severity: 'error' + }); + } + + if (testingIdx !== -1 && executionIdx === -1) { + const hasTestGen = chain.some(cmd => ['test-gen', 'test-fix-gen'].includes(cmd)); + if (!hasTestGen) { + this.warnings.push({ + rule: 'Testing After Execution', + message: 'test-cycle-execute without execution context - needs test-gen or execute first', + severity: 'warning' + }); + } + } + } + + validateReviewPosition(chain) { + const executionIdx = chain.findIndex(cmd => + ['execute', 'lite-execute'].includes(cmd) + ); + + const reviewIdx = chain.findIndex(cmd => + cmd.includes('review') + ); + + if (reviewIdx !== -1 && executionIdx !== -1 && executionIdx > reviewIdx) { + this.errors.push({ + rule: 'Review After Changes', + message: 'Review commands must come after execution', + severity: 'error' + }); + } + + if (reviewIdx !== -1 && executionIdx === -1) { + const isModuleReview = chain[reviewIdx] === 'review-module-cycle'; + if (!isModuleReview) { + this.warnings.push({ + rule: 'Review After Changes', + message: 'Review without execution - needs git changes to review', + severity: 'warning' + }); + } + } + } + + validateBugfixStandalone(chain) { + if (chain.includes('lite-fix')) { + const others = chain.filter(cmd => cmd !== 'lite-fix'); + if (others.length > 0) { + this.errors.push({ + rule: 'BugFix Standalone', + message: 'lite-fix must be standalone, cannot combine with other commands', + fix: 'Use lite-fix alone OR use plan + execute for larger changes', + severity: 'error' + }); + } + } + } + + validateDependencies(chain) { + for (let i = 0; i < chain.length; i++) { + const cmd = chain[i]; + const cmdMeta = this.registry.commands[cmd]; + + if (!cmdMeta) continue; + + const deps = cmdMeta.dependencies || []; + const depsOptional = cmdMeta.dependencies_optional || false; + + if (deps.length > 0 && !depsOptional) { + const hasDependency = deps.some(dep => { + const depIdx = chain.indexOf(dep); + return depIdx !== -1 && depIdx < i; + }); + + if (!hasDependency) { + this.errors.push({ + rule: 'Dependency Satisfaction', + message: `${cmd} requires ${deps.join(' or ')} before it`, + severity: 'error' + }); + } + } + } + } + + validateNoRedundancy(chain) { + const seen = new Set(); + const duplicates = []; + + for (const cmd of chain) { + if (seen.has(cmd)) { + duplicates.push(cmd); + } + seen.add(cmd); + } + + if (duplicates.length > 0) { + this.errors.push({ + rule: 'No Redundant Commands', + message: `Duplicate commands: ${duplicates.join(', ')}`, + severity: 'error' + }); + } + } + + validateCommandExistence(chain) { + for (const cmd of chain) { + if (!this.registry.commands[cmd]) { + this.errors.push({ + rule: 'Command Existence', + message: `Unknown command: ${cmd}`, + severity: 'error' + }); + } + } + } +} + +function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.log('Usage:'); + console.log(' chain-validate.js ...'); + console.log(' chain-validate.js --json "cmd1,cmd2,cmd3"'); + console.log(' chain-validate.js --file chain.json'); + process.exit(1); + } + + let chain; + + if (args[0] === '--json') { + chain = args[1].split(',').map(s => s.trim()); + } else if (args[0] === '--file') { + const filePath = args[1]; + const fileContent = JSON.parse(fs.readFileSync(filePath, 'utf8')); + chain = fileContent.chain || fileContent.steps.map(s => s.command); + } else { + chain = args; + } + + const validator = new ChainValidator(registry); + const result = validator.validate(chain); + + console.log('\n=== Chain Validation Report ===\n'); + console.log('Chain:', chain.join(' → ')); + console.log(''); + + if (result.valid) { + console.log('✓ Chain is valid!\n'); + } else { + console.log('✗ Chain has errors:\n'); + result.errors.forEach(err => { + console.log(` [${err.rule}] ${err.message}`); + if (err.fix) { + console.log(` Fix: ${err.fix}`); + } + }); + console.log(''); + } + + if (result.warnings.length > 0) { + console.log('⚠ Warnings:\n'); + result.warnings.forEach(warn => { + console.log(` [${warn.rule}] ${warn.message}`); + }); + console.log(''); + } + + process.exit(result.valid ? 0 : 1); +} + +if (require.main === module) { + main(); +} + +module.exports = { ChainValidator }; diff --git a/.claude/skills/ccw-coordinator/tools/command-registry.js b/.claude/skills/ccw-coordinator/tools/command-registry.js new file mode 100644 index 00000000..4753e521 --- /dev/null +++ b/.claude/skills/ccw-coordinator/tools/command-registry.js @@ -0,0 +1,181 @@ +#!/usr/bin/env node + +/** + * Command Registry Tool + * + * 功能: + * 1. 根据命令名称查找并提取 YAML 头 + * 2. 从全局 .claude/commands/workflow 目录读取 + * 3. 支持按需提取(不是全量扫描) + */ + +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +class CommandRegistry { + constructor(commandDir = null) { + // 优先使用传入的目录 + if (commandDir) { + this.commandDir = commandDir; + } else { + // 自动查找 .claude/commands/workflow + this.commandDir = this.findCommandDir(); + } + this.cache = {}; + } + + /** + * 自动查找 .claude/commands/workflow 目录 + * 支持: 项目相对路径、用户 home 目录 + */ + findCommandDir() { + // 1. 尝试相对于当前工作目录 + const relativePath = path.join('.claude', 'commands', 'workflow'); + if (fs.existsSync(relativePath)) { + return path.resolve(relativePath); + } + + // 2. 尝试用户 home 目录 + const homeDir = os.homedir(); + const homeCommandDir = path.join(homeDir, '.claude', 'commands', 'workflow'); + if (fs.existsSync(homeCommandDir)) { + return homeCommandDir; + } + + // 未找到时返回 null,后续操作会失败并提示 + return null; + } + + /** + * 解析 YAML 头 + */ + parseYamlHeader(content) { + const match = content.match(/^---\n([\s\S]*?)\n---/); + if (!match) return null; + + const yamlContent = match[1]; + const result = {}; + + const lines = yamlContent.split('\n'); + for (const line of lines) { + if (!line.trim()) continue; + + const colonIndex = line.indexOf(':'); + if (colonIndex === -1) continue; + + const key = line.substring(0, colonIndex).trim(); + const value = line.substring(colonIndex + 1).trim(); + + let cleanValue = value.replace(/^["']|["']$/g, ''); + + if (key === 'allowed-tools') { + cleanValue = cleanValue.split(',').map(t => t.trim()); + } + + result[key] = cleanValue; + } + + return result; + } + + /** + * 获取单个命令的元数据 + * @param {string} commandName 命令名称 (e.g., "lite-plan" 或 "/workflow:lite-plan") + * @returns {object|null} 命令信息或 null + */ + getCommand(commandName) { + if (!this.commandDir) { + console.error('ERROR: .claude/commands/workflow 目录未找到'); + return null; + } + + // 标准化命令名称 + const normalized = commandName.startsWith('/workflow:') + ? commandName.substring('/workflow:'.length) + : commandName; + + // 检查缓存 + if (this.cache[normalized]) { + return this.cache[normalized]; + } + + // 读取命令文件 + const filePath = path.join(this.commandDir, `${normalized}.md`); + if (!fs.existsSync(filePath)) { + return null; + } + + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const header = this.parseYamlHeader(content); + + if (header && header.name) { + const result = { + name: header.name, + command: `/workflow:${header.name}`, + description: header.description || '', + argumentHint: header['argument-hint'] || '', + allowedTools: Array.isArray(header['allowed-tools']) + ? header['allowed-tools'] + : (header['allowed-tools'] ? [header['allowed-tools']] : []), + filePath: filePath + }; + + // 缓存结果 + this.cache[normalized] = result; + return result; + } + } catch (error) { + console.error(`读取命令失败 ${filePath}:`, error.message); + } + + return null; + } + + /** + * 批量获取多个命令的元数据 + * @param {array} commandNames 命令名称数组 + * @returns {object} 命令信息映射 + */ + getCommands(commandNames) { + const result = {}; + + for (const name of commandNames) { + const cmd = this.getCommand(name); + if (cmd) { + result[cmd.command] = cmd; + } + } + + return result; + } + + /** + * 生成注册表 JSON + */ + toJSON(commands = null) { + const data = commands || this.cache; + return JSON.stringify(data, null, 2); + } +} + +// CLI 模式 +if (require.main === module) { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error('用法: node command-registry.js [command-name2] ...'); + console.error('示例: node command-registry.js lite-plan lite-execute'); + console.error(' node command-registry.js /workflow:lite-plan'); + process.exit(1); + } + + const registry = new CommandRegistry(); + const commands = registry.getCommands(args); + + console.log(JSON.stringify(commands, null, 2)); +} + +module.exports = CommandRegistry; +