mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat: add CommandRegistry for command management and direct imports
This commit is contained in:
779
.claude/commands/ccw-coordinator.md
Normal file
779
.claude/commands/ccw-coordinator.md
Normal file
@@ -0,0 +1,779 @@
|
||||
---
|
||||
name: ccw-coordinator
|
||||
description: Command orchestration tool - analyze requirements, recommend chain, execute sequentially with state persistence
|
||||
argument-hint: "[task description]"
|
||||
allowed-tools: Task(*), AskUserQuestion(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*)
|
||||
---
|
||||
|
||||
# CCW Coordinator Command
|
||||
|
||||
Interactive orchestration tool: analyze task → discover commands → recommend chain → execute sequentially → track state.
|
||||
|
||||
**Execution Model**: Pseudocode guidance. Claude intelligently executes each phase based on context.
|
||||
|
||||
## 3-Phase Workflow
|
||||
|
||||
### Phase 1: Analyze Requirements
|
||||
|
||||
Parse task to extract: goal, scope, constraints, complexity, and task type.
|
||||
|
||||
```javascript
|
||||
function analyzeRequirements(taskDescription) {
|
||||
return {
|
||||
goal: extractMainGoal(taskDescription), // e.g., "Implement user registration"
|
||||
scope: extractScope(taskDescription), // e.g., ["auth", "user_management"]
|
||||
constraints: extractConstraints(taskDescription), // e.g., ["no breaking changes"]
|
||||
complexity: determineComplexity(taskDescription), // 'simple' | 'medium' | 'complex'
|
||||
task_type: detectTaskType(taskDescription) // See task type patterns below
|
||||
};
|
||||
}
|
||||
|
||||
// Task Type Detection Patterns
|
||||
function detectTaskType(text) {
|
||||
// Priority order (first match wins)
|
||||
if (/fix|bug|error|crash|fail|debug|diagnose/.test(text)) return 'bugfix';
|
||||
if (/tdd|test-driven|先写测试|test first/.test(text)) return 'tdd';
|
||||
if (/测试失败|test fail|fix test|failing test/.test(text)) return 'test-fix';
|
||||
if (/generate test|写测试|add test|补充测试/.test(text)) return 'test-gen';
|
||||
if (/review|审查|code review/.test(text)) return 'review';
|
||||
if (/不确定|explore|研究|what if|brainstorm|权衡/.test(text)) return 'brainstorm';
|
||||
if (/多视角|比较方案|cross-verify|multi-cli/.test(text)) return 'multi-cli';
|
||||
return 'feature'; // Default
|
||||
}
|
||||
|
||||
// Complexity Assessment
|
||||
function determineComplexity(text) {
|
||||
let score = 0;
|
||||
if (/refactor|重构|migrate|迁移|architect|架构|system|系统/.test(text)) score += 2;
|
||||
if (/multiple|多个|across|跨|all|所有|entire|整个/.test(text)) score += 2;
|
||||
if (/integrate|集成|api|database|数据库/.test(text)) score += 1;
|
||||
if (/security|安全|performance|性能|scale|扩展/.test(text)) score += 1;
|
||||
return score >= 4 ? 'complex' : score >= 2 ? 'medium' : 'simple';
|
||||
}
|
||||
```
|
||||
|
||||
**Display to user**:
|
||||
```
|
||||
Analysis Complete:
|
||||
Goal: [extracted goal]
|
||||
Scope: [identified areas]
|
||||
Constraints: [identified constraints]
|
||||
Complexity: [level]
|
||||
Task Type: [detected type]
|
||||
```
|
||||
|
||||
### Phase 2: Discover Commands & Recommend Chain
|
||||
|
||||
Dynamic command chain assembly using port-based matching.
|
||||
|
||||
#### Command Port Definition
|
||||
|
||||
Each command has input/output ports (tags) for pipeline composition:
|
||||
|
||||
```javascript
|
||||
// Port labels represent data types flowing through the pipeline
|
||||
const commandPorts = {
|
||||
'lite-plan': {
|
||||
name: 'lite-plan',
|
||||
input: ['requirement'], // 输入端口:需求
|
||||
output: ['plan'], // 输出端口:计划
|
||||
tags: ['planning']
|
||||
},
|
||||
'lite-execute': {
|
||||
name: 'lite-execute',
|
||||
input: ['plan'], // 输入端口:计划
|
||||
output: ['code'], // 输出端口:代码
|
||||
tags: ['execution']
|
||||
},
|
||||
'plan': {
|
||||
name: 'plan',
|
||||
input: ['requirement'],
|
||||
output: ['detailed-plan'],
|
||||
tags: ['planning']
|
||||
},
|
||||
'execute': {
|
||||
name: 'execute',
|
||||
input: ['detailed-plan'], // 从 plan 的输出匹配
|
||||
output: ['code'],
|
||||
tags: ['execution']
|
||||
},
|
||||
'test-cycle-execute': {
|
||||
name: 'test-cycle-execute',
|
||||
input: ['code'], // 输入端口:代码
|
||||
output: ['test-passed'], // 输出端口:测试通过
|
||||
tags: ['testing']
|
||||
},
|
||||
'tdd-plan': {
|
||||
name: 'tdd-plan',
|
||||
input: ['requirement'],
|
||||
output: ['tdd-tasks'], // TDD 任务
|
||||
tags: ['planning', 'tdd']
|
||||
},
|
||||
'execute': {
|
||||
name: 'execute',
|
||||
input: ['tdd-tasks'],
|
||||
output: ['code'],
|
||||
tags: ['execution']
|
||||
},
|
||||
'tdd-verify': {
|
||||
name: 'tdd-verify',
|
||||
input: ['code'],
|
||||
output: ['tdd-verified'],
|
||||
tags: ['testing']
|
||||
},
|
||||
'lite-fix': {
|
||||
name: 'lite-fix',
|
||||
input: ['bug-report'], // 输入端口:bug 报告
|
||||
output: ['fixed-code'], // 输出端口:修复后的代码
|
||||
tags: ['bugfix']
|
||||
},
|
||||
'debug': {
|
||||
name: 'debug',
|
||||
input: ['bug-report'],
|
||||
output: ['debug-log'],
|
||||
tags: ['bugfix']
|
||||
},
|
||||
'test-gen': {
|
||||
name: 'test-gen',
|
||||
input: ['code', 'session'], // 可接受代码或会话
|
||||
output: ['tests'],
|
||||
tags: ['testing']
|
||||
},
|
||||
'test-fix-gen': {
|
||||
name: 'test-fix-gen',
|
||||
input: ['failing-tests', 'session'],
|
||||
output: ['test-tasks'],
|
||||
tags: ['testing']
|
||||
},
|
||||
'review': {
|
||||
name: 'review',
|
||||
input: ['code', 'session'],
|
||||
output: ['review-findings'],
|
||||
tags: ['review']
|
||||
},
|
||||
'review-fix': {
|
||||
name: 'review-fix',
|
||||
input: ['review-findings'],
|
||||
output: ['fixed-code'],
|
||||
tags: ['review']
|
||||
},
|
||||
'brainstorm:auto-parallel': {
|
||||
name: 'brainstorm:auto-parallel',
|
||||
input: ['exploration-topic'], // 输入端口:探索主题
|
||||
output: ['brainstorm-analysis'],
|
||||
tags: ['brainstorm']
|
||||
},
|
||||
'multi-cli-plan': {
|
||||
name: 'multi-cli-plan',
|
||||
input: ['requirement'],
|
||||
output: ['comparison-plan'], // 对比分析计划
|
||||
tags: ['planning', 'multi-cli']
|
||||
},
|
||||
'plan-verify': {
|
||||
name: 'plan-verify',
|
||||
input: ['detailed-plan'],
|
||||
output: ['verified-plan'],
|
||||
tags: ['planning']
|
||||
},
|
||||
'review-session-cycle': {
|
||||
name: 'review-session-cycle',
|
||||
input: ['code'],
|
||||
output: ['review-verified'],
|
||||
tags: ['review']
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### Recommendation Algorithm
|
||||
|
||||
```javascript
|
||||
async function recommendCommandChain(analysis) {
|
||||
// Step 1: 根据任务类型确定起始端口和目标端口
|
||||
const { inputPort, outputPort } = determinePortFlow(analysis.task_type, analysis.constraints);
|
||||
|
||||
// Step 2: Claude 根据命令端口定义和任务特征,智能选择命令序列
|
||||
// 优先级:简单任务 → lite-* 命令,复杂任务 → 完整命令,特殊约束 → 调整流程
|
||||
const chain = selectChainByPorts(inputPort, outputPort, analysis);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
// 任务类型对应的端口流
|
||||
function determinePortFlow(taskType, constraints) {
|
||||
const flows = {
|
||||
'bugfix': { inputPort: 'bug-report', outputPort: constraints?.includes('skip-tests') ? 'fixed-code' : 'test-passed' },
|
||||
'tdd': { inputPort: 'requirement', outputPort: 'tdd-verified' },
|
||||
'test-fix': { inputPort: 'failing-tests', outputPort: 'test-passed' },
|
||||
'test-gen': { inputPort: 'code', outputPort: 'test-passed' },
|
||||
'review': { inputPort: 'code', outputPort: 'review-verified' },
|
||||
'brainstorm': { inputPort: 'exploration-topic', outputPort: 'test-passed' },
|
||||
'multi-cli': { inputPort: 'requirement', outputPort: 'test-passed' },
|
||||
'feature': { inputPort: 'requirement', outputPort: constraints?.includes('skip-tests') ? 'code' : 'test-passed' }
|
||||
};
|
||||
return flows[taskType] || flows['feature'];
|
||||
}
|
||||
|
||||
// Claude 根据端口流选择命令链
|
||||
function selectChainByPorts(inputPort, outputPort, analysis) {
|
||||
// 参考下面的命令端口定义表和执行示例,Claude 智能选择合适的命令序列
|
||||
// 返回值示例: [lite-plan, lite-execute, test-cycle-execute]
|
||||
}
|
||||
```
|
||||
|
||||
#### Display to User
|
||||
|
||||
```
|
||||
Recommended Command Chain:
|
||||
|
||||
Pipeline (管道视图):
|
||||
需求 → lite-plan → 计划 → lite-execute → 代码 → test-cycle-execute → 测试通过
|
||||
|
||||
Commands (命令列表):
|
||||
1. /workflow:lite-plan
|
||||
2. /workflow:lite-execute
|
||||
3. /workflow:test-cycle-execute
|
||||
|
||||
Proceed? [Confirm / Show Details / Adjust / Cancel]
|
||||
```
|
||||
|
||||
### Phase 2b: Get User Confirmation
|
||||
|
||||
```javascript
|
||||
async function getUserConfirmation(chain) {
|
||||
const response = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Proceed with this command chain?',
|
||||
header: 'Confirm',
|
||||
options: [
|
||||
{ label: 'Confirm and execute', description: 'Proceed with commands' },
|
||||
{ label: 'Show details', description: 'View each command' },
|
||||
{ label: 'Adjust chain', description: 'Remove or reorder' },
|
||||
{ label: 'Cancel', description: 'Abort' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (response.confirm === 'Cancel') throw new Error('Cancelled');
|
||||
if (response.confirm === 'Show details') {
|
||||
displayCommandDetails(chain);
|
||||
return getUserConfirmation(chain);
|
||||
}
|
||||
if (response.confirm === 'Adjust chain') {
|
||||
return await adjustChain(chain);
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: Execute Sequential Command Chain
|
||||
|
||||
```javascript
|
||||
async function executeCommandChain(chain, analysis) {
|
||||
const sessionId = `ccw-coord-${Date.now()}`;
|
||||
const stateDir = `.workflow/.ccw-coordinator/${sessionId}`;
|
||||
Bash(`mkdir -p "${stateDir}"`);
|
||||
|
||||
const state = {
|
||||
session_id: sessionId,
|
||||
status: 'running',
|
||||
created_at: new Date().toISOString(),
|
||||
analysis: analysis,
|
||||
command_chain: chain,
|
||||
execution_results: [],
|
||||
prompts_used: []
|
||||
};
|
||||
|
||||
for (let i = 0; i < chain.length; i++) {
|
||||
const cmd = chain[i];
|
||||
console.log(`[${i+1}/${chain.length}] ${cmd.command}`);
|
||||
|
||||
// Assemble prompt with previous results
|
||||
const prompt = `Task: ${analysis.goal}\n`;
|
||||
if (state.execution_results.length > 0) {
|
||||
prompt += '\nPrevious results:\n';
|
||||
state.execution_results.forEach(r => {
|
||||
if (r.session_id) {
|
||||
prompt += `- ${r.command}: ${r.session_id} (${r.artifacts?.join(', ') || 'completed'})\n`;
|
||||
}
|
||||
});
|
||||
}
|
||||
prompt += `\n${formatCommand(cmd, state.execution_results, analysis)}\n`;
|
||||
|
||||
// Execute via ccw cli
|
||||
try {
|
||||
const result = Bash(
|
||||
`ccw cli -p "${escapePrompt(prompt)}" --tool claude --mode write -y`,
|
||||
{ run_in_background: true }
|
||||
);
|
||||
const parsed = parseOutput(result.stdout);
|
||||
|
||||
// Record result
|
||||
state.execution_results.push({
|
||||
index: i,
|
||||
command: cmd.command,
|
||||
status: 'completed',
|
||||
session_id: parsed.sessionId,
|
||||
artifacts: parsed.artifacts,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
console.log(`✓ ${parsed.sessionId}\n`);
|
||||
|
||||
} catch (error) {
|
||||
const action = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: `${cmd.command} failed. What to do?`,
|
||||
header: 'Error',
|
||||
options: [
|
||||
{ label: 'Retry', description: 'Try again' },
|
||||
{ label: 'Skip', description: 'Continue' },
|
||||
{ label: 'Abort', description: 'Stop' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (action.error === 'retry') {
|
||||
i--;
|
||||
} else if (action.error === 'abort') {
|
||||
state.status = 'failed';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save state after each command
|
||||
Write(`${stateDir}/state.json`, JSON.stringify(state, null, 2));
|
||||
}
|
||||
|
||||
state.status = 'completed';
|
||||
state.updated_at = new Date().toISOString();
|
||||
Write(`${stateDir}/state.json`, JSON.stringify(state, null, 2));
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// Smart parameter assembly
|
||||
function formatCommand(cmd, previousResults, analysis) {
|
||||
let line = cmd.command + ' --yes';
|
||||
const name = cmd.name;
|
||||
|
||||
// Planning commands - take task description
|
||||
if (['lite-plan', 'plan', 'tdd-plan', 'multi-cli-plan'].includes(name)) {
|
||||
line += ` "${analysis.goal}"`;
|
||||
|
||||
// Lite execution - use --in-memory if plan exists
|
||||
} else if (name === 'lite-execute') {
|
||||
const hasPlan = previousResults.some(r => r.command.includes('plan'));
|
||||
line += hasPlan ? ' --in-memory' : ` "${analysis.goal}"`;
|
||||
|
||||
// Standard execution - resume from planning session
|
||||
} else if (name === 'execute') {
|
||||
const plan = previousResults.find(r => r.command.includes('plan'));
|
||||
if (plan?.session_id) line += ` --resume-session="${plan.session_id}"`;
|
||||
|
||||
// Bug fix commands - take bug description
|
||||
} else if (['lite-fix', 'debug'].includes(name)) {
|
||||
line += ` "${analysis.goal}"`;
|
||||
|
||||
// Brainstorm - take topic description
|
||||
} else if (name === 'brainstorm:auto-parallel' || name === 'auto-parallel') {
|
||||
line += ` "${analysis.goal}"`;
|
||||
|
||||
// Test generation from session - needs source session
|
||||
} else if (name === 'test-gen') {
|
||||
const impl = previousResults.find(r =>
|
||||
r.command.includes('execute') || r.command.includes('lite-execute')
|
||||
);
|
||||
if (impl?.session_id) line += ` "${impl.session_id}"`;
|
||||
else line += ` "${analysis.goal}"`;
|
||||
|
||||
// Test fix generation - session or description
|
||||
} else if (name === 'test-fix-gen') {
|
||||
const latest = previousResults.filter(r => r.session_id).pop();
|
||||
if (latest?.session_id) line += ` "${latest.session_id}"`;
|
||||
else line += ` "${analysis.goal}"`;
|
||||
|
||||
// Review commands - take session or use latest
|
||||
} else if (name === 'review') {
|
||||
const latest = previousResults.filter(r => r.session_id).pop();
|
||||
if (latest?.session_id) line += ` --session="${latest.session_id}"`;
|
||||
|
||||
// Review fix - takes session from review
|
||||
} else if (name === 'review-fix') {
|
||||
const review = previousResults.find(r => r.command.includes('review'));
|
||||
const latest = review || previousResults.filter(r => r.session_id).pop();
|
||||
if (latest?.session_id) line += ` --session="${latest.session_id}"`;
|
||||
|
||||
// TDD verify - takes execution session
|
||||
} else if (name === 'tdd-verify') {
|
||||
const exec = previousResults.find(r => r.command.includes('execute'));
|
||||
if (exec?.session_id) line += ` --session="${exec.session_id}"`;
|
||||
|
||||
// Session-based commands (test-cycle, review-session, plan-verify)
|
||||
} else if (name.includes('test') || name.includes('review') || name.includes('verify')) {
|
||||
const latest = previousResults.filter(r => r.session_id).pop();
|
||||
if (latest?.session_id) line += ` --session="${latest.session_id}"`;
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
// Parse command output
|
||||
function parseOutput(output) {
|
||||
const sessionMatch = output.match(/WFS-[\w-]+/);
|
||||
const artifacts = [];
|
||||
output.matchAll(/\.workflow\/[^\s]+/g).forEach(m => artifacts.push(m[0]));
|
||||
return { sessionId: sessionMatch?.[0] || null, artifacts };
|
||||
}
|
||||
```
|
||||
|
||||
## State File Structure
|
||||
|
||||
**Location**: `.workflow/.ccw-coordinator/{session_id}/state.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "ccw-coord-20250124-143025",
|
||||
"status": "running|completed|failed",
|
||||
"created_at": "2025-01-24T14:30:25Z",
|
||||
"updated_at": "2025-01-24T14:35:45Z",
|
||||
"analysis": {
|
||||
"goal": "Implement user registration",
|
||||
"scope": ["authentication", "user_management"],
|
||||
"constraints": ["no breaking changes"],
|
||||
"complexity": "medium"
|
||||
},
|
||||
"command_chain": [
|
||||
{
|
||||
"index": 0,
|
||||
"command": "/workflow:plan",
|
||||
"name": "plan",
|
||||
"description": "Detailed planning",
|
||||
"argumentHint": "[--explore] \"task\"",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"command": "/workflow:execute",
|
||||
"name": "execute",
|
||||
"description": "Execute with state resume",
|
||||
"argumentHint": "[--resume-session=\"WFS-xxx\"]",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"command": "/workflow:test-cycle-execute",
|
||||
"name": "test-cycle-execute",
|
||||
"status": "pending"
|
||||
}
|
||||
],
|
||||
"execution_results": [
|
||||
{
|
||||
"index": 0,
|
||||
"command": "/workflow:plan",
|
||||
"status": "completed",
|
||||
"session_id": "WFS-plan-20250124",
|
||||
"artifacts": ["IMPL_PLAN.md", "exploration-architecture.json"],
|
||||
"timestamp": "2025-01-24T14:30:45Z"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"command": "/workflow:execute",
|
||||
"status": "completed",
|
||||
"session_id": "WFS-execute-20250124",
|
||||
"artifacts": ["src/features/auth/**", "src/db/migrations/**"],
|
||||
"timestamp": "2025-01-24T14:32:15Z"
|
||||
}
|
||||
],
|
||||
"prompts_used": [
|
||||
{
|
||||
"index": 0,
|
||||
"command": "/workflow:plan",
|
||||
"prompt": "Task: Implement user registration...\n\n/workflow:plan --yes \"Implement user registration...\""
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"command": "/workflow:execute",
|
||||
"prompt": "Task: Implement user registration...\n\nPrevious results:\n- /workflow:plan: WFS-plan-20250124 (IMPL_PLAN.md)\n\n/workflow:execute --yes --resume-session=\"WFS-plan-20250124\""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## CommandRegistry Integration
|
||||
|
||||
Sole CCW tool for command discovery:
|
||||
|
||||
```javascript
|
||||
import { CommandRegistry } from 'ccw/tools/command-registry';
|
||||
|
||||
const registry = new CommandRegistry();
|
||||
|
||||
// Get all commands
|
||||
const allCommands = registry.getAllCommandsSummary();
|
||||
// Map<"/workflow:lite-plan" => {name, description}>
|
||||
|
||||
// Get categorized
|
||||
const byCategory = registry.getAllCommandsByCategory();
|
||||
// {planning, execution, testing, review, other}
|
||||
|
||||
// Get single command metadata
|
||||
const cmd = registry.getCommand('lite-plan');
|
||||
// {name, command, description, argumentHint, allowedTools, filePath}
|
||||
```
|
||||
|
||||
## Execution Examples
|
||||
|
||||
### Simple Feature
|
||||
```
|
||||
Goal: Add API endpoint for user profile
|
||||
Scope: [api]
|
||||
Complexity: simple
|
||||
Constraints: []
|
||||
Task Type: feature
|
||||
|
||||
Pipeline:
|
||||
需求 → lite-plan → 计划 → lite-execute → 代码 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:lite-plan --yes "Add API endpoint..."
|
||||
2. /workflow:lite-execute --yes --in-memory
|
||||
3. /workflow:test-cycle-execute --yes --session="WFS-xxx"
|
||||
```
|
||||
|
||||
### Complex Feature with Verification
|
||||
```
|
||||
Goal: Implement OAuth2 authentication system
|
||||
Scope: [auth, database, api, frontend]
|
||||
Complexity: complex
|
||||
Constraints: [no breaking changes]
|
||||
Task Type: feature
|
||||
|
||||
Pipeline:
|
||||
需求 → plan → 详细计划 → plan-verify → 验证计划 → execute → 代码
|
||||
→ review-session-cycle → 审查通过 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:plan --yes "Implement OAuth2..."
|
||||
2. /workflow:plan-verify --yes --session="WFS-xxx"
|
||||
3. /workflow:execute --yes --resume-session="WFS-xxx"
|
||||
4. /workflow:review-session-cycle --yes --session="WFS-xxx"
|
||||
5. /workflow:test-cycle-execute --yes --session="WFS-xxx"
|
||||
```
|
||||
|
||||
### Quick Bug Fix
|
||||
```
|
||||
Goal: Fix login timeout issue
|
||||
Scope: [auth]
|
||||
Complexity: simple
|
||||
Constraints: [urgent]
|
||||
Task Type: bugfix
|
||||
|
||||
Pipeline:
|
||||
Bug报告 → lite-fix → 修复代码 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:lite-fix --yes "Fix login timeout..."
|
||||
2. /workflow:test-cycle-execute --yes --session="WFS-xxx"
|
||||
```
|
||||
|
||||
### Skip Tests
|
||||
```
|
||||
Goal: Update documentation
|
||||
Scope: [docs]
|
||||
Complexity: simple
|
||||
Constraints: [skip-tests]
|
||||
Task Type: feature
|
||||
|
||||
Pipeline:
|
||||
需求 → lite-plan → 计划 → lite-execute → 代码
|
||||
|
||||
Chain:
|
||||
1. /workflow:lite-plan --yes "Update documentation..."
|
||||
2. /workflow:lite-execute --yes --in-memory
|
||||
```
|
||||
|
||||
### TDD Workflow
|
||||
```
|
||||
Goal: Implement user authentication with test-first approach
|
||||
Scope: [auth]
|
||||
Complexity: medium
|
||||
Constraints: [test-driven]
|
||||
Task Type: tdd
|
||||
|
||||
Pipeline:
|
||||
需求 → tdd-plan → TDD任务 → execute → 代码 → tdd-verify → TDD验证通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:tdd-plan --yes "Implement user authentication..."
|
||||
2. /workflow:execute --yes --resume-session="WFS-xxx"
|
||||
3. /workflow:tdd-verify --yes --session="WFS-xxx"
|
||||
```
|
||||
|
||||
### Debug Workflow
|
||||
```
|
||||
Goal: Fix memory leak in WebSocket handler
|
||||
Scope: [websocket]
|
||||
Complexity: medium
|
||||
Constraints: [production-issue]
|
||||
Task Type: bugfix
|
||||
|
||||
Pipeline (快速修复):
|
||||
Bug报告 → lite-fix → 修复代码 → test-cycle-execute → 测试通过
|
||||
|
||||
Pipeline (系统调试):
|
||||
Bug报告 → debug → 调试日志 → 分析定位 → 修复
|
||||
|
||||
Chain:
|
||||
1. /workflow:lite-fix --yes "Fix memory leak in WebSocket..."
|
||||
2. /workflow:test-cycle-execute --yes --session="WFS-xxx"
|
||||
|
||||
OR (for hypothesis-driven debugging):
|
||||
1. /workflow:debug --yes "Memory leak in WebSocket handler..."
|
||||
```
|
||||
|
||||
### Test Fix Workflow
|
||||
```
|
||||
Goal: Fix failing authentication tests
|
||||
Scope: [auth, tests]
|
||||
Complexity: simple
|
||||
Constraints: []
|
||||
Task Type: test-fix
|
||||
|
||||
Pipeline:
|
||||
失败测试 → test-fix-gen → 测试任务 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:test-fix-gen --yes "WFS-auth-impl-001"
|
||||
2. /workflow:test-cycle-execute --yes --session="WFS-test-xxx"
|
||||
```
|
||||
|
||||
### Test Generation from Implementation
|
||||
```
|
||||
Goal: Generate tests for completed user registration feature
|
||||
Scope: [auth, tests]
|
||||
Complexity: medium
|
||||
Constraints: []
|
||||
Task Type: test-gen
|
||||
|
||||
Pipeline:
|
||||
代码 → test-gen → 测试 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:test-gen --yes "WFS-registration-20250124"
|
||||
2. /workflow:test-cycle-execute --yes --session="WFS-test-xxx"
|
||||
```
|
||||
|
||||
### Review + Fix Workflow
|
||||
```
|
||||
Goal: Code review of payment module
|
||||
Scope: [payment]
|
||||
Complexity: medium
|
||||
Constraints: []
|
||||
Task Type: review
|
||||
|
||||
Pipeline:
|
||||
代码 → review → 审查发现 → review-fix → 修复代码 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:review --yes --session="WFS-payment-impl"
|
||||
2. /workflow:review-fix --yes --session="WFS-payment-impl"
|
||||
3. /workflow:test-cycle-execute --yes --session="WFS-payment-impl"
|
||||
```
|
||||
|
||||
### Brainstorm Workflow (Uncertain Requirements)
|
||||
```
|
||||
Goal: Explore solutions for real-time notification system
|
||||
Scope: [notifications, architecture]
|
||||
Complexity: complex
|
||||
Constraints: []
|
||||
Task Type: brainstorm
|
||||
|
||||
Pipeline:
|
||||
探索主题 → brainstorm:auto-parallel → 分析结果 → plan → 详细计划
|
||||
→ plan-verify → 验证计划 → execute → 代码 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:brainstorm:auto-parallel --yes "Explore solutions for real-time..."
|
||||
2. /workflow:plan --yes "Implement chosen notification approach..."
|
||||
3. /workflow:plan-verify --yes --session="WFS-xxx"
|
||||
4. /workflow:execute --yes --resume-session="WFS-xxx"
|
||||
5. /workflow:test-cycle-execute --yes --session="WFS-xxx"
|
||||
```
|
||||
|
||||
### Multi-CLI Plan (Multi-Perspective Analysis)
|
||||
```
|
||||
Goal: Compare microservices vs monolith architecture
|
||||
Scope: [architecture]
|
||||
Complexity: complex
|
||||
Constraints: []
|
||||
Task Type: multi-cli
|
||||
|
||||
Pipeline:
|
||||
需求 → multi-cli-plan → 对比计划 → lite-execute → 代码 → test-cycle-execute → 测试通过
|
||||
|
||||
Chain:
|
||||
1. /workflow:multi-cli-plan --yes "Compare microservices vs monolith..."
|
||||
2. /workflow:lite-execute --yes --in-memory
|
||||
3. /workflow:test-cycle-execute --yes --session="WFS-xxx"
|
||||
```
|
||||
|
||||
## Execution Flow
|
||||
|
||||
```javascript
|
||||
// Main entry point
|
||||
async function ccwCoordinator(taskDescription) {
|
||||
// Phase 1
|
||||
const analysis = await analyzeRequirements(taskDescription);
|
||||
|
||||
// Phase 2
|
||||
const chain = await recommendCommandChain(analysis);
|
||||
const confirmedChain = await getUserConfirmation(chain);
|
||||
|
||||
// Phase 3
|
||||
const state = await executeCommandChain(confirmedChain, analysis);
|
||||
|
||||
console.log(`✅ Complete! Session: ${state.session_id}`);
|
||||
console.log(`State: .workflow/.ccw-coordinator/${state.session_id}/state.json`);
|
||||
}
|
||||
```
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **No Fixed Logic** - Claude intelligently decides based on analysis
|
||||
2. **Dynamic Discovery** - CommandRegistry retrieves available commands
|
||||
3. **Smart Parameters** - Command args assembled based on previous results
|
||||
4. **Full State Tracking** - All execution recorded to state.json
|
||||
5. **User Control** - Confirmation + error handling with user choice
|
||||
6. **Context Passing** - Each prompt includes previous results
|
||||
7. **Resumable** - Can load state.json to continue
|
||||
|
||||
## Available Commands
|
||||
|
||||
All from `~/.claude/commands/workflow/`:
|
||||
|
||||
**Planning**: lite-plan, plan, multi-cli-plan, plan-verify, tdd-plan
|
||||
**Execution**: lite-execute, execute, develop-with-file
|
||||
**Testing**: test-cycle-execute, test-gen, test-fix-gen, tdd-verify
|
||||
**Review**: review, review-session-cycle, review-module-cycle, review-fix
|
||||
**Bug Fixes**: lite-fix, debug, debug-with-file
|
||||
**Brainstorming**: brainstorm:auto-parallel, brainstorm:artifacts, brainstorm:synthesis
|
||||
**Design**: ui-design:*, animation-extract, layout-extract, style-extract, codify-style
|
||||
**Session Management**: session:start, session:resume, session:complete, session:solidify, session:list
|
||||
**Tools**: context-gather, test-context-gather, task-generate, conflict-resolution, action-plan-verify
|
||||
**Utility**: clean, init, replan
|
||||
|
||||
### Task Type Routing (Pipeline View)
|
||||
|
||||
| Task Type | Pipeline |
|
||||
|-----------|----------|
|
||||
| **feature** (simple) | 需求 → lite-plan → 计划 → lite-execute → 代码 → test-cycle-execute → 测试通过 |
|
||||
| **feature** (complex) | 需求 → plan → 详细计划 → plan-verify → 验证计划 → execute → 代码 → review-session-cycle → 审查通过 → test-cycle-execute → 测试通过 |
|
||||
| **bugfix** | Bug报告 → lite-fix → 修复代码 → test-cycle-execute → 测试通过 |
|
||||
| **tdd** | 需求 → tdd-plan → TDD任务 → execute → 代码 → tdd-verify → TDD验证通过 |
|
||||
| **test-fix** | 失败测试 → test-fix-gen → 测试任务 → test-cycle-execute → 测试通过 |
|
||||
| **test-gen** | 代码 → test-gen → 测试 → test-cycle-execute → 测试通过 |
|
||||
| **review** | 代码 → review → 审查发现 → review-fix → 修复代码 → test-cycle-execute → 测试通过 |
|
||||
| **brainstorm** | 探索主题 → brainstorm:auto-parallel → 分析结果 → plan → 详细计划 → execute → 代码 → test-cycle-execute → 测试通过 |
|
||||
| **multi-cli** | 需求 → multi-cli-plan → 对比计划 → lite-execute → 代码 → test-cycle-execute → 测试通过 |
|
||||
|
||||
Use `CommandRegistry.getAllCommandsSummary()` to discover all commands dynamically.
|
||||
308
ccw/src/tools/command-registry.ts
Normal file
308
ccw/src/tools/command-registry.ts
Normal file
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* Command Registry Tool
|
||||
*
|
||||
* Features:
|
||||
* 1. Scan and parse YAML headers from command files
|
||||
* 2. Read from global ~/.claude/commands/workflow directory
|
||||
* 3. Support on-demand extraction (not full scan)
|
||||
* 4. Cache parsed metadata for performance
|
||||
*/
|
||||
|
||||
import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
|
||||
export interface CommandMetadata {
|
||||
name: string;
|
||||
command: string;
|
||||
description: string;
|
||||
argumentHint: string;
|
||||
allowedTools: string[];
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
export interface CommandSummary {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export class CommandRegistry {
|
||||
private commandDir: string | null;
|
||||
private cache: Map<string, CommandMetadata>;
|
||||
|
||||
constructor(commandDir?: string) {
|
||||
this.cache = new Map();
|
||||
|
||||
if (commandDir) {
|
||||
this.commandDir = commandDir;
|
||||
} else {
|
||||
this.commandDir = this.findCommandDir();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detect ~/.claude/commands/workflow directory
|
||||
*/
|
||||
private findCommandDir(): string | null {
|
||||
// Try relative to current working directory
|
||||
const relativePath = join('.claude', 'commands', 'workflow');
|
||||
if (existsSync(relativePath)) {
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
// Try user home directory
|
||||
const homeDir = homedir();
|
||||
const homeCommandDir = join(homeDir, '.claude', 'commands', 'workflow');
|
||||
if (existsSync(homeCommandDir)) {
|
||||
return homeCommandDir;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse YAML header (simplified version)
|
||||
*
|
||||
* Limitations:
|
||||
* - Only supports simple key: value pairs (single-line values)
|
||||
* - No support for multi-line values, nested objects, complex lists
|
||||
* - allowed-tools field converts comma-separated strings to arrays
|
||||
*/
|
||||
private parseYamlHeader(content: string): Record<string, any> | null {
|
||||
// Handle Windows line endings (\r\n)
|
||||
const match = content.match(/^---[\r\n]+([\s\S]*?)[\r\n]+---/);
|
||||
if (!match) return null;
|
||||
|
||||
const yamlContent = match[1];
|
||||
const result: Record<string, any> = {};
|
||||
|
||||
try {
|
||||
const lines = yamlContent.split(/[\r\n]+/);
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith('#')) continue; // Skip empty lines and comments
|
||||
|
||||
const colonIndex = trimmed.indexOf(':');
|
||||
if (colonIndex === -1) continue;
|
||||
|
||||
const key = trimmed.substring(0, colonIndex).trim();
|
||||
let value = trimmed.substring(colonIndex + 1).trim();
|
||||
|
||||
if (!key) continue; // Skip invalid lines
|
||||
|
||||
// Remove quotes (single or double)
|
||||
let cleanValue = value.replace(/^["']|["']$/g, '');
|
||||
|
||||
// Special handling for allowed-tools field: convert to array
|
||||
// Supports format: "Read, Write, Bash" or "Read,Write,Bash"
|
||||
if (key === 'allowed-tools') {
|
||||
cleanValue = cleanValue
|
||||
.split(',')
|
||||
.map(t => t.trim())
|
||||
.filter(t => t)
|
||||
.join(','); // Keep as comma-separated for now, will convert in getCommand
|
||||
}
|
||||
|
||||
result[key] = cleanValue;
|
||||
}
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
console.error('YAML parsing error:', err.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single command metadata
|
||||
* @param commandName Command name (e.g., "lite-plan" or "/workflow:lite-plan")
|
||||
* @returns Command metadata or null
|
||||
*/
|
||||
public getCommand(commandName: string): CommandMetadata | null {
|
||||
if (!this.commandDir) {
|
||||
console.error('ERROR: ~/.claude/commands/workflow directory not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Normalize command name
|
||||
const normalized = commandName.startsWith('/workflow:')
|
||||
? commandName.substring('/workflow:'.length)
|
||||
: commandName;
|
||||
|
||||
// Check cache
|
||||
const cached = this.cache.get(normalized);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Read command file
|
||||
const filePath = join(this.commandDir, `${normalized}.md`);
|
||||
if (!existsSync(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = readFileSync(filePath, 'utf-8');
|
||||
const header = this.parseYamlHeader(content);
|
||||
|
||||
if (header && header.name) {
|
||||
const toolsStr = header['allowed-tools'] || '';
|
||||
const allowedTools = toolsStr
|
||||
.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter((t: string) => t);
|
||||
|
||||
const result: CommandMetadata = {
|
||||
name: header.name,
|
||||
command: `/workflow:${header.name}`,
|
||||
description: header.description || '',
|
||||
argumentHint: header['argument-hint'] || '',
|
||||
allowedTools: allowedTools,
|
||||
filePath: filePath
|
||||
};
|
||||
|
||||
// Cache result
|
||||
this.cache.set(normalized, result);
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
console.error(`Failed to read command ${filePath}:`, err.message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple commands metadata
|
||||
* @param commandNames Array of command names
|
||||
* @returns Map of command metadata
|
||||
*/
|
||||
public getCommands(commandNames: string[]): Map<string, CommandMetadata> {
|
||||
const result = new Map<string, CommandMetadata>();
|
||||
|
||||
for (const name of commandNames) {
|
||||
const cmd = this.getCommand(name);
|
||||
if (cmd) {
|
||||
result.set(cmd.command, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all commands' names and descriptions
|
||||
* @returns Map of command names to summaries
|
||||
*/
|
||||
public getAllCommandsSummary(): Map<string, CommandSummary> {
|
||||
const result = new Map<string, CommandSummary>();
|
||||
|
||||
if (!this.commandDir) {
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
const files = readdirSync(this.commandDir);
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.md')) continue;
|
||||
|
||||
const filePath = join(this.commandDir, file);
|
||||
const stat = statSync(filePath);
|
||||
|
||||
if (stat.isDirectory()) continue;
|
||||
|
||||
try {
|
||||
const content = readFileSync(filePath, 'utf-8');
|
||||
const header = this.parseYamlHeader(content);
|
||||
|
||||
if (header && header.name) {
|
||||
const commandName = `/workflow:${header.name}`;
|
||||
result.set(commandName, {
|
||||
name: header.name,
|
||||
description: header.description || ''
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Skip files that fail to read
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Return empty map if directory read fails
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all commands organized by category/tags
|
||||
*/
|
||||
public getAllCommandsByCategory(): Record<string, CommandMetadata[]> {
|
||||
const summary = this.getAllCommandsSummary();
|
||||
const result: Record<string, CommandMetadata[]> = {
|
||||
planning: [],
|
||||
execution: [],
|
||||
testing: [],
|
||||
review: [],
|
||||
other: []
|
||||
};
|
||||
|
||||
for (const [cmdName] of summary) {
|
||||
const cmd = this.getCommand(cmdName);
|
||||
if (cmd) {
|
||||
// Categorize based on command name patterns
|
||||
if (cmd.name.includes('plan')) {
|
||||
result.planning.push(cmd);
|
||||
} else if (cmd.name.includes('execute')) {
|
||||
result.execution.push(cmd);
|
||||
} else if (cmd.name.includes('test')) {
|
||||
result.testing.push(cmd);
|
||||
} else if (cmd.name.includes('review')) {
|
||||
result.review.push(cmd);
|
||||
} else {
|
||||
result.other.push(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to JSON for serialization
|
||||
*/
|
||||
public toJSON(): Record<string, any> {
|
||||
const result: Record<string, CommandMetadata> = {};
|
||||
for (const [key, value] of this.cache) {
|
||||
result[`/workflow:${key}`] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export function for direct usage
|
||||
*/
|
||||
export function createCommandRegistry(commandDir?: string): CommandRegistry {
|
||||
return new CommandRegistry(commandDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export function to get all commands
|
||||
*/
|
||||
export function getAllCommandsSync(): Map<string, CommandSummary> {
|
||||
const registry = new CommandRegistry();
|
||||
return registry.getAllCommandsSummary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export function to get specific command
|
||||
*/
|
||||
export function getCommandSync(name: string): CommandMetadata | null {
|
||||
const registry = new CommandRegistry();
|
||||
return registry.getCommand(name);
|
||||
}
|
||||
@@ -378,3 +378,7 @@ export { registerTool };
|
||||
|
||||
// Export ToolSchema type
|
||||
export type { ToolSchema };
|
||||
|
||||
// Export CommandRegistry for direct import
|
||||
export { CommandRegistry, createCommandRegistry, getAllCommandsSync, getCommandSync } from './command-registry.js';
|
||||
export type { CommandMetadata, CommandSummary } from './command-registry.js';
|
||||
|
||||
Reference in New Issue
Block a user