mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-15 02:42:45 +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:
@@ -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++`
|
||||
Reference in New Issue
Block a user