name: skill-planner description: 从研究发现创建分阶段实施计划。当任务需要实施计划时调用。 allowed-tools: Task, Bash, Edit, Read, Write
原始上下文(现由子代理加载):
- .claude/context/core/formats/plan-format.md
- .claude/context/core/workflows/task-breakdown.md
原始工具(现由子代理使用):
- Read, Write, Edit, Glob, Grep
计划技能
薄包装,将计划创建委托给 planner-agent 子代理。
重要:此技能实现技能内部后处理模式。在子代理返回后, 此技能处理所有后处理操作(状态更新、工件链接、git提交)然后返回。 这消除了技能返回和协调器之间的“继续”提示问题。
上下文引用
引用(不急切加载):
- 路径:
.claude/context/core/formats/return-metadata-file.md- 元数据文件模式 - 路径:
.claude/context/core/patterns/postflight-control.md- 标记文件协议 - 路径:
.claude/context/core/patterns/file-metadata-exchange.md- 文件I/O助手 - 路径:
.claude/context/core/patterns/jq-escaping-workarounds.md- jq转义模式(问题 #1132)
注意:此技能是一个带有内部后处理的薄包装。上下文由委托代理加载。
触发条件
此技能在以下情况激活:
- 任务状态允许规划(未开始,已研究)
- 调用 /plan 命令
- 实施方法需要正式化
执行流程
阶段 1:输入验证
验证必需输入:
task_number- 必须提供并存在于 state.json 中- 任务状态必须允许规划
# 查找任务
task_data=$(jq -r --argjson num "$task_number" \
'.active_projects[] | select(.project_number == $num)' \
specs/state.json)
# 验证存在
if [ -z "$task_data" ]; then
return error "任务 $task_number 未找到"
fi
# 提取字段
language=$(echo "$task_data" | jq -r '.language // "general"')
status=$(echo "$task_data" | jq -r '.status')
project_name=$(echo "$task_data" | jq -r '.project_name')
description=$(echo "$task_data" | jq -r '.description // ""')
# 验证状态
if [ "$status" = "completed" ]; then
return error "任务已完成"
fi
阶段 2:预飞行状态更新
在调用子代理之前,更新任务状态为“planning”。
更新 state.json:
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg status "planning" \
--arg sid "$session_id" \
'(.active_projects[] | select(.project_number == '$task_number')) |= . + {
status: $status,
last_updated: $ts,
session_id: $sid
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
更新 TODO.md:使用 Edit 工具将状态标记从 [RESEARCHED] 或 [NOT STARTED] 更改为 [PLANNING]。
阶段 3:创建后处理标记
创建标记文件以防止过早终止:
# 确保任务目录存在
padded_num=$(printf "%03d" "$task_number")
mkdir -p "specs/${padded_num}_${project_name}"
cat > "specs/${padded_num}_${project_name}/.postflight-pending" << EOF
{
"session_id": "${session_id}",
"skill": "skill-planner",
"task_number": ${task_number},
"operation": "plan",
"reason": "后处理待处理:状态更新、工件链接、git提交",
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"stop_hook_active": false
}
EOF
阶段 4:准备委托上下文
为子代理准备委托上下文:
{
"session_id": "sess_{timestamp}_{random}",
"delegation_depth": 1,
"delegation_path": ["orchestrator", "plan", "skill-planner"],
"timeout": 1800,
"task_context": {
"task_number": N,
"task_name": "{project_name}",
"description": "{description}",
"language": "{language}"
},
"research_path": "{path to research report if exists}",
"metadata_file_path": "specs/{NNN}_{SLUG}/.return-meta.json"
}
阶段 5:调用子代理
关键:您必须使用 Task 工具生成子代理。
必需的工具调用:
Tool: Task (NOT Skill)
Parameters:
- subagent_type: "planner-agent"
- prompt: [Include task_context, delegation_context, research_path, metadata_file_path]
- description: "执行任务 {N} 的规划"
不要使用 Skill(planner-agent) - 这会失败。
子代理将:
- 加载规划上下文文件
- 分析任务需求和研究
- 分解为逻辑阶段
- 识别风险和缓解措施
- 在
specs/{NNN}_{SLUG}/plans/创建计划 - 将元数据写入
specs/{NNN}_{SLUG}/.return-meta.json - 返回简短文本摘要(非JSON)
阶段 6:解析子代理返回(读取元数据文件)
子代理返回后,读取元数据文件:
metadata_file="specs/${padded_num}_${project_name}/.return-meta.json"
if [ -f "$metadata_file" ] && jq empty "$metadata_file" 2>/dev/null; then
status=$(jq -r '.status' "$metadata_file")
artifact_path=$(jq -r '.artifacts[0].path // ""' "$metadata_file")
artifact_type=$(jq -r '.artifacts[0].type // ""' "$metadata_file")
artifact_summary=$(jq -r '.artifacts[0].summary // ""' "$metadata_file")
else
echo "错误:无效或缺少元数据文件"
status="failed"
fi
阶段 7:更新任务状态(后处理)
如果状态为“planned”,更新 state.json 和 TODO.md:
更新 state.json:
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg status "planned" \
'(.active_projects[] | select(.project_number == '$task_number')) |= . + {
status: $status,
last_updated: $ts,
planned: $ts
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
更新 TODO.md:使用 Edit 工具将状态标记从 [PLANNING] 更改为 [PLANNED]。
部分/失败时:保持状态为“planning”以便恢复。
阶段 8:链接工件
向 state.json 添加工件及摘要。
重要:使用两步 jq 模式以避免问题 #1132 转义错误。参见 jq-escaping-workarounds.md。
if [ -n "$artifact_path" ]; then
# 步骤 1:过滤出现有计划工件(使用“| not”模式以避免 != 转义 - 问题 #1132)
jq '(.active_projects[] | select(.project_number == '$task_number')).artifacts =
[(.active_projects[] | select(.project_number == '$task_number')).artifacts // [] | .[] | select(.type == "plan" | not)]' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
# 步骤 2:添加新计划工件
jq --arg path "$artifact_path" \
--arg type "$artifact_type" \
--arg summary "$artifact_summary" \
'(.active_projects[] | select(.project_number == '$task_number')).artifacts += [{"path": $path, "type": $type, "summary": $summary}]' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
fi
更新 TODO.md:添加计划工件链接:
- **计划**: [implementation-{NNN}.md]({artifact_path})
阶段 9:Git 提交
使用会话ID提交更改:
git add -A
git commit -m "任务 ${task_number}: 创建实施计划
会话: ${session_id}
共同作者:Claude Opus 4.5 <noreply@anthropic.com>"
阶段 10:清理
移除标记和元数据文件:
rm -f "specs/${padded_num}_${project_name}/.postflight-pending"
rm -f "specs/${padded_num}_${project_name}/.postflight-loop-guard"
rm -f "specs/${padded_num}_${project_name}/.return-meta.json"
阶段 11:返回简短摘要
返回简短文本摘要(非JSON)。示例:
计划为任务 {N} 创建:
- 定义了 {phase_count} 个阶段,估计 {estimated_hours} 小时
- 关键阶段:{phase names}
- 在 specs/{NNN}_{SLUG}/plans/implementation-{NNN}.md 创建计划
- 状态更新为 [PLANNED]
- 更改已提交
错误处理
输入验证错误
如果任务未找到或状态无效,立即返回错误消息。
元数据文件缺失
如果子代理未写入元数据文件:
- 保持状态为“planning”
- 不清理后处理标记
- 向用户报告错误
Git 提交失败
非阻塞:记录失败但继续成功响应。
jq 解析失败
如果 jq 命令因 INVALID_CHARACTER 或语法错误失败(问题 #1132):
- 记录到 errors.json:
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg sid "$session_id" \
--arg msg "jq parse error in postflight artifact linking" \
--argjson task "$task_number" \
'.errors += [{
"id": ("err_" + ($ts | gsub("[^0-9]"; ""))),
"timestamp": $ts,
"type": "jq_parse_failure",
"severity": "medium",
"message": $msg,
"context": {"session_id": $sid, "command": "/plan", "task": $task, "checkpoint": "GATE_OUT"},
"recovery": {"suggested_action": "Use two-step jq pattern from jq-escaping-workarounds.md", "auto_recoverable": true},
"fix_status": "unfixed"
}]' specs/errors.json > /tmp/errors.json && mv /tmp/errors.json specs/errors.json
- 使用两步模式重试(已在阶段 8 实现)
子代理超时
如果子代理超时(默认 1800秒),返回部分状态。 保持状态为“planning”以便恢复。
返回格式
此技能返回一个 简短文本摘要(非JSON)。JSON元数据被写入文件并在内部处理。
示例成功返回:
计划为任务 414 创建:
- 定义了 5 个阶段,估计 2.5 小时
- 覆盖:代理结构、执行流程、错误处理、示例、验证
- 在 specs/414_create_planner_agent/plans/implementation-001.md 创建计划
- 状态更新为 [PLANNED]
- 更改已提交,会话 sess_1736700000_abc123
示例部分返回:
任务 414 的计划部分创建:
- 在超时前定义了 3 个阶段中的 5 个
- 部分计划保存在 specs/414_create_planner_agent/plans/implementation-001.md
- 状态保持为 [PLANNING] - 运行 /plan 414 以完成