mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
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.
This commit is contained in:
45
.claude/skills/ccw-coordinator/README.md
Normal file
45
.claude/skills/ccw-coordinator/README.md
Normal file
@@ -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 | 验证工具 |
|
||||
320
.claude/skills/ccw-coordinator/SKILL.md
Normal file
320
.claude/skills/ccw-coordinator/SKILL.md
Normal file
@@ -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 智能推荐基于任务分析,非查表静态推荐
|
||||
@@ -0,0 +1,9 @@
|
||||
# action-abort
|
||||
|
||||
中止会话,保存状态
|
||||
|
||||
```javascript
|
||||
updateState({ status: 'aborted' });
|
||||
|
||||
console.log(`会话已中止: ${workDir}`);
|
||||
```
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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' });
|
||||
```
|
||||
25
.claude/skills/ccw-coordinator/phases/actions/action-init.md
Normal file
25
.claude/skills/ccw-coordinator/phases/actions/action-init.md
Normal file
@@ -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}`);
|
||||
```
|
||||
59
.claude/skills/ccw-coordinator/phases/orchestrator.md
Normal file
59
.claude/skills/ccw-coordinator/phases/orchestrator.md
Normal file
@@ -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++;
|
||||
}
|
||||
```
|
||||
66
.claude/skills/ccw-coordinator/phases/state-schema.md
Normal file
66
.claude/skills/ccw-coordinator/phases/state-schema.md
Normal file
@@ -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++`
|
||||
66
.claude/skills/ccw-coordinator/skill-config.json
Normal file
66
.claude/skills/ccw-coordinator/skill-config.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
362
.claude/skills/ccw-coordinator/specs/specs.md
Normal file
362
.claude/skills/ccw-coordinator/specs/specs.md
Normal file
@@ -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`。
|
||||
77
.claude/skills/ccw-coordinator/tools/README.md
Normal file
77
.claude/skills/ccw-coordinator/tools/README.md
Normal file
@@ -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 目录)
|
||||
|
||||
281
.claude/skills/ccw-coordinator/tools/chain-validate.js
Normal file
281
.claude/skills/ccw-coordinator/tools/chain-validate.js
Normal file
@@ -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 <command1> <command2> ...');
|
||||
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 };
|
||||
181
.claude/skills/ccw-coordinator/tools/command-registry.js
Normal file
181
.claude/skills/ccw-coordinator/tools/command-registry.js
Normal file
@@ -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-name> [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;
|
||||
|
||||
Reference in New Issue
Block a user