Open-source AI coding agent with agents, skills, commands, and plugins.
Vendor: Anomaly (anomalyco) | License: MIT | Runtime: TypeScript / Bun
- Docs: https://opencode.ai/docs
- Config: https://opencode.ai/docs/config/
- Agents: https://opencode.ai/docs/agents/
- Skills: https://opencode.ai/docs/skills/
- Plugins: https://opencode.ai/docs/plugins/
- GitHub: https://github.com/anomalyco/opencode
- Awesome OpenCode: https://github.com/awesome-opencode/awesome-opencode
curl -fsSL https://opencode.ai/install | bash
# or
npm install -g opencode-ai
# or
bun install -g opencode-ai
# or
pnpm install -g opencode-ai
# or
yarn global add opencode-ai
# or (Homebrew)
brew install anomalyco/tap/opencode| File | Scope | Purpose |
|---|---|---|
~/.config/opencode/opencode.json |
Global | User settings |
opencode.json |
Project | Project settings |
.opencode/plugins/ |
Project | Local plugins |
~/.config/opencode/plugins/ |
Global | Global plugins |
.opencode/agents/ |
Project | Agent markdown files |
~/.config/opencode/agents/ |
Global | Global agent markdown files |
.opencode/skills/ |
Project | Reusable skill instruction sets |
~/.config/opencode/skills/ |
Global | Global skills |
The .opencode/ and ~/.config/opencode/ directories use plural names for subdirectories: agents/, commands/, modes/, plugins/, skills/, tools/, and themes/. Config keys in opencode.json are singular.
OpenCode uses a plugin system as its primary extension mechanism. Plugins run on Bun and are TypeScript/JavaScript modules. Native hook events are provided via plugins rather than a built-in hook config key.
Plugins can register callbacks for 30+ lifecycle events:
| Category | Events |
|---|---|
| Tool | tool.execute.before, tool.execute.after |
| Session | session.created, session.updated, session.deleted, session.idle, session.error, session.compacted, session.diff, session.status |
| Message | message.updated, message.removed, message.part.updated, message.part.removed |
| File | file.edited, file.watcher.updated |
| Permission | permission.asked, permission.replied |
| LSP | lsp.updated, lsp.client.diagnostics |
| Shell / Command | command.executed, shell.env |
| TUI | tui.prompt.append, tui.command.execute, tui.toast.show |
| Server | server.connected |
| Install | installation.updated |
| Misc | todo.updated |
| Experimental | experimental.session.compacting |
Specialized hooks (returned as top-level keys alongside event handlers):
| Hook Key | Purpose |
|---|---|
tool |
Define custom tools using a tool() factory function with Zod-based argument schemas |
tool.execute.before |
Intercept/modify tool args before execution |
tool.execute.after |
Process tool results post-execution |
stop |
Intercept agent termination attempts |
experimental.chat.system.transform |
Inject context into system prompt |
experimental.session.compacting |
Preserve state during compaction |
{
"plugin": ["opencode-plugin-logger", "./plugins/my-plugin.js"]
}Note: npm package names are bare — no npm: prefix. Local files use relative paths.
Or place plugins directly in .opencode/plugins/ (auto-loaded, no config entry needed).
Plugins export an async function (not a static object) receiving a context argument and returning a hooks object with dot-namespaced keys:
// .opencode/plugins/my-plugin.ts
import type { Plugin } from "@opencode-ai/plugin"
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
"tool.execute.before": async (input, output) => {
// input = { tool, sessionID, callID } — metadata about the call
// output = { args: { command, ... } } — the tool's actual arguments, nested under `.args`
if (input.tool === "bash" && output.args.command?.includes("rm -rf")) {
throw new Error("Blocked destructive command")
}
},
"tool.execute.after": async ({ tool, output }) => {
console.error(`[audit] ${tool} completed`)
},
event: async ({ event }) => {
// subscribe to any system event by type
}
}
}Live-verified 2026-07-23, two real corrections to the pattern above (tested on opencode v1.18.4 with an OpenAI model): (1) the callback signature is
(input, output), two separate arguments — not a single destructured{ tool, input }object.inputcarries{tool, sessionID, callID}; the tool's actual arguments (e.g.commandforbash) live on the second argument, underoutput.args.command. A plugin written the old way (input.command) throwsundefined is not an object (evaluating 'input.command')the moment a matching tool runs — confirmed by triggering it live. (2) Blocking is done bythrowing an Error inside the hook, not byreturn { block: true, message: "..." }. Thethrowapproach was confirmed to actually stop the tool call (✗ echo ... failed,Error: <message>surfaced to the model); thereturn { block: true }shape shown in some examples was not verified to work and should not be trusted without re-testing.
Context object properties:
project— Current project infoclient— OpenCode SDK client for AI interaction$— Bun's shell API for command executiondirectory— Current working directoryworktree— Git worktree path
Plugin dependencies go in .opencode/package.json; OpenCode runs bun install automatically.
| Tool | Description |
|---|---|
bash |
Execute shell commands |
read |
Read file contents |
write |
Write files |
edit |
Apply edits |
glob |
File pattern matching |
grep |
Search files |
ls |
List directory |
fetch |
Fetch URLs |
Configure MCP servers in opencode.json. Server names are direct children of mcp — there is no servers sub-key:
{
"mcp": {
"my-server": {
"type": "local",
"command": ["node", "./mcp-server/index.js"]
},
"remote-server": {
"type": "remote",
"url": "https://example.com/mcp",
"enabled": true
}
}
}Define specialized agents with custom prompts, models, and tool access. Config key is agent (singular), and the system prompt key is prompt (points to a file path):
{
"agent": {
"code-reviewer": {
"model": "anthropic/claude-opus-4",
"description": "Strict code reviewer",
"prompt": "{file:./agents/code-reviewer.md}",
"mode": "subagent",
"permission": {
"read": "allow",
"edit": "ask",
"bash": "deny"
}
}
}
}Agents can also be defined as markdown files in .opencode/agent/ (filename becomes the agent name). Live-verified 2026-07-23: the correct directory is singular .opencode/agent/ — this matches the --path default used by opencode agent create, and a file placed there (with mode: subagent in its YAML frontmatter) was correctly picked up: opencode agent list showed it as <name> (subagent). (Some other docs/examples may show the plural .opencode/agents/; treat that as unconfirmed/possibly stale — this page now reflects the live-tested singular form.)
Live-verified 2026-07-23 — subagent delegation genuinely works. Wrote a real subagent to
.opencode/agent/writer.md(frontmatter:mode: subagent,model: openai/gpt-4o-mini,tools: {write, read, bash}) and asked the primary agent to "Use the task tool to delegate to the writer subagent." The transcript showed the delegated step labeledWriter Agent, and the target file was created correctly — confirming OpenCode'stasktool (visible inopencode agent create --permissionsas an available permission:bash, read, edit, glob, grep, webfetch, task, todowrite, websearch, lsp, skill) does real inter-agent delegation, comparable to Claude Code'sTasktool, Goose'sdelegate, OpenHands'delegate, and Codex'scollab/SpawnAgent— and notably more real than the non-functional "subagent" scaffolding found in Crush and Continue CLI during this same testing pass.
Agent config fields:
| Field | Description |
|---|---|
model |
Override default model (provider/model-id) |
description |
Brief explanation (required) |
prompt |
File path reference {file:./path} |
mode |
"primary", "subagent", or "all" |
permission |
Per-tool permission overrides |
temperature |
0.0–1.0 |
top_p |
0.0–1.0, alternative diversity control |
steps |
Max agentic iterations |
color |
Hex or theme name |
hidden |
Boolean; hide from @ autocomplete |
disable |
Boolean; disable this agent entirely |
Config key is permission (singular). Valid values: "allow", "ask", "deny".
{
"permission": {
"bash": "ask",
"edit": "ask",
"read": "allow"
}
}Permission keys include: read, edit, glob, grep, bash, task, external_directory, webfetch, websearch, lsp, skill, question, doom_loop. (todowrite no longer appears in the official permissions doc as of 2026-07-23 — may have been removed or folded into another key.)
| Permission Key | Description |
|---|---|
read |
Reading a file (matches the file path) |
edit |
All file modifications (covers edit, write, patch) |
glob |
File globbing (matches the glob pattern) |
grep |
Content search (matches the regex pattern) |
bash |
Running shell commands (matches parsed commands) |
task |
Launching subagents (matches the subagent type) |
skill |
Loading a skill (matches the skill name) |
lsp |
Running LSP queries (currently non-granular) |
webfetch |
Fetching a URL (matches the URL) |
websearch |
Web search (matches the query) |
external_directory |
Triggered when a tool touches paths outside the project working directory |
question |
Asking the user questions during execution |
doom_loop |
Triggered when the same tool call repeats 3 times with identical input |
Skills are reusable instruction sets loaded on-demand by agents. A skill is a directory containing a SKILL.md file with a name, description, and instructions.
- Project skills:
.opencode/skills/ - Global skills:
~/.config/opencode/skills/
Custom slash commands go in .opencode/commands/.
- OpenCode does not have a native
PreToolUse/PostToolUsehook config key — all lifecycle control goes through plugins. - Plugins run on Bun, not Node.js.
- The
toolsfield on agents is deprecated; usepermissioninstead for fine-grained tool access control. - The GitHub repo moved from
sst/opencodetoanomalyco/opencodefollowing the SST → Anomaly company rebrand in 2026.
| Topic | URL | Fetched | Label |
|---|---|---|---|
| Docs home | https://opencode.ai/docs | 2026-06-26 | [official] |
| Config reference | https://opencode.ai/docs/config/ | 2026-06-26 | [official] |
| Agents reference | https://opencode.ai/docs/agents/ | 2026-06-26 | [official] |
| Skills reference | https://opencode.ai/docs/skills/ | 2026-06-26 | [official] |
| Plugins reference | https://opencode.ai/docs/plugins/ | 2026-06-26 | [official] |
| Permissions reference | https://opencode.ai/docs/permissions/ | 2026-07-23 | [official] |
| GitHub repo | https://github.com/anomalyco/opencode | 2026-06-26 | [github] |
| Awesome OpenCode | https://github.com/awesome-opencode/awesome-opencode | 2026-06-26 | [community] |
| Plugin guide (gist) | https://gist.github.com/johnlindquist/0adf1032b4e84942f3e1050aba3c5e4a | 2026-06-26 | [community] |