mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
Add execution and planning agent prompts, specifications, and quality standards
- Created execution agent prompt for issue execution with detailed deliverables and validation criteria. - Developed planning agent prompt to analyze issues and generate structured solution plans. - Introduced issue handling specifications outlining the workflow and issue structure. - Established quality standards for evaluating completeness, consistency, correctness, and clarity of solutions. - Defined solution schema specification detailing the required structure and validation rules for solutions. - Documented subagent roles and responsibilities, emphasizing the dual-agent strategy for improved workflow efficiency.
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
# Action: Complete
|
||||
|
||||
完成工作流并生成最终报告。
|
||||
|
||||
## Purpose
|
||||
|
||||
序列化最终状态,生成执行摘要,清理临时文件。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] `state.status === "running"`
|
||||
- [ ] 所有 issues 已处理或错误限制达到
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state) {
|
||||
const workDir = state.work_dir;
|
||||
const issues = state.issues || {};
|
||||
|
||||
console.log("\n=== Finalizing Workflow ===");
|
||||
|
||||
// 1. 生成统计信息
|
||||
const totalIssues = Object.keys(issues).length;
|
||||
const completedCount = Object.values(issues).filter(i => i.status === "completed").length;
|
||||
const failedCount = Object.values(issues).filter(i => i.status === "failed").length;
|
||||
const pendingCount = totalIssues - completedCount - failedCount;
|
||||
|
||||
const stats = {
|
||||
total_issues: totalIssues,
|
||||
completed: completedCount,
|
||||
failed: failedCount,
|
||||
pending: pendingCount,
|
||||
success_rate: totalIssues > 0 ? ((completedCount / totalIssues) * 100).toFixed(1) : 0,
|
||||
duration_ms: new Date() - new Date(state.created_at)
|
||||
};
|
||||
|
||||
console.log("\n=== Summary ===");
|
||||
console.log(`Total Issues: ${stats.total_issues}`);
|
||||
console.log(`✓ Completed: ${stats.completed}`);
|
||||
console.log(`✗ Failed: ${stats.failed}`);
|
||||
console.log(`○ Pending: ${stats.pending}`);
|
||||
console.log(`Success Rate: ${stats.success_rate}%`);
|
||||
console.log(`Duration: ${(stats.duration_ms / 1000).toFixed(1)}s`);
|
||||
|
||||
// 2. 生成详细报告
|
||||
const reportLines = [
|
||||
"# Execution Report",
|
||||
"",
|
||||
`## Summary`,
|
||||
`- Total Issues: ${stats.total_issues}`,
|
||||
`- Completed: ${stats.completed}`,
|
||||
`- Failed: ${stats.failed}`,
|
||||
`- Pending: ${stats.pending}`,
|
||||
`- Success Rate: ${stats.success_rate}%`,
|
||||
`- Duration: ${(stats.duration_ms / 1000).toFixed(1)}s`,
|
||||
"",
|
||||
"## Results by Issue"
|
||||
];
|
||||
|
||||
Object.values(issues).forEach((issue, index) => {
|
||||
const status = issue.status === "completed" ? "✓" : issue.status === "failed" ? "✗" : "○";
|
||||
reportLines.push(`### ${status} [${index + 1}] ${issue.id}: ${issue.title}`);
|
||||
reportLines.push(`- Status: ${issue.status}`);
|
||||
if (issue.solution_id) {
|
||||
reportLines.push(`- Solution: ${issue.solution_id}`);
|
||||
}
|
||||
if (issue.planned_at) {
|
||||
reportLines.push(`- Planned: ${issue.planned_at}`);
|
||||
}
|
||||
if (issue.executed_at) {
|
||||
reportLines.push(`- Executed: ${issue.executed_at}`);
|
||||
}
|
||||
if (issue.error) {
|
||||
reportLines.push(`- Error: ${issue.error}`);
|
||||
}
|
||||
reportLines.push("");
|
||||
});
|
||||
|
||||
if (state.errors && state.errors.length > 0) {
|
||||
reportLines.push("## Errors");
|
||||
state.errors.forEach(error => {
|
||||
reportLines.push(`- [${error.timestamp}] ${error.action}: ${error.message}`);
|
||||
});
|
||||
reportLines.push("");
|
||||
}
|
||||
|
||||
reportLines.push("## Files Generated");
|
||||
reportLines.push(`- Work Directory: ${workDir}`);
|
||||
reportLines.push(`- State File: ${workDir}/state.json`);
|
||||
reportLines.push(`- Execution Results: ${workDir}/execution-results.json`);
|
||||
reportLines.push(`- Solutions: ${workDir}/solutions/`);
|
||||
reportLines.push(`- Snapshots: ${workDir}/snapshots/`);
|
||||
|
||||
// 3. 保存报告
|
||||
const reportPath = `${workDir}/final-report.md`;
|
||||
Write(reportPath, reportLines.join("\n"));
|
||||
|
||||
// 4. 保存最终状态
|
||||
const finalState = {
|
||||
...state,
|
||||
status: "completed",
|
||||
phase: "completed",
|
||||
completed_at: new Date().toISOString(),
|
||||
completed_actions: [...state.completed_actions, "action-complete"],
|
||||
context: {
|
||||
...state.context,
|
||||
...stats
|
||||
}
|
||||
};
|
||||
|
||||
Write(`${workDir}/state.json`, JSON.stringify(finalState, null, 2));
|
||||
|
||||
// 5. 保存汇总 JSON
|
||||
Write(`${workDir}/summary.json`, JSON.stringify({
|
||||
status: "completed",
|
||||
stats: stats,
|
||||
report_file: reportPath,
|
||||
work_dir: workDir,
|
||||
completed_at: new Date().toISOString()
|
||||
}, null, 2));
|
||||
|
||||
// 6. 输出完成消息
|
||||
console.log(`\n✓ Workflow completed`);
|
||||
console.log(`📄 Report: ${reportPath}`);
|
||||
console.log(`📁 Working directory: ${workDir}`);
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: "completed",
|
||||
phase: "completed",
|
||||
completed_at: new Date().toISOString(),
|
||||
completed_actions: [...state.completed_actions, "action-complete"],
|
||||
context: finalState.context
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: "completed",
|
||||
phase: "completed",
|
||||
completed_at: timestamp,
|
||||
completed_actions: [...state.completed_actions, "action-complete"],
|
||||
context: {
|
||||
total_issues: stats.total_issues,
|
||||
completed_count: stats.completed,
|
||||
failed_count: stats.failed,
|
||||
success_rate: stats.success_rate
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 报告生成失败 | 输出文本摘要到控制台 |
|
||||
| 文件写入失败 | 继续完成,允许手动保存 |
|
||||
| 权限错误 | 使用替代目录 |
|
||||
|
||||
## Next Actions (Hints)
|
||||
|
||||
- 无(终止状态)
|
||||
- 用户可选择:
|
||||
- 查看报告:`cat {report_path}`
|
||||
- 恢复并重试失败的 issues:`codex issue:plan-execute --resume {work_dir}`
|
||||
- 清理临时文件:`rm -rf {work_dir}`
|
||||
@@ -0,0 +1,220 @@
|
||||
# Action: Execute Solutions
|
||||
|
||||
按队列顺序执行已规划的解决方案。
|
||||
|
||||
## Purpose
|
||||
|
||||
加载计划的解决方案并使用 subagent 执行所有任务、提交更改。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] `state.status === "running"`
|
||||
- [ ] `issues with solution_id` exist (来自规划阶段)
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state) {
|
||||
const workDir = state.work_dir;
|
||||
const issues = state.issues || {};
|
||||
const queue = state.queue || [];
|
||||
|
||||
// 1. 构建执行队列(来自已规划的 issues)
|
||||
const plannedIssues = Object.values(issues).filter(i => i.status === "planned");
|
||||
|
||||
if (plannedIssues.length === 0) {
|
||||
console.log("No planned solutions to execute");
|
||||
return { stateUpdates: { queue } };
|
||||
}
|
||||
|
||||
console.log(`\n=== Executing ${plannedIssues.length} Solutions ===`);
|
||||
|
||||
// 2. 序列化执行每个解决方案
|
||||
const executionResults = [];
|
||||
|
||||
for (let i = 0; i < plannedIssues.length; i++) {
|
||||
const issue = plannedIssues[i];
|
||||
const solutionId = issue.solution_id;
|
||||
|
||||
console.log(`\n[${i + 1}/${plannedIssues.length}] Executing: ${solutionId}`);
|
||||
|
||||
try {
|
||||
// 创建快照(便于恢复)
|
||||
const beforeSnapshot = {
|
||||
timestamp: new Date().toISOString(),
|
||||
phase: "before-execute",
|
||||
issue_id: issue.id,
|
||||
solution_id: solutionId,
|
||||
state: { ...state }
|
||||
};
|
||||
Write(`${workDir}/snapshots/snapshot-before-execute-${i}.json`, JSON.stringify(beforeSnapshot, null, 2));
|
||||
|
||||
// 执行 subagent
|
||||
const executionPrompt = `
|
||||
## TASK ASSIGNMENT
|
||||
|
||||
### MANDATORY FIRST STEPS (Agent Execute)
|
||||
1. **Read role definition**: ~/.codex/agents/issue-execute-agent.md (MUST read first)
|
||||
2. Read: .workflow/project-tech.json
|
||||
3. Read: .workflow/project-guidelines.json
|
||||
|
||||
---
|
||||
|
||||
Goal: Execute solution "${solutionId}" for issue "${issue.id}"
|
||||
|
||||
Scope:
|
||||
- CAN DO: Implement tasks, run tests, commit code
|
||||
- CANNOT DO: Push to remote or create PRs without approval
|
||||
- Directory: ${process.cwd()}
|
||||
|
||||
Solution ID: ${solutionId}
|
||||
|
||||
Load solution details:
|
||||
- Read: ${workDir}/solutions/${issue.id}-plan.json
|
||||
|
||||
Execution steps:
|
||||
1. Parse all tasks from solution
|
||||
2. Execute each task: implement → test → verify
|
||||
3. Commit once for all tasks with formatted summary
|
||||
4. Report completion
|
||||
|
||||
Quality bar:
|
||||
- All acceptance criteria verified
|
||||
- Tests passing
|
||||
- Commit message follows conventions
|
||||
|
||||
Return: JSON with files_modified[], commit_hash, status
|
||||
`;
|
||||
|
||||
const result = await Task({
|
||||
subagent_type: "universal-executor",
|
||||
run_in_background: false,
|
||||
description: `Execute solution ${solutionId}`,
|
||||
prompt: executionPrompt
|
||||
});
|
||||
|
||||
// 解析执行结果
|
||||
let execResult;
|
||||
try {
|
||||
execResult = typeof result === "string" ? JSON.parse(result) : result;
|
||||
} catch {
|
||||
execResult = { status: "executed", commit_hash: "unknown" };
|
||||
}
|
||||
|
||||
// 保存执行结果
|
||||
Write(`${workDir}/solutions/${issue.id}-execution.json`, JSON.stringify({
|
||||
solution_id: solutionId,
|
||||
issue_id: issue.id,
|
||||
status: "completed",
|
||||
executed_at: new Date().toISOString(),
|
||||
execution_result: execResult
|
||||
}, null, 2));
|
||||
|
||||
// 更新 issue 状态
|
||||
issues[issue.id].status = "completed";
|
||||
issues[issue.id].executed_at = new Date().toISOString();
|
||||
|
||||
// 更新队列项
|
||||
const queueIndex = queue.findIndex(q => q.solution_id === solutionId);
|
||||
if (queueIndex >= 0) {
|
||||
queue[queueIndex].status = "completed";
|
||||
}
|
||||
|
||||
// 更新 ccw
|
||||
try {
|
||||
Bash(`ccw issue update ${issue.id} --status completed`);
|
||||
} catch (error) {
|
||||
console.log(`Note: Could not update ccw status (${error.message})`);
|
||||
}
|
||||
|
||||
console.log(`✓ ${solutionId} completed`);
|
||||
executionResults.push({
|
||||
issue_id: issue.id,
|
||||
solution_id: solutionId,
|
||||
status: "completed",
|
||||
commit: execResult.commit_hash
|
||||
});
|
||||
|
||||
state.context.completed_count++;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`✗ Execution failed for ${solutionId}: ${error.message}`);
|
||||
|
||||
// 更新失败状态
|
||||
issues[issue.id].status = "failed";
|
||||
issues[issue.id].error = error.message;
|
||||
|
||||
state.context.failed_count++;
|
||||
|
||||
executionResults.push({
|
||||
issue_id: issue.id,
|
||||
solution_id: solutionId,
|
||||
status: "failed",
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 保存执行结果摘要
|
||||
Write(`${workDir}/execution-results.json`, JSON.stringify({
|
||||
total: plannedIssues.length,
|
||||
completed: state.context.completed_count,
|
||||
failed: state.context.failed_count,
|
||||
results: executionResults,
|
||||
timestamp: new Date().toISOString()
|
||||
}, null, 2));
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
issues: issues,
|
||||
queue: queue,
|
||||
context: state.context,
|
||||
completed_actions: [...state.completed_actions, "action-execute"]
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
issues: {
|
||||
[issue.id]: {
|
||||
...issue,
|
||||
status: "completed|failed",
|
||||
executed_at: timestamp,
|
||||
error: errorMessage
|
||||
}
|
||||
},
|
||||
queue: [
|
||||
...queue.map(item =>
|
||||
item.solution_id === solutionId
|
||||
? { ...item, status: "completed|failed" }
|
||||
: item
|
||||
)
|
||||
],
|
||||
context: {
|
||||
...state.context,
|
||||
completed_count: newCompletedCount,
|
||||
failed_count: newFailedCount
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 任务执行失败 | 标记为失败,继续下一个 |
|
||||
| 测试失败 | 不提交,标记为失败 |
|
||||
| 提交失败 | 保存快照便于恢复 |
|
||||
| Subagent 超时 | 记录超时,继续 |
|
||||
|
||||
## Next Actions (Hints)
|
||||
|
||||
- 执行完成:转入 action-complete 阶段
|
||||
- 有失败项:用户选择是否重试
|
||||
- 全部完成:生成最终报告
|
||||
@@ -0,0 +1,86 @@
|
||||
# Action: Initialize
|
||||
|
||||
初始化 Skill 执行状态和工作目录。
|
||||
|
||||
## Purpose
|
||||
|
||||
设置初始状态,创建工作目录,准备执行环境。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] `state.status === "pending"`
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state) {
|
||||
// 创建工作目录
|
||||
const timestamp = new Date().toISOString().slice(0,19).replace(/[-:T]/g, '');
|
||||
const workDir = `.workflow/.scratchpad/codex-issue-${timestamp}`;
|
||||
|
||||
Bash(`mkdir -p "${workDir}/solutions" "${workDir}/snapshots"`);
|
||||
|
||||
// 初始化状态
|
||||
const initialState = {
|
||||
status: "running",
|
||||
phase: "initialized",
|
||||
work_dir: workDir,
|
||||
issues: {},
|
||||
queue: [],
|
||||
completed_actions: ["action-init"],
|
||||
context: {
|
||||
total_issues: 0,
|
||||
completed_count: 0,
|
||||
failed_count: 0
|
||||
},
|
||||
errors: [],
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 保存初始状态
|
||||
Write(`${workDir}/state.json`, JSON.stringify(initialState, null, 2));
|
||||
Write(`${workDir}/state-history.json`, JSON.stringify([{
|
||||
timestamp: initialState.created_at,
|
||||
phase: "init",
|
||||
completed_actions: 1,
|
||||
issues_count: 0
|
||||
}], null, 2));
|
||||
|
||||
console.log(`✓ Initialized: ${workDir}`);
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: "running",
|
||||
phase: "initialized",
|
||||
work_dir: workDir,
|
||||
completed_actions: ["action-init"]
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: "running",
|
||||
phase: "initialized",
|
||||
work_dir: workDir,
|
||||
completed_actions: ["action-init"]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 目录创建失败 | 检查权限,使用临时目录 |
|
||||
| 文件写入失败 | 重试或切换存储位置 |
|
||||
|
||||
## Next Actions (Hints)
|
||||
|
||||
- 成功:进入 listing phase,执行 action-list
|
||||
- 失败:中止工作流
|
||||
@@ -0,0 +1,165 @@
|
||||
# Action: List Issues
|
||||
|
||||
列出 issues 并支持用户交互选择。
|
||||
|
||||
## Purpose
|
||||
|
||||
展示当前所有 issues 的状态,收集用户的规划/执行意图。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] `state.status === "running"`
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state) {
|
||||
// 1. 加载或初始化 issues
|
||||
let issues = state.issues || {};
|
||||
|
||||
// 2. 从 ccw issue list 或提供的参数加载 issues
|
||||
// 这取决于用户是否在命令行提供了 issue IDs
|
||||
// 示例:ccw codex issue:plan-execute ISS-001,ISS-002
|
||||
|
||||
// 对于本次演示,我们假设从 issues.jsonl 加载
|
||||
try {
|
||||
const issuesListOutput = Bash("ccw issue list --status registered,planned --json").output;
|
||||
const issuesList = JSON.parse(issuesListOutput);
|
||||
|
||||
issuesList.forEach(issue => {
|
||||
if (!issues[issue.id]) {
|
||||
issues[issue.id] = {
|
||||
id: issue.id,
|
||||
title: issue.title,
|
||||
status: "registered",
|
||||
solution_id: null,
|
||||
planned_at: null,
|
||||
executed_at: null,
|
||||
error: null
|
||||
};
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Note: Could not load issues from ccw issue list");
|
||||
// 使用来自参数的 issues,或者空列表
|
||||
}
|
||||
|
||||
// 3. 显示当前状态
|
||||
const totalIssues = Object.keys(issues).length;
|
||||
const registeredCount = Object.values(issues).filter(i => i.status === "registered").length;
|
||||
const plannedCount = Object.values(issues).filter(i => i.status === "planned").length;
|
||||
const completedCount = Object.values(issues).filter(i => i.status === "completed").length;
|
||||
|
||||
console.log("\n=== Issue Status ===");
|
||||
console.log(`Total: ${totalIssues} | Registered: ${registeredCount} | Planned: ${plannedCount} | Completed: ${completedCount}`);
|
||||
|
||||
if (totalIssues === 0) {
|
||||
console.log("\nNo issues found. Please create issues first using 'ccw issue init'");
|
||||
return {
|
||||
stateUpdates: {
|
||||
context: {
|
||||
...state.context,
|
||||
total_issues: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 4. 显示详细列表
|
||||
console.log("\n=== Issue Details ===");
|
||||
Object.values(issues).forEach((issue, index) => {
|
||||
const status = issue.status === "completed" ? "✓" : issue.status === "planned" ? "→" : "○";
|
||||
console.log(`${status} [${index + 1}] ${issue.id}: ${issue.title} (${issue.status})`);
|
||||
});
|
||||
|
||||
// 5. 询问用户下一步
|
||||
const issueIds = Object.keys(issues);
|
||||
const pendingIds = issueIds.filter(id => issues[id].status === "registered");
|
||||
|
||||
if (pendingIds.length === 0) {
|
||||
console.log("\nNo unplanned issues. Ready to execute planned solutions.");
|
||||
return {
|
||||
stateUpdates: {
|
||||
context: {
|
||||
...state.context,
|
||||
total_issues: totalIssues
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 6. 显示选项
|
||||
console.log("\nNext action:");
|
||||
console.log("- Enter 'p' to PLAN selected issues");
|
||||
console.log("- Enter 'x' to EXECUTE planned solutions");
|
||||
console.log("- Enter 'a' to plan ALL pending issues");
|
||||
console.log("- Enter 'q' to QUIT");
|
||||
|
||||
const response = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Select issues to plan (comma-separated numbers, or 'all'):",
|
||||
header: "Selection",
|
||||
multiSelect: false,
|
||||
options: pendingIds.slice(0, 4).map(id => ({
|
||||
label: `${issues[id].id}: ${issues[id].title}`,
|
||||
description: `Current status: ${issues[id].status}`
|
||||
}))
|
||||
}]
|
||||
});
|
||||
|
||||
// 7. 更新 issues 状态为 "planning"
|
||||
const selectedIds = [];
|
||||
if (response.Selection === "all") {
|
||||
selectedIds.push(...pendingIds);
|
||||
} else {
|
||||
// 解析用户选择
|
||||
selectedIds.push(response.Selection);
|
||||
}
|
||||
|
||||
selectedIds.forEach(issueId => {
|
||||
if (issues[issueId]) {
|
||||
issues[issueId].status = "planning";
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
issues: issues,
|
||||
context: {
|
||||
...state.context,
|
||||
total_issues: totalIssues
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
issues: issues,
|
||||
context: {
|
||||
total_issues: Object.keys(issues).length,
|
||||
registered_count: registeredCount,
|
||||
planned_count: plannedCount,
|
||||
completed_count: completedCount
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| Issues 加载失败 | 使用空列表继续 |
|
||||
| 用户输入无效 | 要求重新选择 |
|
||||
| 列表显示异常 | 使用 JSON 格式输出 |
|
||||
|
||||
## Next Actions (Hints)
|
||||
|
||||
- 有 "planning" issues:执行 action-plan
|
||||
- 无 pending issues:执行 action-execute
|
||||
- 用户取消:中止
|
||||
@@ -0,0 +1,170 @@
|
||||
# Action: Plan Solutions
|
||||
|
||||
为选中的 issues 生成执行方案。
|
||||
|
||||
## Purpose
|
||||
|
||||
使用 subagent 分析 issues 并生成解决方案,支持多解决方案选择和自动绑定。
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] `state.status === "running"`
|
||||
- [ ] `issues with status === "planning"` exist
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state) {
|
||||
const workDir = state.work_dir;
|
||||
const issues = state.issues || {};
|
||||
|
||||
// 1. 识别需要规划的 issues
|
||||
const planningIssues = Object.values(issues).filter(i => i.status === "planning");
|
||||
|
||||
if (planningIssues.length === 0) {
|
||||
console.log("No issues to plan");
|
||||
return { stateUpdates: { issues } };
|
||||
}
|
||||
|
||||
console.log(`\n=== Planning ${planningIssues.length} Issues ===`);
|
||||
|
||||
// 2. 为每个 issue 生成规划 subagent
|
||||
const planningAgents = planningIssues.map(issue => ({
|
||||
issue_id: issue.id,
|
||||
issue_title: issue.title,
|
||||
prompt: `
|
||||
## TASK ASSIGNMENT
|
||||
|
||||
### MANDATORY FIRST STEPS (Agent Execute)
|
||||
1. **Read role definition**: ~/.codex/agents/issue-plan-agent.md (MUST read first)
|
||||
2. Read: .workflow/project-tech.json
|
||||
3. Read: .workflow/project-guidelines.json
|
||||
4. Read schema: ~/.claude/workflows/cli-templates/schemas/solution-schema.json
|
||||
|
||||
---
|
||||
|
||||
Goal: Plan solution for issue "${issue.id}: ${issue.title}"
|
||||
|
||||
Scope:
|
||||
- CAN DO: Explore codebase, design solutions, create tasks
|
||||
- CANNOT DO: Execute solutions, modify production code
|
||||
- Directory: ${process.cwd()}
|
||||
|
||||
Task Description:
|
||||
${issue.title}
|
||||
|
||||
Deliverables:
|
||||
- Create ONE primary solution
|
||||
- Write to: ${workDir}/solutions/${issue.id}-plan.json
|
||||
- Format: JSON following solution-schema.json
|
||||
|
||||
Quality bar:
|
||||
- Tasks have quantified acceptance.criteria
|
||||
- Each task includes test.commands
|
||||
- Solution follows schema exactly
|
||||
|
||||
Return: JSON with solution_id, task_count, status
|
||||
`
|
||||
}));
|
||||
|
||||
// 3. 执行规划(串行执行避免竞争)
|
||||
for (const agent of planningAgents) {
|
||||
console.log(`\n→ Planning: ${agent.issue_id}`);
|
||||
|
||||
try {
|
||||
// 对于 Codex,这里应该使用 spawn_agent
|
||||
// 对于 Claude Code Task,使用 Task()
|
||||
|
||||
// 模拟 Task 调用 (实际应该是 spawn_agent 对于 Codex)
|
||||
const result = await Task({
|
||||
subagent_type: "universal-executor",
|
||||
run_in_background: false,
|
||||
description: `Plan solution for ${agent.issue_id}`,
|
||||
prompt: agent.prompt
|
||||
});
|
||||
|
||||
// 解析结果
|
||||
let planResult;
|
||||
try {
|
||||
planResult = typeof result === "string" ? JSON.parse(result) : result;
|
||||
} catch {
|
||||
planResult = { status: "executed", solution_id: `SOL-${agent.issue_id}-1` };
|
||||
}
|
||||
|
||||
// 更新 issue 状态
|
||||
issues[agent.issue_id].status = "planned";
|
||||
issues[agent.issue_id].solution_id = planResult.solution_id || `SOL-${agent.issue_id}-1`;
|
||||
issues[agent.issue_id].planned_at = new Date().toISOString();
|
||||
|
||||
console.log(`✓ ${agent.issue_id} → ${issues[agent.issue_id].solution_id}`);
|
||||
|
||||
// 绑定解决方案
|
||||
try {
|
||||
Bash(`ccw issue bind ${agent.issue_id} ${issues[agent.issue_id].solution_id}`);
|
||||
} catch (error) {
|
||||
console.log(`Note: Could not bind solution (${error.message})`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`✗ Planning failed for ${agent.issue_id}: ${error.message}`);
|
||||
issues[agent.issue_id].status = "registered"; // 回退
|
||||
issues[agent.issue_id].error = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 更新 issue 状态到 ccw
|
||||
try {
|
||||
Bash(`ccw issue update --from-planning`);
|
||||
} catch {
|
||||
console.log("Note: Could not update issue status");
|
||||
}
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
issues: issues,
|
||||
completed_actions: [...state.completed_actions, "action-plan"]
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
issues: {
|
||||
[issue.id]: {
|
||||
...issue,
|
||||
status: "planned",
|
||||
solution_id: solutionId,
|
||||
planned_at: timestamp
|
||||
}
|
||||
},
|
||||
queue: [
|
||||
...state.queue,
|
||||
{
|
||||
item_id: `S-${index}`,
|
||||
issue_id: issue.id,
|
||||
solution_id: solutionId,
|
||||
status: "pending"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| Subagent 超时 | 标记为失败,继续下一个 |
|
||||
| 无效解决方案 | 回退到 registered 状态 |
|
||||
| 绑定失败 | 记录警告,但继续 |
|
||||
| 文件写入失败 | 重试 3 次 |
|
||||
|
||||
## Next Actions (Hints)
|
||||
|
||||
- 所有 issues 规划完成:执行 action-execute
|
||||
- 部分失败:用户选择是否继续或重试
|
||||
- 全部失败:返回 action-list 重新选择
|
||||
210
.codex/skills/codex-issue-plan-execute/phases/orchestrator.md
Normal file
210
.codex/skills/codex-issue-plan-execute/phases/orchestrator.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Orchestrator - Dual-Agent Pipeline Architecture
|
||||
|
||||
主流程编排器:创建两个持久化 agent(规划和执行),流水线式处理所有 issue。
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Main Orchestrator (Claude Code) │
|
||||
│ 流水线式分配任务给两个持久化 agent │
|
||||
└──────┬────────────────────────────────────────┬────────┘
|
||||
│ send_input │ send_input
|
||||
│ (逐个 issue) │ (逐个 solution)
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Planning Agent │ │ Execution Agent │
|
||||
│ (持久化) │ │ (持久化) │
|
||||
│ │ │ │
|
||||
│ • 接收 issue │ │ • 接收 solution │
|
||||
│ • 设计方案 │ │ • 执行 tasks │
|
||||
│ • 返回 solution │ │ • 返回执行结果 │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
▲ ▲
|
||||
└────────────────┬─────────────────────┘
|
||||
wait for completion
|
||||
```
|
||||
|
||||
## Main Orchestrator Pseudocode
|
||||
|
||||
```javascript
|
||||
async function mainOrchestrator(workDir, issues) {
|
||||
const planningResults = { results: [] }; // 统一存储
|
||||
const executionResults = { results: [] }; // 统一存储
|
||||
|
||||
// 1. Create persistent agents (never close until done)
|
||||
const planningAgentId = spawn_agent({
|
||||
message: Read('prompts/planning-agent-system.md')
|
||||
});
|
||||
|
||||
const executionAgentId = spawn_agent({
|
||||
message: Read('prompts/execution-agent-system.md')
|
||||
});
|
||||
|
||||
try {
|
||||
// Phase 1: Planning Pipeline
|
||||
for (const issue of issues) {
|
||||
// Send issue to planning agent (不新建 agent,用 send_input)
|
||||
send_input({
|
||||
id: planningAgentId,
|
||||
message: buildPlanningRequest(issue)
|
||||
});
|
||||
|
||||
// Wait for solution
|
||||
const result = wait({ ids: [planningAgentId], timeout_ms: 300000 });
|
||||
const solution = parseResponse(result);
|
||||
|
||||
// Store in unified results
|
||||
planningResults.results.push({
|
||||
issue_id: issue.id,
|
||||
solution: solution,
|
||||
status: solution ? "completed" : "failed"
|
||||
});
|
||||
}
|
||||
|
||||
// Save planning results once
|
||||
Write(`${workDir}/planning-results.json`, JSON.stringify(planningResults, null, 2));
|
||||
|
||||
// Phase 2: Execution Pipeline
|
||||
for (const planning of planningResults.results) {
|
||||
if (planning.status !== "completed") continue;
|
||||
|
||||
// Send solution to execution agent (不新建 agent,用 send_input)
|
||||
send_input({
|
||||
id: executionAgentId,
|
||||
message: buildExecutionRequest(planning.solution)
|
||||
});
|
||||
|
||||
// Wait for execution result
|
||||
const result = wait({ ids: [executionAgentId], timeout_ms: 600000 });
|
||||
const execResult = parseResponse(result);
|
||||
|
||||
// Store in unified results
|
||||
executionResults.results.push({
|
||||
issue_id: planning.issue_id,
|
||||
status: execResult?.status || "failed",
|
||||
commit_hash: execResult?.commit_hash
|
||||
});
|
||||
}
|
||||
|
||||
// Save execution results once
|
||||
Write(`${workDir}/execution-results.json`, JSON.stringify(executionResults, null, 2));
|
||||
|
||||
} finally {
|
||||
// Close agents after ALL issues processed
|
||||
close_agent({ id: planningAgentId });
|
||||
close_agent({ id: executionAgentId });
|
||||
}
|
||||
|
||||
generateFinalReport(workDir, planningResults, executionResults);
|
||||
}
|
||||
```
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
### 1. Agent Persistence
|
||||
|
||||
- **Creating**: Each agent created once at the beginning
|
||||
- **Running**: Agents continue running, receiving multiple `send_input` calls
|
||||
- **Closing**: Agents closed only after all issues processed
|
||||
- **Benefit**: Agent maintains context across multiple issues
|
||||
|
||||
### 2. Unified Results Storage
|
||||
|
||||
```json
|
||||
// planning-results.json
|
||||
{
|
||||
"phase": "planning",
|
||||
"created_at": "2025-01-29T12:00:00Z",
|
||||
"results": [
|
||||
{
|
||||
"issue_id": "ISS-001",
|
||||
"solution_id": "SOL-ISS-001-1",
|
||||
"status": "completed",
|
||||
"solution": { "id": "...", "tasks": [...] },
|
||||
"planned_at": "2025-01-29T12:05:00Z"
|
||||
},
|
||||
{
|
||||
"issue_id": "ISS-002",
|
||||
"solution_id": "SOL-ISS-002-1",
|
||||
"status": "completed",
|
||||
"solution": { "id": "...", "tasks": [...] },
|
||||
"planned_at": "2025-01-29T12:10:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// execution-results.json
|
||||
{
|
||||
"phase": "execution",
|
||||
"created_at": "2025-01-29T12:15:00Z",
|
||||
"results": [
|
||||
{
|
||||
"issue_id": "ISS-001",
|
||||
"solution_id": "SOL-ISS-001-1",
|
||||
"status": "completed",
|
||||
"commit_hash": "abc123def",
|
||||
"files_modified": ["src/auth.ts"],
|
||||
"executed_at": "2025-01-29T12:20:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 单一 JSON 文件,易于查询和分析
|
||||
- 完整的处理历史
|
||||
- 减少文件 I/O 次数
|
||||
|
||||
### 3. Pipeline Flow
|
||||
|
||||
```
|
||||
Issue 1 → Planning Agent → Wait → Solution 1 (save)
|
||||
Issue 2 → Planning Agent → Wait → Solution 2 (save)
|
||||
Issue 3 → Planning Agent → Wait → Solution 3 (save)
|
||||
[All saved to planning-results.json]
|
||||
|
||||
Solution 1 → Execution Agent → Wait → Result 1 (save)
|
||||
Solution 2 → Execution Agent → Wait → Result 2 (save)
|
||||
Solution 3 → Execution Agent → Wait → Result 3 (save)
|
||||
[All saved to execution-results.json]
|
||||
```
|
||||
|
||||
### 4. Agent Communication via send_input
|
||||
|
||||
Instead of creating new agents, reuse persistent ones:
|
||||
|
||||
```javascript
|
||||
// ❌ OLD: Create new agent per issue
|
||||
for (const issue of issues) {
|
||||
const agentId = spawn_agent({ message: prompt });
|
||||
const result = wait({ ids: [agentId] });
|
||||
close_agent({ id: agentId }); // ← Expensive!
|
||||
}
|
||||
|
||||
// ✅ NEW: Persistent agent with send_input
|
||||
const agentId = spawn_agent({ message: initialPrompt });
|
||||
for (const issue of issues) {
|
||||
send_input({ id: agentId, message: taskPrompt }); // ← Reuse!
|
||||
const result = wait({ ids: [agentId] });
|
||||
}
|
||||
close_agent({ id: agentId }); // ← Single cleanup
|
||||
```
|
||||
|
||||
### 5. Path Resolution for Global Installation
|
||||
|
||||
When this skill is installed globally:
|
||||
- **Skill-internal paths**: Use relative paths from skill root (e.g., `prompts/planning-agent-system.md`)
|
||||
- **Project paths**: Use project-relative paths starting with `.` (e.g., `.workflow/project-tech.json`)
|
||||
- **User-home paths**: Use `~` prefix (e.g., `~/.codex/agents/...`)
|
||||
- **Working directory**: Always relative to the project root when skill executes
|
||||
|
||||
## Benefits of This Architecture
|
||||
|
||||
| 方面 | 优势 |
|
||||
|------|------|
|
||||
| **性能** | Agent 创建/销毁开销仅一次(而非 N 次) |
|
||||
| **上下文** | Agent 在多个任务间保持上下文 |
|
||||
| **存储** | 统一的 JSON 文件,易于追踪和查询 |
|
||||
| **通信** | 通过 send_input 实现 agent 间的数据传递 |
|
||||
| **可维护性** | 流水线结构清晰,易于调试 |
|
||||
136
.codex/skills/codex-issue-plan-execute/phases/state-schema.md
Normal file
136
.codex/skills/codex-issue-plan-execute/phases/state-schema.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# State Schema Definition
|
||||
|
||||
状态结构定义和验证规则。
|
||||
|
||||
## 初始状态
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "pending",
|
||||
"phase": "init",
|
||||
"work_dir": "",
|
||||
"issues": {},
|
||||
"queue": [],
|
||||
"completed_actions": [],
|
||||
"context": {
|
||||
"total_issues": 0,
|
||||
"completed_count": 0,
|
||||
"failed_count": 0
|
||||
},
|
||||
"errors": [],
|
||||
"created_at": "ISO-8601",
|
||||
"updated_at": "ISO-8601"
|
||||
}
|
||||
```
|
||||
|
||||
## 状态转移
|
||||
|
||||
```
|
||||
pending
|
||||
↓
|
||||
init (Action-Init)
|
||||
↓
|
||||
running
|
||||
├→ list (Action-List) → Display issues
|
||||
├→ plan (Action-Plan) → Plan issues
|
||||
├→ execute (Action-Execute) → Execute solutions
|
||||
├→ back to list/plan/execute loop
|
||||
│
|
||||
└→ complete (Action-Complete) → Finalize
|
||||
↓
|
||||
completed
|
||||
```
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `status` | string | "pending"\|"running"\|"completed" - 全局状态 |
|
||||
| `phase` | string | "init"\|"listing"\|"planning"\|"executing"\|"complete" - 当前阶段 |
|
||||
| `work_dir` | string | 工作目录路径 |
|
||||
| `issues` | object | Issue 状态映射 `{issue_id: IssueState}` |
|
||||
| `queue` | array | 待执行队列 |
|
||||
| `completed_actions` | array | 已执行动作 ID 列表 |
|
||||
| `context` | object | 执行上下文信息 |
|
||||
| `errors` | array | 错误日志 |
|
||||
|
||||
## Issue 状态
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "ISS-xxx",
|
||||
"title": "Issue title",
|
||||
"status": "registered|planning|planned|executing|completed|failed",
|
||||
"solution_id": "SOL-xxx-1",
|
||||
"planned_at": "ISO-8601",
|
||||
"executed_at": "ISO-8601",
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
## Queue Item
|
||||
|
||||
```json
|
||||
{
|
||||
"item_id": "S-1",
|
||||
"issue_id": "ISS-xxx",
|
||||
"solution_id": "SOL-xxx-1",
|
||||
"status": "pending|executing|completed|failed"
|
||||
}
|
||||
```
|
||||
|
||||
## 验证函数
|
||||
|
||||
```javascript
|
||||
function validateState(state) {
|
||||
// Required fields
|
||||
if (!state.status) throw new Error("Missing: status");
|
||||
if (!state.phase) throw new Error("Missing: phase");
|
||||
if (!state.work_dir) throw new Error("Missing: work_dir");
|
||||
|
||||
// Valid status values
|
||||
const validStatus = ["pending", "running", "completed"];
|
||||
if (!validStatus.includes(state.status)) {
|
||||
throw new Error(`Invalid status: ${state.status}`);
|
||||
}
|
||||
|
||||
// Issues structure
|
||||
if (typeof state.issues !== "object") {
|
||||
throw new Error("issues must be object");
|
||||
}
|
||||
|
||||
// Queue is array
|
||||
if (!Array.isArray(state.queue)) {
|
||||
throw new Error("queue must be array");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## 状态持久化
|
||||
|
||||
```javascript
|
||||
// 保存状态
|
||||
function saveState(state) {
|
||||
const statePath = `${state.work_dir}/state.json`;
|
||||
Write(statePath, JSON.stringify(state, null, 2));
|
||||
|
||||
// 保存历史
|
||||
const historyPath = `${state.work_dir}/state-history.json`;
|
||||
const history = Read(historyPath).then(JSON.parse).catch(() => []);
|
||||
history.push({
|
||||
timestamp: new Date().toISOString(),
|
||||
phase: state.phase,
|
||||
completed_actions: state.completed_actions.length,
|
||||
issues_count: Object.keys(state.issues).length
|
||||
});
|
||||
Write(historyPath, JSON.stringify(history, null, 2));
|
||||
}
|
||||
|
||||
// 加载状态
|
||||
function loadState(workDir) {
|
||||
const statePath = `${workDir}/state.json`;
|
||||
return JSON.parse(Read(statePath));
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user