xiaowei-system/skills/autonomous-ai-agents/opencode/SKILL.md

25 KiB
Raw Blame History

name description version author license metadata
opencode Delegate coding to OpenCode CLI (features, PR review). 1.3.0 Hermes Agent MIT
hermes
tags related_skills
Coding-Agent
OpenCode
Autonomous
Refactoring
Code-Review
claude-code
codex
hermes-agent

OpenCode CLI

Use OpenCode as an autonomous coding worker orchestrated by Hermes terminal/process tools. OpenCode is a provider-agnostic, open-source AI coding agent with a TUI and CLI.

When to Use — THE FIRST RULE

CRITICAL RULE: Default to opencode for ALL coding work. Do NOT self-execute (patch/sed/manual edit) unless the user explicitly says "你直接改" or the change is trivially mechanical (<3 lines). The user called this out explicitly: every time I think "it's small enough" and patch code myself instead of delegating to opencode, it's wrong. Load this skill at session start to reinforce this rule.

When to use opencode

  • ALL coding tasks (features, bug fixes, refactoring, tests, code review/inspection)
  • Code audit/inspection — Use opencode run '全面审查代码...' for systematic code review. Works well for finding security issues, bugs, code quality problems.
  • Long-running coding sessions with progress checks
  • Parallel task execution in isolated workdirs

When NOT to use opencode

  • User explicitly says "你直接改" or "直接改吧"
  • Trivially mechanical changes (<3 lines, no logic changed)
  • Config-only changes (e.g. editing YAML/JSON configs)

Installation

When the user asks to install/update OpenCode, always run the install command — do not just display docs. Execute immediately.

Install / Upgrade (npm)

# npm may not be in PATH — use nvm-managed node's npm
export NVM_DIR=~/.config/nvm
source $NVM_DIR/nvm.sh
nvm use v22.22.2

# Install to custom prefix (avoids /usr/lib/node_modules permission issues)
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
~/.npm-global/bin/npm install -g opencode-ai@latest --registry https://registry.npmmirror.com

Timeout: 300s — the install can take several minutes.

Wrapper script (required for custom provider injection)

Create /home/muc/.local/bin/opencode as a wrapper that patches models.json and injects the API key on every call:

cat > ~/.local/bin/opencode << 'WRAPPER'
#!/bin/bash
export PATH="$HOME/.npm-global/bin:$HOME/.opencode/bin:$PATH"
python3 -c "
import json, os
p = os.path.expanduser('~/.cache/opencode/models.json')
try:
    d = json.load(open(p))
    if 'qwen' not in d:
        d['qwen'] = {
            'id': 'qwen',
            'env': ['QWEN_API_KEY'],
            'npm': '@ai-sdk/openai-compatible',
            'api': 'http://127.0.0.1:3000/v1',
            'name': 'Qwen',
            'doc': 'http://127.0.0.1:3000',
            'models': {
                'qwen3-coder-480b-a35b-instruct': {
                    'id': 'qwen/qwen3-coder-480b-a35b-instruct',
                    'name': 'Qwen3-Coder-480B',
                    'family': 'qwen',
                    'attachment': True,
                    'reasoning': True,
                    'tool_call': True,
                    'temperature': True,
                    'knowledge': '2025-04',
                    'modalities': {'input': ['text'], 'output': ['text']},
                    'open_weights': True,
                    'limit': {'context': 1000000, 'output': 65536}
                }
            }
        }
        json.dump(d, open(p, 'w'), indent=2)
except: pass
"
export QWEN_API_KEY=sk-YOUR-KEY-HERE
exec /home/muc/.npm-global/bin/opencode "$@"
WRAPPER
chmod +x ~/.local/bin/opencode

Then ensure ~/.local/bin is first in PATH (add to ~/.bashrc if needed):

export PATH="$HOME/.local/bin:$PATH"

Verify

opencode --version   # should print e.g. 1.15.10

DeepSeek Provider (This System — Verified 2026-06-20)

Binary: /home/muc/bin/opencode (standalone ELF, v1.15.10, NOT npm-installed)

Setup in ~/.bashrc:

export DEEPSEEK_API_KEY="sk-b12...6bbd"
alias opencode='DEEPSEEK_API_KEY="sk-b12...6bbd" opencode -m deepseek/deepseek-v4-flash'

Available DeepSeek models:

deepseek/deepseek-chat
deepseek/deepseek-reasoner
deepseek/deepseek-v4-flash      # alias default
deepseek/deepseek-v4-pro

Auth check:

opencode providers list
# → Credentials: 0
# → Environment: DEEPSEEK_API_KEY  ✓  (1 env var detected automatically)

Important: The binary is at /home/muc/bin/opencode, which takes PATH precedence over ~/.local/bin/opencode (the old npm-installed wrapper). No wrapper script needed — DeepSeek is a built-in recognized provider. The alias handles env var injection + default model.

If opencode models deepseek returns "Error: Provider not found", the env var isn't reaching the binary. Explicitly pass: DEEPSEEK_API_KEY="sk-b12...6bbd" opencode models deepseek to verify.

Custom Providers (Local / OpenAI-Compatible APIs)

OpenCode has a cached provider registry at ~/.cache/opencode/models.json. To add a custom OpenAI-compatible API (e.g. a local LLM server, a proxy, or a provider not natively supported), add an entry to this file.

Step 1: Add the provider to models.json

import json
data = json.load(open(os.path.expanduser("~/.cache/opencode/models.json")))
data["provider-id"] = {
    "id": "provider-id",
    "env": ["PROVIDER_ID_API_KEY"],       # env var name OpenCode will look for
    "npm": "@ai-sdk/openai-compatible",
    "api": "https://your-api-base-url/v1", # must include /v1 suffix
    "name": "Provider Display Name",
    "doc": "https://provider.docs.url",
    "models": {
        "model-slug": {
            "id": "provider-id/model-slug",  # full id shown in `opencode models`
            "name": "Model Display Name",
            "family": "model-family",
            "attachment": True,
            "reasoning": True,              # or False
            "tool_call": True,
            "temperature": True,
            "knowledge": "2025-04",
            "release_date": "2025-04-29",
            "modalities": {"input": ["text"], "output": ["text"]},
            "open_weights": True,
            "limit": {"context": 1000000, "output": 65536}
        }
    }
}
json.dump(data, open(os.path.expanduser("~/.cache/opencode/models.json"), "w"), indent=2)

Step 2: Set the environment variable and verify

export PROVIDER_ID_API_KEY=your-api-key-here
opencode models provider-id                    # should list your model(s)
opencode run 'Respond with exactly: OK' --model provider-id/model-slug  # smoke test

Step 3 (optional): Make it permanent

Add to ~/.bashrc or ~/.zshrc:

export PROVIDER_ID_API_KEY=your-api-key-here

What does NOT work

  • ~/.config/opencode/config.json with a providers key — throws "Unrecognized key: providers".
  • opencode providers login with a custom base URL — times out in non-interactive shells; also throws undefined is not an object when a URL is passed as a positional argument.
  • provider/model format for models not registered in models.jsonProviderModelNotFoundError.

Verified working pattern

For a local server at http://127.0.0.1:3000/v1 with a Qwen model:

env var:    QWEN_API_KEY
provider:   qwen  (from models.json entry)
model:      qwen/qwen3-coder-480b-a35b-instruct  (full ID including provider prefix)
api:        http://127.0.0.1:3000/v1
npm:        @ai-sdk/openai-compatible

A reference implementation is saved at references/custom-provider-models-json.md.

Because models.json is reset on every opencode run (not just interactive mode), and interactive login doesn't work in non-interactive shells, the only reliable approach for automation/script environments is a wrapper script.

On this system, the wrapper is already installed at /home/muc/.local/bin/opencode. It:

  1. Patches ~/.cache/opencode/models.json to add the qwen provider on every invocation
  2. Injects QWEN_API_KEY into the environment
  3. Delegates to the real binary at /home/muc/.local/bin/opencode-bin

To reinstall or inspect:

# Backup original binary
mv /home/muc/.local/bin/opencode /home/muc/.local/bin/opencode-bin

# Create wrapper at /home/muc/.local/bin/opencode
cat > /home/muc/.local/bin/opencode << 'WRAPPER'
#!/bin/bash
python3 -c "
import json,os
p=os.path.expanduser('~/.cache/opencode/models.json')
d=json.load(open(p))
if 'qwen' not in d:
    d['qwen']={'id':'qwen','env':['QWEN_API_KEY'],'npm':'@ai-sdk/openai-compatible','api':'http://127.0.0.1:3000/v1','name':'Qwen','doc':'http://127.0.0.1:3000','models':{'qwen3-coder-480b-a35b-instruct':{'id':'qwen/qwen3-coder-480b-a35b-instruct','name':'Qwen3-Coder-480B','family':'qwen','attachment':True,'reasoning':True,'tool_call':True,'temperature':True,'knowledge':'2025-04','modalities':{'input':['text'],'output':['text']},'open_weights':True,'limit':{'context':1000000,'output':65536}}}
    json.dump(d,open(p,'w'),indent=2)
" 2>/dev/null
export QWEN_API_KEY=YOUR_KEY_HERE
exec /home/muc/.local/bin/opencode-bin "$@"
WRAPPER
chmod +x /home/muc/.local/bin/opencode

After installing, usage is simply:

opencode run 'Your task here' --model qwen/qwen3-coder-480b-a35b-instruct
# No env var setup needed — wrapper handles it

Provider Setup — Local / Custom API Endpoints

OpenCode syncs ~/.cache/opencode/models.json from network on every run, overwriting any custom provider entries. To register a custom provider (e.g. local LLM endpoint):

  1. Patch models.json before each opencode run. Use a wrapper script (see below).
  2. Provider must use @ai-sdk/openai-compatible as the npm package.
  3. Environment variable name must match the env field in the provider config.

Critical: Wrapper Script Required

Because models.json is re-synced on every run, manual edits are never persistent. The fix is a wrapper script at ~/.local/bin/opencode that:

  1. Patches models.json to add the custom provider entry
  2. Sets the required API key environment variable
  3. Delegates to the real binary

Example wrapper for a local Qwen endpoint:

#!/bin/bash
# Patch custom provider into models.json (survives re-sync)
python3 -c "
import json, os
p = os.path.expanduser('~/.cache/opencode/models.json')
d = json.load(open(p))
if 'qwen' not in d:
    d['qwen'] = {
        'id': 'qwen',
        'env': ['QWEN_API_KEY'],
        'npm': '@ai-sdk/openai-compatible',
        'api': 'http://127.0.0.1:3000/v1',
        'name': 'Qwen',
        'doc': 'http://127.0.0.1:3000',
        'models': {
            'qwen3-coder-480b-a35b-instruct': {
                'id': 'qwen/qwen3-coder-480b-a35b-instruct',
                'name': 'Qwen3-Coder-480B',
                'family': 'qwen',
                'attachment': True,
                'reasoning': True,
                'tool_call': True,
                'temperature': True,
                'knowledge': '2025-04',
                'modalities': {'input': ['text'], 'output': ['text']},
                'open_weights': True,
                'limit': {'context': 1000000, 'output': 65536}
            }
        }
    }
    json.dump(d, open(p, 'w'), indent=2)
" 2>/dev/null

export QWEN_API_KEY=sk-YOUR-KEY-HERE
exec /path/to/opencode-bin "$@"

The real binary at ~/.local/bin/opencode-bin (renamed from original) is called via exec to replace the wrapper process.

Dual Binary Conflict (This System)

There are TWO opencode binaries on this system:

  • /home/muc/.local/bin/opencode — npm-installed (1.14.39), wrapper script
  • /home/muc/.opencode/bin/opencode — older standalone ELF binary (2025-06-27)

The older binary takes precedence if it appears first in PATH, causing:

  • "Error: agent coder not found" in TUI mode
  • Custom provider missing even with wrapper
  • Wrapper never invoked

Fix: Remove the older binary or ensure ~/.local/bin precedes ~/.opencode/bin in PATH:

export PATH=$HOME/.local/bin:$HOME/.opencode/bin:$PATH

If still broken, rename/remove the old binary:

mv /home/muc/.opencode/bin/opencode /home/muc/.opencode/bin/opencode.bak

PATH Ordering Pitfall

If /home/muc/.opencode/bin appears before ~/.local/bin in PATH, the old opencode binary is invoked instead of the wrapper. This causes "Error: agent coder not found" in TUI mode or missing custom providers. Fix: ensure ~/.local/bin comes first in PATH (see Dual Binary Conflict above).

TUI Mode Usage

启动并选模型

# 方法1命令行直接指定推荐
opencode -m qwen/qwen3-coder-480b-a35b-instruct /path/to/project

# 方法2启动后再选
opencode /path/to/project
# 按 Ctrl+X M → 输入 qwen/qwen3-coder-480b-a35b-instruct → 回车

TUI 启动后如果提示 "No provider selected",按 Ctrl+X M 切换 model。

退出 TUI

  • Ctrl+C(不要输入 /exit,那是无效命令)

Troubleshooting

"Error: agent coder not found" — Two Distinct Causes

Cause 1 (documented): Old binary at /home/muc/.opencode/bin/opencode takes precedence over wrapper. Fix: remove or rename it.

Cause 2 (v0.0.55 architecture bug): The agent subsystem itself fails to initialize even with the correct binary. This happens in ALL modes (TUI, -p, run). The error fires before any provider/model resolution.

Diagnosis:

# Any of these failing with "agent coder not found" = Cause 2
opencode --version    # prints 0.0.55
opencode -p "hello"
opencode run "hello"
opencode              # TUI mode

Fix for Cause 2: Update opencode to a newer version. The bug is in v0.0.55's agent initialization logic. Reinstall:

curl -LsSf https://ast.opencode.ai/opencode-installer.sh | sh
# or
npm install -g opencode-ai@latest

Workaround: None confirmed working for v0.0.55. The user (牧尘) must update opencode before delegation can proceed.

Direct Orchestration Workflow (No Feishu Bridge)

When the user says "you directly control opencode" — run opencode as a subprocess via terminal() with monitoring via process(). No Feishu or ACP bridge required.

Pattern:

# Start in background with pty, get session_id
terminal(command="opencode -c /path/to/project", background=true, pty=true, notify_on_complete=True)
# → Returns session_id

# Monitor progress
process(action="poll", session_id="<id>")
process(action="log", session_id="<id>")

# Send input
process(action="submit", session_id="<id>", data="task description")

# Exit
process(action="write", session_id="<id>", data="\x03")

Timeout: 300s for most tasks. For opencode v0.0.55 with "agent coder not found" — no timeout will help; must update opencode first.

User preference: OpenCode has no Feishu integration. I (小唯) control opencode directly as subprocess orchestrator — no intermediary channel needed.

Common Flags

Flag Use
-m, --model provider/model Model to use (TUI and run mode)
opencode run 'prompt' One-shot execution, exit when done
-p, --prompt 'prompt' Non-interactive prompt (TUI mode)
-c, --cwd DIR Working directory
-c, --continue Continue last session
-s, --session ID Continue specific session
-d, --debug Debug logging
--thinking Show model thinking blocks

Pitfalls

  • ⚠️ Behavioral trap: self-executing instead of delegating — When a coding task comes up, the default reaction is to fix it myself (patch, sed, manual edit). This is WRONG. Always ask: "Is this coding work? → opencode run." The user explicitly expects opencode to be the default for code changes. Exception: user says "你直接改" or the change is purely mechanical (<3 lines).
  • ⚠️ Task splitting pattern for complex tasks — A single opencode run with a multi-part task (e.g. "检查 A, B, C 系统") will time out at 300s. Fix: split into focused sub-tasks and run in parallel via delegate_task:
    # ❌ Don't: single opencode run with 4 sub-tasks
    opencode run "检查 A, B, C, D 四个系统"
    # → times out after 300s, partial results
    
    # ✅ Do: split into parallel focused tasks
    delegate_task [goal="opencode run '检查系统A'", goal="opencode run '检查系统B'"]
    
    Each sub-task should be focused enough to complete within 180s. If a sub-task still times out, split further.
  • Never use --model — it's -m for both TUI and run modes.
  • models.json is re-synced on every run — custom providers require wrapper patching.
  • /exit is not valid in TUI — use Ctrl+C to exit.
  • Enter may need to be pressed twice to submit in TUI.
  • PATH mismatch: if opencode --help shows different flags than opencode -m, the wrong binary is being called. Check which -a opencode and PATH order.
  • Interactive TUI requires no pty flag — just run opencode or opencode /path/to/project.
  • models.json location: ~/.cache/opencode/models.json — not in config dir.
  • delegate_task 超时600s:复杂任务(如多文件 TUI 应用)容易超时,超时后直接用 opencode run 执行,不要重试 delegate。
  • ⚠️ Workdir 不支持中文字符workdir 路径含中文(如 /home/muc/mc/小唯/)会报 Blocked: workdir contains disallowed character。用 --cwd 指定不含中文的路径,或在无中文目录执行。
  • ⚠️ Go build: 必须在模块目录内编译cd /tmp/memoryweave/go && go build -o zhiyid-new ./cmd/zhiyid。从根目录 go build ./... 会报 no required module provides package 那是 lint 的假阳性;只在模块目录内的编译报错才是真正的编译错误。
  • ⚠️ Go: 跨包调用需导出函数 — 私有函数(如 computeBM25Score)在自身包内可用,被其他包调用时必须导出为 ComputeBM25Score 并用 package.ComputeBM25Score() 语法。新增 sort.Slice 需要 import "sort"。
  • ⚠️ Go: switch/case 预声明变量 — 如果 switch 内多个 case 需要读/写同一变量且 case 有 return必须 var results Type 在 switch 前预声明;不能在 case 内用 :=
  • ⚠️ 数据库优先于启发式分析:当修改数据验证/分类逻辑时,先检查数据库是否已有该字段。

Workflow Selection

场景 推荐方式
单次 bounded 任务(写文件/跑脚本) opencode run '...'
迭代式开发TUI 交互) opencode 交互模式 + process()
复杂多文件项目 opencode run + 分步任务拆分
delegate_task 超时 切换 opencode run,不要重试
远程服务器部署(代码在远端) 1) sync remote→local 2) opencode edit 3) scp back 4) restart service 5) test via SSH

Remote Deployment Workflow

When the target codebase runs on a remote server (not local), use this five-step pattern:

# Step 1: Sync files from remote to local workdir
sshpass -p '$PASS' ssh root@$HOST "cat /path/to/file.py" > ./file.py

# Step 2: opencode edits local files
opencode run 'fix bug in file.py' -m deepseek/deepseek-v4-flash

# Step 3: Deploy back to server
sshpass -p '$PASS' scp ./file.py root@$HOST:/remote/path/
sshpass -p '$PASS' ssh root@$HOST "chown www:www /remote/path/file.py"

# Step 4: Restart the server process
sshpass -p '$PASS' ssh root@$HOST "pkill -f gunicorn && sleep 2 && cd /remote/path && nohup ... &"

# Step 5: Test via SSH curl
sshpass -p '$PASS' ssh root@$HOST "curl -s -X POST http://127.0.0.1:8080/api/endpoint ..."

Key differences from local development:

  • opencode run edits local copies, not remote files — after fix, must scp back
  • Service restart is manual (no hot reload on most production servers)
  • Test via SSH pipe, not local curl
  • Python test scripts: write to /tmp/test.py, then cat /tmp/test.py | sshpass -p '$PASS' ssh host "python3" to avoid heredoc quoting issues with f-strings/emojis

路径规范

  • 工作目录避免中文(--cwd 不支持中文路径,会报 disallowed character
  • 涉及中文路径时cd 到 /home/muc 再执行,用绝对路径访问目标
  • 例:opencode run 'task' --cwd /home/muc(而不是 /home/muc/mc/小唯/

One-Shot Tasks

Use opencode run for bounded, non-interactive tasks.

Timeout: Set timeout=300 for most tasks. For complex multi-file tasks, split into smaller sub-tasks (see Pitfalls) rather than raising timeout — a 300s timeout for a focused task is usually enough. If a task regularly needs more time, it's too broad.

terminal(command="opencode run 'Add retry logic to API calls and update tests' --model qwen/qwen3-coder-480b-a35b-instruct", workdir="~/project")

Attach context files with -f:

terminal(command="opencode run 'Review this config for security issues' -f config.yaml -f .env.example", workdir="~/project")

Show model thinking with --thinking:

terminal(command="opencode run 'Debug why tests fail in CI' --thinking", workdir="~/project")

Force a specific model:

terminal(command="opencode run 'Refactor auth module' --model openrouter/anthropic/claude-sonnet-4", workdir="~/project")

Interactive Sessions (Background)

For iterative work requiring multiple exchanges, start the TUI in background:

terminal(command="opencode", workdir="~/project", background=true, pty=true)
# Returns session_id

# Send a prompt
process(action="submit", session_id="<id>", data="Implement OAuth refresh flow and add tests")

# Monitor progress
process(action="poll", session_id="<id>")
process(action="log", session_id="<id>")

# Send follow-up input
process(action="submit", session_id="<id>", data="Now add error handling for token expiry")

# Exit cleanly — Ctrl+C
process(action="write", session_id="<id>", data="\x03")
# Or just kill the process
process(action="kill", session_id="<id>")

Important: Do NOT use /exit — it is not a valid OpenCode command and will open an agent selector dialog instead. Use Ctrl+C (\x03) or process(action="kill") to exit.

TUI Keybindings

Key Action
Enter Submit message (press twice if needed)
Tab Switch between agents (build/plan)
Ctrl+P Open command palette
Ctrl+X L Switch session
Ctrl+X M Switch model
Ctrl+X N New session
Ctrl+X E Open editor
Ctrl+C Exit OpenCode

Resuming Sessions

After exiting, OpenCode prints a session ID. Resume with:

terminal(command="opencode -c", workdir="~/project", background=true, pty=true)  # Continue last session
terminal(command="opencode -s ses_abc123", workdir="~/project", background=true, pty=true)  # Specific session

Procedure

  1. Verify tool readiness:
    • terminal(command="opencode --version")
    • terminal(command="opencode providers list")
  2. For bounded tasks, use opencode run '...' (no pty needed).
  3. For iterative tasks, start opencode with background=true, pty=true.
  4. Monitor long tasks with process(action="poll"|"log").
  5. If OpenCode asks for input, respond via process(action="submit", ...).
  6. Exit with process(action="write", data="\x03") or process(action="kill").
  7. Summarize file changes, test results, and next steps back to user.

PR Review Workflow

OpenCode has a built-in PR command:

terminal(command="opencode pr 42", workdir="~/project", pty=true)

Or review in a temporary clone for isolation:

terminal(command="REVIEW=$(mktemp -d) && git clone https://github.com/user/repo.git $REVIEW && cd $REVIEW && opencode run 'Review this PR vs main. Report bugs, security risks, test gaps, and style issues.' -f $(git diff origin/main --name-only | head -20 | tr '\n' ' ')", pty=true)

Parallel Work Pattern

Use separate workdirs/worktrees to avoid collisions:

terminal(command="opencode run 'Fix issue #101 and commit'", workdir="/tmp/issue-101", background=true, pty=true)
terminal(command="opencode run 'Add parser regression tests and commit'", workdir="/tmp/issue-102", background=true, pty=true)
process(action="list")

Session & Cost Management

List past sessions:

terminal(command="opencode session list")

Check token usage and costs:

terminal(command="opencode stats")
terminal(command="opencode stats --days 7 --models anthropic/claude-sonnet-4")

Smoke test:

terminal(command="opencode run 'Respond with exactly: OPENCODE_SMOKE_OK'")

Success criteria:

  • Output includes OPENCODE_SMOKE_OK
  • Command exits without provider/model errors
  • For code tasks: expected files changed and tests pass
  • If authentication was via environment variables, opencode providers list should show "1 environment variable"

Rules

  1. Prefer opencode run for one-shot automation — it's simpler and doesn't need pty.
  2. Use interactive background mode only when iteration is needed.
  3. Always scope OpenCode sessions to a single repo/workdir.
  4. For long tasks, provide progress updates from process logs.
  5. Report concrete outcomes (files changed, tests, remaining risks).
  6. Exit interactive sessions with Ctrl+C or kill, never /exit.