to-opencode
扮演一个超精简的架构师和协调器。此技能的核心目标是保持主交互会话的低成本:它只负责探索和设计,但绝不会在会话中直接编写规范、测试、模型或实现代码。所有这些工作都会在无状态的 CLI 子进程中完成(claude -p 和 opencode run),这些子进程被串联成一个生成的批处理脚本并在后台启动。会话的工作是架构和监督,而不是编写代码。
何时使用
当任务规模足够大,以至于直接在会话中编写代码会占用大量上下文(例如新功能、多文件重构、规范+测试+实现链条),或者用户明确要求“在不消耗上下文的情况下构建”时使用此技能。切勿将其用于一个快速的一文件修复;对于只需两个工具调用即可完成的任务,启动批处理脚本的开销得不偿失。
1. 架构角色与会话约束
- 零直接文件写入。 请勿调用
Write或Edit在本会话中起草规范、模型、测试或应用逻辑。如果你发现自己正要直接编写实现代码,请停止——该工作属于排队的 CLI 调用。 - 探索与规划。 使用子代理检查代码库,概述架构,并制定任务链(哪些文件,以什么顺序,以及它们之间的契约)。
- 项目特定的临时路径。 永远不要使用裸的
/tmp/路径——并发项目/会话会冲突。请派生一个项目范围的路径:PROJECT_NAME=$(basename "$PWD") TMP_DIR="/tmp/claude_opencode_${PROJECT_NAME}" mkdir -p "$TMP_DIR" - 项目范围的启动前清理与启动。 在运行批处理脚本之前,只清理本项目遗留下来的进程/快照——切勿触及其他项目的状态:
PROJECT_NAME=$(basename "$PWD") TMP_DIR="/tmp/claude_opencode_${PROJECT_NAME}" mkdir -p "$TMP_DIR" pkill -9 -f "opencode.*${PROJECT_NAME}" || true pkill -9 -f "${TMP_DIR}" || true find ~/.local/share/opencode/snapshot -maxdepth 2 -name "*${PROJECT_NAME}*" -exec rm -rf {} + 2>/dev/null || true bash "$TMP_DIR/master_batch.sh" >> ./batch_execution.log 2>&1 - 后台调用。 通过
Bash并使用run_in_background: true选项启动批处理脚本。 - 报告日志路径。 启动后立即告知用户
batch_execution.log的确切路径。 - 禁止轮询。 请勿使用
Monitor、睡眠循环或tail -f。依靠框架的完成通知,或用户直接询问。主动等待违背了将任务后台处理的目的。 - 执行后审查。 收到完成通知后:读取日志(
grep -E "FAIL|ABORT|DONE"),运行git log main..HEAD,如果需要修复,起草后续批处理脚本。
2. 脚本设置与配置
- 骨架。 以
set -u开头。定义PROJECT_NAME、TMP_DIR,执行mkdir -p "$TMP_DIR"。在一个专用的功能分支上工作——永远不要直接提交到main。将日志文件$TMP_DIR和本地配置添加到.git/info/exclude,以防止它们泄漏到仓库的跟踪状态中。 - 提供者超时修复。 在任何
opencode调用之前,确保存在一个本地的、被 git 排除的.opencode.json文件,并包含扩展的超时设置(本地模型响应第一个 token 可能很慢):{ "provider": { "ollama": { "options": { "chunkTimeout": 86400000, "headerTimeout": 86400000 } } } } - 引用式 heredoc 提示。 通过引用式 heredoc (
cat <<'PROMPT' > "$TMP_DIR/prompt.md") 将每个 CLI 提示写入一个项目范围的临时文件。引用可以防止变量展开和 Shell 转义错误;项目范围的路径可以防止跨会话覆盖。 - 范围限定的原子 Git 提交。 每次 CLI 调用后都进行范围限定的原子提交,仅添加该调用所触及的目录——切勿使用未限定范围的
git add .或git add -A:git add -A <工作区目录> .opencode && git commit -m "<常规提交信息>" -m "Co-Authored-By: Claude/Opencode" - 辅助函数。 定义 Shell 辅助函数(
cl用于claude调用,oc用于opencode调用),这些函数记录开始/退出时间戳,检查进程退出码,运行针对性测试,验证 SHA1 散列,并触发提交——使得脚本中的每个调用点都变成一行,而不是重复的代码块。 - 最终验证。 脚本以以下内容结束:完整的测试套件 + 代码检查 + 生产构建,每个都记录为 PASS/FAIL;删除本项目创建的所有
opencode会话;运行git log main..HEAD;清理$TMP_DIR;并输出echo "<功能名称> DONE"。
3. 阶段 A——规范、类型、模型与测试(claude CLI)
- 执行。 无状态的
claude调用,每个调用处理一个目标文件。切勿使用--resume或--continue——每个调用都获得一个全新、有界上下文。 - 标志。
--permission-mode acceptEdits --allowedTools Read Write Edit Glob Grep "Bash(node:*)"。 - 阶段 A 调用允许网络访问。 (可用于查找库 API、规范约定等)
- 提示结构。 保持简洁:短标题 + 共享契约片段 + 具体任务。共享契约(导出的名称、签名、类型)是每个调用所引用的唯一真实来源——不要让每个文件都重新发明自己版本的接口。
- 确定性转换交给
node -e,而不是 LLM。 解析固件、将数据集转换为 TS 常量——任何只有一个正确机械答案的操作都应使用普通 Node 脚本,而不是模型调用。 - 速率限制防护。 如果输出包含
session limit|usage limit|rate limit,立即中止主脚本。 - SHA1 验证。 在调用前后计算目标文件的散列值。散列没有变化 → 重试一次。仍然没有变化 → 中止主脚本。
4. 阶段 B——运行时实现(opencode run)
- 严格串行。 一次只运行一个
opencode进程——脚本内不能使用&后台运行。本地模型和共享快照状态不能容忍在此处并发运行。 - 禁用网络访问。 编写
.opencode/agent/<name>.md并包含permission: { webfetch: deny, websearch: deny },在每个调用上传递--agent <name>,并在每个提示中添加:“请勿使用 WebFetch、websearch 或访问互联网。” - PTY/TTY 包装器。 对每个调用进行包装,以防止 stdout 阻塞缓冲或跨会话冲突:
script -qec "opencode run --agent <name> --auto --print-logs -m ollama/qwen-agent:latest \"\$(cat \"$TMP_DIR/prompt.md\")\"" /dev/null >> "$LOG" 2>&1 < /dev/null - 上下文精简与提示。
- 将提示保持在 1–2 KB。
- 指定确切的文件路径进行读取;对于大文件,通过
grep -n偏移量指向行范围或describe(块,而不是整个文件。 - 只包含与此特定任务相关的契约片段。
- 追加:“保持探索简短:尽早编写文件,然后进行优化。”
- 测试执行与失败处理,在每个提示中明确说明:
- 给出确切的测试命令:
"通过以下命令运行测试以验证实现:npx ng test <proj> --watch=false --include='**/<file>.spec.ts'" - 禁止编辑测试文件或测试配置:
"永远不要修改 *.spec.ts 文件,并且永远不要创建或修改测试配置文件(例如 jest.config.js, karma.conf.js, vitest.config.ts, tsconfig.spec.json)。测试配置是静态的,由外部处理。" - 禁止自行修复工具链:
"如果测试执行失败,或者遇到测试配置/运行器错误,请勿尝试修复或生成测试配置文件。立即停止执行,打印失败详细信息,并以错误码退出。" - 指示它忽略来自其他尚未构建模块的不相关编译错误。
- 给出确切的测试命令:
- 粒度。 每个调用一个文件。将大文件拆分为增量式、仅追加的调用(“追加 X;请勿更改现有函数”)。
- 定向验证与中止防护。 在每个步骤后通过
oc辅助函数运行定向测试。如果非零退出,或者测试失败是由配置/运行器错误引起的,则立即中止主脚本。 - SHA1 验证。 与阶段 A 相同:目标文件未变化 → 最多重试 3 次,然后中止。
5. 无响应看门狗(30 分钟阈值)
在后台运行每个 opencode 调用,捕获其 PID($OC_PID),并每 60 秒轮询一次日志。
- 终止条件——两个条件必须同时成立:
- 最新的非空日志行包含
cleanup prune。 - 日志大小或内容在 1800 秒内未发生变化。
- 最新的非空日志行包含
- 切勿仅凭
cleanup prune就终止进程——它在启动和每小时例行输出中都会出现。切勿在日志还在增长时终止进程。 - 终止程序(仅限于项目范围):
kill -9 $OC_PID 2>/dev/null || pkill -9 -f "opencode.*${PROJECT_NAME}",并同时终止包装它的scriptPID。- 仅清理本项目的快照:
find ~/.local/share/opencode/snapshot -maxdepth 2 -name "*${PROJECT_NAME}*" -exec rm -rf {} + 2>/dev/null || true。 - 重试该步骤,最多重试 2 次。