# OpenCode
ID: `opencode` — `agent_config::by_id("opencode")`
(`sst/opencode` — the open-source AI coding agent.)
## Hooks
### User scope (`Scope::Global`)
| File | `~/.config/opencode/plugins/<tag>.ts` |
| Mechanism | One TypeScript plugin file per consumer |
| Backup | `<file>.bak` (only if the file already existed before install) |
### Project scope (`Scope::Local(<root>)`)
| File | `<root>/.opencode/plugins/<tag>.ts` |
| Mechanism | One TypeScript plugin file per consumer |
| Backup | `<file>.bak` (only if the file already existed before install) |
> OpenCode also supports plugins listed in the `plugin` array of
> `opencode.json`, auto-installed via Bun. Not used here; we drop a single
> file in the plugins directory.
### Format
The default plugin (when `HookSpec::script` is `None`):
```ts
// Generated by agent-config. Edit at your own risk.
// Re-running install will overwrite this file.
import type { Plugin } from "@opencode-ai/plugin";
export const Hook: Plugin = async ({ $ }) => ({
"tool.execute.before": async (input, output) => {
if (input.tool !== "bash") return;
const payload = JSON.stringify({ tool: input.tool, args: output.args });
await $`echo ${payload} | myapp hook opencode`;
},
});
```
Pass `ScriptTemplate::TypeScript(body)` on the `HookSpec` to ship a custom
plugin instead. The library writes the body verbatim.
### Event mapping
The `Event` enum is dynamically mapped to OpenCode's hook points:
- `Event::PreToolUse` maps to `"tool.execute.before"`.
- `Event::PostToolUse` maps to `"tool.execute.after"`.
- `Event::Custom(name)` maps directly to the custom hook name `name` (e.g., `"session.idle"`).
For tool events (`tool.execute.before`/`after`), the callback receives `(input, output)` and compiles with tool filtering/payload formatting. For custom non-tool events, the callback receives `(input, output)` and compiles a payload passing the entire input: `const payload = JSON.stringify({ event: input });`.
OpenCode supports 25+ hook points: `tool.execute.before/after`, session/file/
message/shell/command/LSP/permission/server/todo/TUI families.
### Matcher mapping
The `Matcher` enum is dynamically compiled into a guard block in the hook callback:
- `Matcher::All` compiles to no tool filter.
- `Matcher::Bash` compiles to `if (input.tool !== "bash") return;`.
- `Matcher::Exact(tool)` compiles to `if (input.tool !== "<tool>") return;`.
- `Matcher::AnyOf(tools)` compiles to `if (![<tools>].includes(input.tool)) return;`.
- `Matcher::Regex(pattern)` compiles to `if (!new RegExp("<pattern>").test(input.tool)) return;`.
## Prompt instructions
OpenCode reads system instructions from `AGENTS.md`.
| User | `~/.config/opencode/AGENTS.md` |
| Project | `<root>/AGENTS.md` |
When `HookSpec::rules` is present, this integration injects the rules body as a
tagged `AGENT-CONFIG:<tag>` HTML-comment fenced block in that host file.
## Instructions
Standalone instruction bodies installed via `InstructionSurface` use
`InstructionPlacement::InlineBlock`. The body is injected as a tagged
`AGENT-CONFIG-INSTR:<name>` HTML-comment fenced block in OpenCode's `AGENTS.md`
file.
### User scope (`Scope::Global`)
| Host file | `~/.config/opencode/AGENTS.md` |
| Mechanism | Tagged HTML-comment fence (`<!-- BEGIN AGENT-CONFIG-INSTR:<name> -->`) |
| Ledger | `~/.config/opencode/.agent-config-instructions.json` |
| Placement | `InstructionPlacement::InlineBlock` |
### Project scope (`Scope::Local(<root>)`)
| Host file | `<root>/AGENTS.md` |
| Mechanism | Tagged HTML-comment fence (`<!-- BEGIN AGENT-CONFIG-INSTR:<name> -->`) |
| Ledger | `<root>/.opencode/.agent-config-instructions.json` |
| Placement | `InstructionPlacement::InlineBlock` |
## MCP servers
### User scope (`Scope::Global`)
| File | `~/.config/opencode/opencode.json` |
| Key | `mcp` |
| Format | JSONC accepted, written as JSON |
### Project scope (`Scope::Local(<root>)`)
| File | `<root>/opencode.json` |
| Format | JSONC accepted, written as JSON |
| Key | `mcp` (object keyed by server name) |
### Configuration
```json
{
"mcp": {
"my-server": {
"type": "local",
"command": ["node", "/path/to/server.js"]
}
}
}
```
OpenCode automatically handles OAuth for remote servers and supports both local
(command array) and remote (url) transports.
## Skills
| User | `~/.config/opencode/skills/<name>/` |
| Project | `.opencode/skills/<name>/` |
OpenCode also scans Claude-compatible and `.agents/skills` locations, but
`agent-config` writes the native OpenCode path for this integration.
## References
- <https://opencode.ai/docs/plugins/>
- <https://opencode.ai/docs/rules/>
- <https://opencode.ai/docs/config/>
- <https://opencode.ai/docs/mcp-servers/>
- <https://opencode.ai/docs/skills>
- <https://github.com/sst/opencode>