名称: skill-web-implementation 描述: 根据计划实现网络(Astro/Tailwind/TypeScript)变化。调用以处理网络语言实现任务。 允许工具: Task, Bash, Edit, Read, Write, Read(/tmp/.json), Bash(rm:)
原始上下文(现在由子代理加载):
- .opencode/context/project/web/README.md
- .opencode/context/project/web/domain/astro-framework.md
- .opencode/context/project/web/domain/tailwind-v4.md
- .opencode/context/project/web/standards/web-style-guide.md
- .opencode/context/project/web/patterns/astro-component.md
- .opencode/context/project/web/patterns/tailwind-patterns.md
- .opencode/context/project/web/patterns/accessibility-patterns.md
- .opencode/context/project/web/standards/performance-standards.md
- .opencode/context/project/web/tools/astro-cli-guide.md
- .opencode/context/project/web/tools/pnpm-guide.md
原始工具(现在由子代理使用):
- Read, Write, Edit, Glob, Grep
- Bash(pnpm build, pnpm check, npx astro check)
Web实现技能
薄包装器,将网络(Astro/Tailwind/TypeScript)实现委托给 web-implementation-agent 子代理。
重要: 此技能实现了技能内部的后处理模式。在子代理返回后, 此技能处理所有后处理操作(状态更新、工件链接、git提交)然后返回。 这消除了技能返回和协调器之间的“继续”提示问题。
上下文引用
引用(不要急切加载):
- 路径:
.opencode/context/core/formats/return-metadata-file.md- 元数据文件模式 - 路径:
.opencode/context/core/patterns/postflight-control.md- 标记文件协议 - 路径:
.opencode/context/core/patterns/file-metadata-exchange.md- 文件I/O助手 - 路径:
.opencode/context/core/patterns/jq-escaping-workarounds.md- jq转义模式(问题 #1132)
注意:此技能是一个具有内部后处理的薄包装器。上下文由委托的代理加载。
触发条件
此技能在以下情况激活:
- 任务语言是“web”
- /implement 命令针对网络(Astro/Tailwind/TypeScript)任务
- 页面、组件、布局或网页样式需要创建或修改
执行
0. 预检状态更新
在委托给子代理之前,将任务状态更新为“implementing”。
引用: @.opencode/context/core/patterns/inline-status-update.md
更新 state.json:
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg status "implementing" \
--arg sid "$session_id" \
'(.active_projects[] | select(.project_number == '$task_number')) |= . + {
status: $status,
last_updated: $ts,
session_id: $sid,
started: $ts
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
更新 TODO.md: 使用编辑工具将状态标记从 [PLANNED] 更改为 [IMPLEMENTING]。
更新计划文件(如果存在):更新计划元数据中的状态字段:
# 查找最新计划文件
plan_file=$(ls -1 "specs/${padded_num}_${project_name}/plans/implementation-"*.md 2>/dev/null | sort -V | tail -1)
if [ -n "$plan_file" ] && [ -f "$plan_file" ]; then
sed -i "s/^\- \*\*Status\*\*: \[.*\]$/- **Status**: [IMPLEMENTING]/" "$plan_file"
fi
创建后处理标记:
# 确保任务目录存在
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-web-implementation",
"task_number": ${task_number},
"operation": "implement",
"reason": "后处理待定:状态更新、工件链接、git提交",
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"stop_hook_active": false
}
EOF
1. 输入验证
验证必需输入:
task_number- 必须提供并存在于 state.json 中- 任务状态必须允许实现(planned, implementing, partial)
# 查找任务
task_data=$(jq -r --arg num "$task_number" \
'.active_projects[] | select(.project_number == ($num | tonumber))' \
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 [ "$language" != "web" ]; then
return error "任务 $task_number 不是网络任务"
fi
# 验证状态
if [ "$status" = "completed" ]; then
return error "任务已完成"
fi
2. 上下文准备
准备委托上下文:
{
"session_id": "sess_{timestamp}_{random}",
"delegation_depth": 1,
"delegation_path": ["orchestrator", "implement", "skill-web-implementation"],
"timeout": 3600,
"task_context": {
"task_number": N,
"task_name": "{project_name}",
"description": "{description}",
"language": "web"
},
"plan_path": "specs/{NNN}_{SLUG}/plans/implementation-{NNN}.md",
"metadata_file_path": "specs/{NNN}_{SLUG}/.return-meta.json"
}
3. 调用子代理
关键: 您必须使用 Task 工具来生成子代理。
此技能前驱中的 agent 字段指定了目标:web-implementation-agent
必需工具调用:
工具: Task(不是 Skill)
参数:
- subagent_type: "web-implementation-agent"
- prompt: [包括 task_context, delegation_context, plan_path]
- description: "为任务 {N} 执行网络实现"
不要使用 Skill(web-implementation-agent) - 这会失败。
代理位于 .opencode/agents/,而不是 .opencode/skills/。
Skill 工具只能调用来自 .opencode/skills/ 的技能。
子代理将:
- 加载网络特定上下文文件(Astro框架、Tailwind v4、样式指南等)
- 创建/修改 .astro, .ts, .tsx, .css 文件
- 执行构建验证(pnpm build, pnpm check)
- 处理 TypeScript 和 Astro 错误
- 创建实现摘要
- 将元数据写入
specs/{NNN}_{SLUG}/.return-meta.json - 返回简短的文本摘要(不是 JSON)
3a. 验证子代理返回格式
重要: 检查子代理是否意外地将 JSON 返回到控制台(v1 模式)而不是写入文件(v2 模式)。
如果子代理的文本返回解析为有效 JSON,则记录警告:
# 检查子代理返回是否像 JSON(以 { 开头且是有效 JSON)
subagent_return="$SUBAGENT_TEXT_RETURN"
if echo "$subagent_return" | grep -q '^{' && echo "$subagent_return" | jq empty 2>/dev/null; then
echo "警告:子代理将 JSON 返回到控制台而不是写入元数据文件。"
echo "这表明代理可能有过时的指令(v1 模式而不是 v2)。"
echo "技能将继续读取元数据文件,但这应修复。"
fi
此验证:
- 不会导致操作失败(继续读取元数据文件)
- 记录警告以供调试
- 指示子代理指令需要更新
- 允许优雅处理混合 v1/v2 代理
4. 解析子代理返回(读取元数据文件)
子代理返回后,读取元数据文件:
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")
phases_completed=$(jq -r '.metadata.phases_completed // 0' "$metadata_file")
phases_total=$(jq -r '.metadata.phases_total // 0' "$metadata_file")
# 提取 completion_data 字段(如果存在)
completion_summary=$(jq -r '.completion_data.completion_summary // ""' "$metadata_file")
roadmap_items=$(jq -c '.completion_data.roadmap_items // []' "$metadata_file")
else
echo "错误:无效或缺少元数据文件"
status="failed"
fi
验证元数据包含必需字段:
- 状态是其中之一:implemented, partial, failed, blocked
- 摘要非空且 <100 个令牌
- 工件数组存在(源文件、摘要)
- 元数据包含 session_id, agent_type, 委托信息
5. 后处理状态更新
实现后,根据结果更新任务状态。
引用: @.opencode/context/core/patterns/inline-status-update.md
如果 result.status == “implemented”:
更新 state.json 为 “completed” 并添加 completion_data 字段(两步模式用于问题 #1132):
# 步骤1:更新状态和时间戳
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg status "completed" \
'(.active_projects[] | select(.project_number == '$task_number')) |= . + {
status: $status,
last_updated: $ts,
completed: $ts
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
# 步骤2:添加 completion_summary(完成任务的始终必需)
if [ -n "$completion_summary" ]; then
jq --arg summary "$completion_summary" \
'(.active_projects[] | select(.project_number == '$task_number')).completion_summary = $summary' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
fi
# 步骤3:添加 roadmap_items(如果存在且非空)
if [ "$roadmap_items" != "[]" ] && [ -n "$roadmap_items" ]; then
jq --argjson items "$roadmap_items" \
'(.active_projects[] | select(.project_number == '$task_number')).roadmap_items = $items' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
fi
# 步骤4:过滤掉现有摘要工件(使用“| not”模式避免 != 转义 - 问题 #1132)
jq '(.active_projects[] | select(.project_number == '$task_number')).artifacts =
[(.active_projects[] | select(.project_number == '$task_number')).artifacts // [] | .[] | select(.type == "summary" | not)]' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
# 步骤5:添加新摘要工件
jq --arg path "$artifact_path" \
'(.active_projects[] | select(.project_number == '$task_number')).artifacts += [{"path": $path, "type": "summary"}]' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
更新 TODO.md:
- 将状态标记从
[IMPLEMENTING]更改为[COMPLETED] - 为 TODO.md 剥离 specs/ 前缀(TODO.md 在 specs/ 内):
todo_link_path="${artifact_path#specs/}" - 添加摘要工件链接:
- **Summary**: [implementation-summary-{DATE}.md]({todo_link_path})
更新计划文件(如果存在):将状态字段更新为 [COMPLETED]:
plan_file=$(ls -1 "specs/${padded_num}_${project_name}/plans/implementation-"*.md 2>/dev/null | sort -V | tail -1)
if [ -n "$plan_file" ] && [ -f "$plan_file" ]; then
sed -i "s/^\- \*\*Status\*\*: \[.*\]$/- **Status**: [COMPLETED]/" "$plan_file"
fi
如果 result.status == “partial”:
更新 state.json 与恢复点(保持状态为 “implementing”):
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg phase "$completed_phase" \
'(.active_projects[] | select(.project_number == '$task_number')) |= . + {
last_updated: $ts,
resume_phase: ($phase | tonumber + 1)
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
TODO.md 保持为 [IMPLEMENTING]。
更新计划文件(如果存在):将状态字段更新为 [PARTIAL]:
plan_file=$(ls -1 "specs/${padded_num}_${project_name}/plans/implementation-"*.md 2>/dev/null | sort -V | tail -1)
if [ -n "$plan_file" ] && [ -f "$plan_file" ]; then
sed -i "s/^\- \*\*Status\*\*: \[.*\]$/- **Status**: [PARTIAL]/" "$plan_file"
fi
失败时: 不要运行后处理。保持状态为 “implementing” 以便重试。不要更新计划文件(保留为 [IMPLEMENTING] 以便重试)。
6. Git提交
使用会话ID提交更改:
git add -A
git commit -m "任务 ${task_number}: 完成实现
会话: ${session_id}
共同作者: Claude Opus 4.5 <noreply@anthropic.com>"
7. 清理
后处理处理后删除标记和元数据文件:
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"
8. 返回简要摘要
返回描述实现结果的简短文本摘要(不是 JSON)。
返回格式
此技能返回简短的文本摘要(不是 JSON)。JSON 元数据写入文件并在内部处理。
成功返回示例:
任务10的网络实现完成:
- 所有3个阶段执行,构建通过干净
- 创建关于页面,包含英雄部分和团队网格
- 在 specs/10_create_about_page/summaries/implementation-summary-20260205.md 创建摘要
- 状态更新为 [COMPLETED]
- 使用会话 sess_1770319142_a293c5 提交更改
部分返回示例:
任务10的网络实现部分完成:
- 3个阶段中的第1-2阶段执行
- 第3阶段被阻止:ContactForm 组件中的 TypeScript 错误
- 在 specs/10_create_about_page/summaries/implementation-summary-20260205.md 部分摘要
- 状态保持 [IMPLEMENTING] - 运行 /implement 10 以恢复
错误处理
输入验证错误
如果任务未找到、语言错误或状态无效,立即返回失败状态。
子代理错误
逐字传递子代理的错误返回。
超时
如果子代理超时(默认3600秒),返回部分状态。