OpenCode: How the Terminal AI Agent Works Like an Entire Engineering Department

"The terminal in front of you doesn't just write code — it operates like an entire department with builders, planners, and scouts, each playing its role without overstepping boundaries."

OpenCode — The Architect's Notebook

Section1. The Problem: Why Ordinary AI Coding Agents Aren't Enough

Picture this: it's 2 AM, the feature deadline is tomorrow morning, and your AI assistant — the chatbot in VS Code — asks you every 30 seconds, "Would you like me to do X?" Every time you hit "allow," it advances a little and stops again. Result: what should have taken 20 minutes takes two hours.

Or scenario two: you're working on a large repository — 50,000 files — and your AI agent greps everything, flooding your chat window with irrelevant results. Three hours of scrolling to find the function you need.

You'd think: "This is normal, AI agents just work this way." No — they don't have to.

Architectural Divergence — OpenCode vs Claude Code

The real problem is that existing AI coding tools operate on a single model: one agent, one chat, one permission level. The agent does everything itself — search, write, execute — and nothing stops it from dangerous operations except asking you first. There's no partitioning — the subagent doing search floods the main chat with everything it finds.

You think that's a clean architecture? It's not — it's exactly what will make you waste two hours on something that should take twenty minutes.

Section2. What is OpenCode?

System Synthesis — Context + Operators + Guardrails

OpenCode is a Go-based terminal AI coding agent that interacts with AI models directly through a TUI (Terminal User Interface). But what sets it apart isn't that it runs in the terminal — many tools do that. What sets it apart is that it operates as a complete multi-agent system:

  • Primary Agents that manage sessions and represent the user
  • Subagents specialized in specific tasks (search, research, multi-step work)
  • Hooks that let you control every tool call before it executes
  • Permissions triad (Allow/Ask/Deny) that protects you from mistakes
  • Skills via SKILL.md files that extend capabilities without modifying code

OpenCode is an open-source project built with Go that functions as a terminal AI coding agent. The project is actively developed and supports a complete multi-agent architecture.

Note: Throughout this article we focus on OpenCode as a single project. The config file is .opencode.json and the CLI command is opencode.

Install OpenCode
➜ ~ curl -fsSL https://opencode.ai/install | bash
⠋ Downloading opencode v0.1.80...
✓ opencode v0.1.80 installed → /usr/local/bin/opencode
➜ ~ opencode --version
opencode v0.1.80
➜ ~⏎ run · ⌫ clear

Section3. The Operator Topology: Primary Agents and Subagents

Agent Swarm Architecture — OpenCode

Operator Topology — Primary Agents and Subagents

Imagine you're a department manager — you have full-time staff (Primary Agents) and on-demand specialists (Subagents).

Primary Agents: Build and Plan

Primary Roster — Build and Plan

The system runs two primary agents that manage the conversation with you:

AgentRoleTemperaturePermissions
BuildDefault driver — does everything (writes code, runs tests, modifies files)0.3–0.5 (Balanced)Full file ops + system command access
PlanAnalyst — reads and understands only, asks permission before modifying0.0–0.2 (Deterministic)Ask-first for file edits + bash

The Build agent is who you talk to most — the executor. The Plan agent is the thinker — it analyzes and says "this approach is better because..." but doesn't do anything without your approval.

You can switch between agents at any time with Tab in the TUI. Start a session with Build to write a feature, then switch to Plan and ask it to "analyze the architecture of what we just wrote and check for issues."

Subagents: General, Explore, and Scout

Subagent Roster — General, Explore, and Scout

These are the specialists that get summoned only when a Primary Agent needs them:

General — the Swiss Army knife:

  • Access: Full tools (except todo)
  • Role: Executes complex, multi-step research or modification tasks

Explore — the radar:

  • Access: Fast, Read-only — only glob, grep, ls, view
  • Role: Scans the local codebase, searches by patterns and keywords, but never modifies any file

Scout — the binoculars for looking far:

  • Access: External researcher — clones dependencies into managed cache
  • Role: Researches external documentation and upstream source without touching the local workspace

Subagent Isolation Boundaries

The key point — Subagents do not speak in the main channel. When the Explore agent greps 10,000 files, it doesn't flood your chat window — because the team_message tool is not available to them. Only the Primary Agent can relay synthesized findings to you.

Section4. The Messaging Architecture: Why Event-Driven Is Dramatically Better

Event-Driven vs Polling Messaging Architecture

Event-Driven vs Polling Messaging

Let's talk about something nobody discusses but it's what separates OpenCode from every other agent: the messaging approach.

Other agents (like Claude Code) use polling — they periodically read the entire JSON array, add a new message, and rewrite the whole thing. This is O(N) — the longer the conversation, the slower the operation.

OpenCode uses event-driven auto-wake:

  1. Each message is appended as a line in a JSONL file — O(1)
  2. The recipient is auto-woken immediately when a new message is written
  3. No polling, no read-modify-write cycle

This difference isn't just theoretical — in long sessions (which is the normal mode for coding), the polling approach consumes more RAM and CPU and gets progressively slower. The append-only approach stays fast from start to finish.

Section5. The Permission Triad: Allow, Ask, and Deny

Permission Triad + Hooks Pipeline

The Permission Triad — Allow/Ask/Deny

You might ask: "So the AI agent will just do things without asking me?" No — the system is built on a triad of permissions:

StateMeaningExample
ALLOWExecute without askinggit status * — safe command
ASKAsk you first, you choosegrep * — potentially risky
DENYBlocked completelyrm -rf / — dangerous

The rule: the last matching rule wins. So you can do:

json
{
  "permissions": {
    "bash": {
      "rules": [
        { "pattern": "git status *", "decision": "allow" },
        { "pattern": "grep *", "decision": "ask" },
        { "pattern": "*", "decision": "deny" }
      ]
    }
  }
}

This means: allow git status, ask for grep, and deny everything else. Then there's --yolo mode if you want everything to execute without prompts:

bash
opencode--yolo# ⚠️ All tool calls execute without permission prompts
Non-interactive Mode
$ opencode -p "fix the null pointer in auth.go" -f json
⠋ Running agent...
✓ Applied 3 changes to auth/auth.go
$⏎ run · ⌫ clear

And then there are hooks that let you create dynamic permissions — instead of writing a rule for every command, you write a script that runs before each tool call and decides:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "^(view|ls|grep|glob)$",
        "command": "echo '{\"decision\":\"allow\"}'"
      }
    ]
  }
}

Section6. The Hooks System: Control That No Other Tool Offers

Hooks are the system that lets you control the AI agent in a deterministic way — you don't rely on the model "understanding" that it shouldn't do something; you write a rule and it executes.

How it works

  1. Every time the agent makes a tool call, the system checks if a hook is registered
  2. If there's a hook, it runs your script (bash, python, lua, js — any language)
  3. The script receives JSON input on stdin and outputs JSON on stdout
  4. Based on the output, the system decides: allow (execute), deny (reject), or halt (stop the entire turn)

Available Environment Variables

VariableDescription
OPENCODEAlways 1 when running under OpenCode
OPENCODE_TOOL_NAMETool being called (e.g. bash)
OPENCODE_TOOL_INPUT_COMMANDThe command for bash tool calls
OPENCODE_TOOL_INPUT_FILE_PATHThe file path for file tools
OPENCODE_SESSION_IDCurrent session ID
OPENCODE_CWDWorking directory

Practical Examples

1. Block rm -rf on root:

bash
#!/usr/bin/env bash
ifecho"$OPENCODE_TOOL_INPUT_COMMAND"|grep-qE'rm\s+-(rf|fr)\s+/';then
echo"Refusing to run rm -rf against root">&2
exit2# Block tool
fi

2. Inject context when editing Go files:

bash
#!/usr/bin/env bash
if[["$OPENCODE_TOOL_INPUT_FILE_PATH"==*.go]];then
echo'{"context": "Remember: run gofumpt after editing Go files."}'
else
echo'{}'
fi

3. Halt the turn on drop database:

bash
#!/usr/bin/env bash
ifecho"$OPENCODE_TOOL_INPUT_COMMAND"|grep-qE'drop\s+database';then
echo"Database drop detected — halting turn">&2
exit49# Halt entire turn
fi
Hook — Block DB Deletion
➜ ~ cat .opencode.json
"hooks": { "pre-tool-use": ["bash .opencode/guard.sh"] }
➜ ~ opencode -p "delete db.sqlite"
⚠ Hook blocked: cannot delete *.sqlite files
✗ Tool call denied by pre-tool-use hook
➜ ~⏎ run · ⌫ clear

Section7. The Skills System: Extending Capabilities Without Modifying Code

Agent Skills — SKILL.md

Skills are OpenCode's way of extending capabilities through Markdown files — not through programming. Each skill is a folder containing a SKILL.md file with YAML frontmatter + instructions:

yaml
name: git-release
description: Creates a standardized semver release commit and tag.
user-invocable: true
disable-model-invocation: true

# Git Release Skill

Instructions for creating a semver release...

How the agent finds skills

The system scans these paths in order:

Global (user-level):

  • $XDG_CONFIG_HOME/opencode/skills/ or ~/.config/opencode/skills/
  • ~/.agents/skills/
  • ~/.claude/skills/

Project-level:

  • .agents/skills
  • .opencode/skills
  • .claude/skills

The rule: the last skill with the same name wins — if you have a git-release skill in global and another with the same name in the project, the project version takes precedence.

Built-in Skills

SkillDescription
opencode-configTeaches the agent how to configure itself
opencode-hooksTeaches the agent how to write and debug hooks
jqBuilt-in JSON processor (gojq) — no installation needed
Activate a Skill
➜ ~ cat .opencode/SKILL.md
my-deploy-skill
Deploy the current branch to staging.
➜ ~ opencode -p "use my-deploy-skill to deploy"
⠋ Loading skill: my-deploy-skill
✓ Deployed to staging → https://staging.myapp.com
➜ ~⏎ run · ⌫ clear

Section8. Context Files: How the Agent Understands Your Project

Dedicated Context — AGENTS.md

Context files are where you put instructions for the agent to follow. In OpenCode the config file is .opencode.json — but there are also separate context files:

AGENTS.md — Universal Standard

Paths the agent reads

PathType
AGENTS.mdUniversal standard — all AI agents understand it
OPENCODE.md / opencode.mdOpenCode-specific
CLAUDE.mdClaude Code-specific
.cursorrulesCursor-specific
GEMINI.mdGemini-specific
.github/copilot-instructions.mdGitHub Copilot

The beauty of AGENTS.md is that it's become a universal standard read by over 60,000 open-source repositories — write your instructions once in AGENTS.md and all agents (Cursor, Claude Code, Copilot, Windsurf, Zed, etc.) will understand them.

Section9. Crash Recovery: What Happens When the Server Goes Down?

Crash Recovery Protocol

You'd think that when the server crashes and agents were running, they'd automatically restart and continue their work? No — that's exactly what would have you wake up to find four autonomous agents burning API credits all night on a stale task.

Crash Recovery + System Safety

When the system recovers from a crash, it does three things in order:

  1. Register Restoration Handler — prepares delegate-mode permissions before cleanup
  2. Force-Transition — scans for "busy" agents and transitions them to "ready"
  3. Halt & Await — stops. Does not restart agents automatically. You must manually start them.

The system does automatic cleanup, but the human must press the button to continue. This protects you from unintentional API burn.

Section10. Hidden Orchestrators: Background Agents You Don't See

Hidden System Orchestrators

Three agents work in the background without you noticing:

Compaction — when the context window approaches its limit (95%), this agent automatically summarizes the conversation and creates a new session with the summary. No more sudden session cutoffs.

Title — generates automatic names for each session so you can find them quickly. Instead of "session 3a7f" you see "Fix auth middleware CORS issue".

Summary — continuously creates background overviews so if you return to an old session, you find the context written out.

Section11. Configuration: How to Set Everything Up

Agent Configuration Anatomy

The config file is .opencode.json. The key point is you can configure everything:

json
{
  "agents": {
    "coder": {
      "model": "anthropic.claude-sonnet-4",
      "maxTokens": 5000,
      "reasoningEffort": "medium"
    },
    "task": {
      "model": "anthropic.claude-sonnet-4",
      "maxTokens": 5000
    },
    "title": {
      "model": "anthropic.claude-sonnet-4",
      "maxTokens": 80
    }
  },
  "providers": {
    "anthropic": { "apiKey": "$ANTHROPIC_API_KEY" },
    "openai": { "apiKey": "$OPENAI_API_KEY" }
  },
  "mcp": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": { "Authorization": "Bearer $GH_PAT" }
    }
  },
  "permissions": {
    "allowed_tools": ["view", "ls", "grep", "glob", "edit"]
  },
  "options": {
    "context_paths": ["AGENTS.md", ".cursorrules"],
    "global_context_paths": ["~/.config/opencode/OPENCODE.md"]
  }
}

Available Providers

ProviderEnvironment VariableNotes
AnthropicANTHROPIC_API_KEYClaude models
OpenAIOPENAI_API_KEYGPT models
Google GeminiGEMINI_API_KEYGemini models
AWS BedrockAWS_ACCESS_KEY_IDClaude via AWS
Azure OpenAIAZURE_OPENAI_ENDPOINTGPT via Azure
OpenRouterOPENROUTER_API_KEYMulti-model access
GitHub CopilotGITHUB_TOKENFree tier available
GroqGROQ_API_KEYFast inference
OllamaLocal endpointSelf-hosted models
LM StudioLocal endpointSelf-hosted models

You can also add custom providers via type: "openai-compat" or type: "anthropic-compat" — so if you have a model running on your own endpoint, you can connect it easily.

Section12. MCP Integration: Model Context Protocol

MCP (Model Context Protocol) is a system that lets the AI agent communicate with external tools. Instead of the agent being confined to the terminal, it can open GitHub issues, send Slack messages, read from databases — all through MCP servers.

json
{
  "mcp": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": { "Authorization": "Bearer $GH_PAT" }
    },
    "filesystem": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
      "disabled_tools": ["write_file"]
    }
  }
}

Available types:

  • stdio — runs a local process (like npx)
  • http — connects to an HTTP endpoint
  • sse — Server-Sent Events

Section13. LSP Integration: Code Intelligence

LSP (Language Server Protocol) is what lets the agent understand code more deeply — instead of just grepping for text, it can go to definition, find references, and get diagnostics.

Built-in LSP support:

  • gopls — Go
  • typescript-language-server — TypeScript/JavaScript
  • nixd — Nix

You can add custom LSPs:

json
{
  "lsp": {
    "rust-analyzer": {
      "command": "rust-analyzer",
      "filetypes": ["rs"],
      "root_markers": ["Cargo.toml"]
    }
  }
}

Section14. The TUI: Keyboard Shortcuts

Operator Shortcuts

ShortcutAction
Ctrl+XLeader key (start of all shortcuts)
Leader nNew session
Leader lList sessions
Ctrl+PCommand palette
TabCycle Primary Agents
Leader ↓/↑Navigate child/parent sessions
Ctrl+GScroll to top
Ctrl+Alt+GScroll to bottom

Section15. The Context-to-Collaboration Pipeline

Context-to-Collaboration Pipeline

Let's see the full pipeline from start to finish:

  1. Foundation / Context — AGENTS.md and skills build the foundation. This is what makes the agent understand how the project works.

  2. Orchestration / Human Intent — You as a user tell the Build agent "do X", and the agent reads the context and decides how to execute.

  3. Mesh / Subagent Deployment — The Build agent summons Subagents (General, Explore, Scout) via a peer-to-peer event-driven mesh — not through a central coordinator.

  4. Security / Safe Execution — All tool calls pass through the permission system (Allow/Ask/Deny) and hooks before execution.

Result? Secure, highly autonomous development at scale.

Section16. Comparison: OpenCode vs. Claude Code

OpenCode vs Claude Code — Architecture Comparison

FeatureClaude CodeOpenCode
Message StorageJSON Array / O(N)JSONL Append / O(1)
NotificationPollingEvent-Driven Auto-Wake
CommunicationLeader-CentricFull Mesh / P2P
Model SupportSingle ProviderMulti-Provider (20+)
Message TrackingLocal FlagRead + Delivery Receipts
Agent SystemSingle AgentMulti-Agent (Build/Plan + Subs)
PermissionsAllow/DenyAllow/Ask/Deny Triad
HooksNoPreToolUse (any language)
SkillsNoSKILL.md system
Context FilesCLAUDE.md onlyAGENTS.md + OPENCODE.md + CLAUDE.md + GEMINI.md + .cursorrules
LSPNoGo, TS, Nix + custom
MCPLimitedFull (stdio, http, sse)
Crash RecoveryAuto-restart (dangerous)Manual start (safe)
Self-hosted ModelsLimitedOllama, LM Studio, LiteLLM

Section17. Key Rules and Gotchas

RuleDetailsWhy It Matters
Build agent doesn't commit on its ownYou must explicitly say "commit"So it doesn't push something you haven't reviewed
Subagents don't speak in the main channelteam_message tool not available to themSo they don't flood the chat window
Crash recovery requires manual startAgents transition to "ready" but don't restartTo prevent API burn overnight
Skills are scanned each timeFrom 6+ paths (global + project)So if you delete a project skill it disappears immediately
Context window auto-compactsAt 95% it summarizesSo sessions don't break suddenly
Hooks only fire on the top-level agentNot on SubagentsBecause Subagents are trusted (read-only or scoped)
Last permission rule winsgit status *: allow then *: denySo you can allow specific commands and deny everything else

Section18. Conclusion

So what does all of this mean?

First — OpenCode isn't just a chatbot in the terminal. It's a complete multi-agent system with Build and Plan agents, specialized Subagents (General, Explore, Scout), and each plays its role without overstepping boundaries.

Second — the permission triad (Allow/Ask/Deny) + the hooks system gives you a level of deterministic control that doesn't exist in any other tool — you can write a script to block rm -rf, rewrite commands, or add context automatically.

Third — the event-driven messaging architecture and JSONL append-only storage keep the system fast from start to finish — unlike the polling approach that gets slower as the conversation grows.

Fourth — the AGENTS.md standard made context files not just for OpenCode, but for all AI agents — write once and let 60,000+ repositories read it.

Fifth — crash recovery with manual start, not auto-restart — this protects you from unintentional API burn.

And the question remains: if the AI agent you work with every day truly operates like an entire department — Build, Plan, Explore, Scout — do you need to be the manager, or just the supervisor?