Skip to main content

DRIVER_SKILL

Constant DRIVER_SKILL 

Source
pub const DRIVER_SKILL: &str = "<!--\nSPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\nCopyright the opencode-ctl authors. License: https://polyformproject.org/licenses/noncommercial/1.0.0\nThe canonical source-of-truth for the driver skill lives at `shared/opencode-ctl/skill/opencode-ctl-driver.md`.\nThis file is a TEMPLATE: a later task renders it per host by substituting the host placeholders; the\nengine embeds this template and renders it per host at run time, and the committed copies under each plugin\'s\n`skills/` are generated from that output; regenerate that plugin\'s copy from the shipped binary with `emit-skill opencode-ctl-driver --host <cc|qwen|agy|opencode>` so it can never drift.\nEdit ONLY that canonical file - if the copy you are viewing is under a plugin/CLI `skills/` dir, it is\ngenerated; do NOT edit it, your changes will be overwritten on the next emit.\n-->\n---\nname: opencode-ctl-driver\ndescription: Delegate bounded implementation work to an OpenCode worker session on a cheaper or free model and verify it. Use when the user asks to hand a task, plan or file to opencode, to delegate to a cheaper or free model, to have another agent (not the host\'s own built-in subagents) implement a plan, to run something in the opencode worker, to check what the worker did, or when an opencode-ctl tool returns awaiting_permission, awaiting_input, stalled, budget_exceeded, omitted_patches or patch_truncated. Not for questions about OpenCode itself\nallowed-tools: {{ALLOWED_TOOLS}}\n---\n\nYou are running as {{HOST_NAME}}; this skill is the macro workflow for delegating bounded implementation work\nto an OpenCode worker session on a cheaper or free model and verifying it, driven through the\n`opencode-ctl-mcp` tools. Read each tool\'s own description for parameters and error codes; read this for the\nloop.\n\n# When to delegate\n\nDelegate to an OpenCode worker session when the user asks to hand a task, a plan or a file to opencode; to\ndelegate work to a cheaper or free model; to have another agent (not the host\'s own built-in subagents)\nimplement a plan; to run something in the opencode worker; or to check what the worker did. The worker\'s\nmodel is a configuration the loop checks, not a property of the tool: \"cheaper or free\" is what the user\nconfigured, so say so when the server default is used instead. Do not use this skill for questions about\nOpenCode itself.\n\n# The loop\n\n1. Call `service_status` first. When it reports not running, call `service_start`; on the OpenCode host the\n   service is up by definition.\n2. If the result is `paired`, USE THAT SESSION: never create a second one beside the human\'s tab unless a\n   parallel worker is wanted on purpose. Otherwise call `session_create`, and tell the user which `model`\n   the result names when it is the server default rather than a configured cheap one.\n3. Call `session_prompt`, then `session_wait` with `after = <message_id>` on EVERY slice, dispatching on the\n   result\'s `state` by this table:\n\n| `state` | action |\n| --- | --- |\n| `running` | call again with the same `after` |\n| `idle` | go to VERIFY |\n| `failed` | report `error`, ask the user before re-prompting |\n| `interrupted` | report, ask the user |\n| `awaiting_permission` | read `action`, `resources`, `save`, answer with `permission_reply` (`once` by default; `reject` rejects every pending request of that session; `always` needs the human\'s say-so), then wait again |\n| `awaiting_input` | `form_reply` with visible required fields, then wait again |\n| `stalled` | if the previous slice returned `SERVICE_DOWN`, read `session_messages` for the restart notice and `session_get` for the state before anything else (the server resumes the turn by itself); otherwise `session_interrupt`, report, ask the user |\n| `budget_exceeded` | `session_interrupt`, report, ask the user |\n| `timeout` | only with `hard: true`; treat as `running` |\n\nIndependently of the state, when the result carries `concurrent_turn = <id>` the human\'s turn is running:\nwait PASSIVELY with `after = <id>` for at most `max_turn_s`, answer none of its permissions or forms, never\ninterrupt it, tell the user once what it is waiting on, then VERIFY when it ends (a human prompt steered INTO\nthe controller\'s turn joins it, so the controller\'s `session_changes` range then includes the human\'s work and\nthe report says so).\n\nThe `pair` line is a separate rule; each value has its own action:\n- `tui = dead` or `unknown` -> tell the user the tab is gone (`opencode-pair attach <pair-id>` reopens it),\n  keep driving.\n- `tui = starting` -> say nothing yet.\n- `tui = none` -> no tab was requested, say nothing.\n- a tab without `server_url` reconnects itself after `service_restart`.\n\nWhen `server_url` is set, two behaviours of this loop change:\n- `service_start` with `server_url` set only re-checks reachability, so when it returns `running: false`\n  tell the user the remote server at `server_url` must be started by hand, and when it returns an error\n  (`AUTH_FAILED`: a wrong or missing password for that server) run `{{DOCTOR_INVOCATION}}` instead.\n- `service_stop` and `service_restart` are `REFUSED_ON_HOST` with `server_url` set; the Related line routes\n  that code to the doctor.\n\n# Verify\n\nVERIFY the work once `session_wait` reports `idle`:\n- Call `session_changes` with `from` = the prompt id for this turn, plus `session_messages`.\n- Verify by files and diff, not by the worker\'s prose.\n- Any of `omitted_patches`, `patch_truncated` or `omitted_files` is never a pass: page by `paths` or read the\n  file.\n- On a gap re-prompt the SAME session with the specific finding.\n- Never trust the worker\'s closing summary.\n\n# Which tools spend worker tokens\n\n- `session_prompt` spends worker tokens: yes.\n- `opencode_api` depends on the operation it calls.\n- All other tools spend none.\n\n# Answering permissions and forms\n\n- For `awaiting_permission`, read `action`, `resources` and `save` before answering.\n- Answer with `permission_reply`, `once` by default.\n- `reject` rejects every pending request of that session.\n- `always` needs the human\'s say-so.\n- For `awaiting_input`, answer with `form_reply`, filling only the visible required fields.\n\n# Raw-CLI fallback\n\nFor a host without the binary, drive the worker with the raw CLI. In managed mode (no `server_url`) the four\ncommands are:\n- `opencode service start`\n- `opencode api session.create -d \'{\"title\":...,\"location\":{\"directory\":...}}\'`\n- `opencode api session.prompt --param sessionID=<id> -d \'{\"text\":...}\'`\n- `opencode api experimental.session.wait --param sessionID=<id>`\n\nWhen `server_url` is set, the list is instead the LAST THREE of those, each with `--server <url>` appended,\nbecause a plain `opencode api` targets or starts the LOCAL managed service, and `opencode service start` is\ndropped from it because it has no `--server` flag and starts only the local service:\n- `opencode api session.create -d \'{\"title\":...,\"location\":{\"directory\":...}}\' --server <url>`\n- `opencode api session.prompt --param sessionID=<id> -d \'{\"text\":...}\' --server <url>`\n- `opencode api experimental.session.wait --param sessionID=<id> --server <url>`\n\n# Cheap model check\n\nWhen `session_create` returns the server default model rather than a configured cheap or free one, tell the\nuser: the description\'s \"cheaper or free model\" is a configuration the loop checks, not a property of the\ntool.\n\n# Related\n\nThis skill is `{{INVOCATION}}`; when a tool returns OPENCODE_NOT_FOUND, SERVICE_DOWN, AUTH_FAILED, UNKNOWN_OPERATION or REFUSED_ON_HOST, run `{{DOCTOR_INVOCATION}}`.\n";
Expand description

The driver skill template, embedded at compile time (version-locked to this binary).