Note
Fork of now archived https://github.com/opencode-ai/opencode The focus is changed towards headless experience oriented towards autonomous agents.
OpenCode is a CLI tool that brings AI assistance to your terminal. It provides a TUI (Terminal User Interface), a headless non-interactive mode for scripting, an HTTP REST API server for external UI integration, and an ACP server for editor integration.
- Interactive TUI built with Bubble Tea
- Non-interactive mode for headless automation and autonomous agents
- HTTP REST API server for external UI integration (e.g., OpenWork) with SSE event streaming (guide)
- ACP server (Agent Client Protocol) for editor and desktop UI integration (AionUI, Zed, JetBrains) via JSON-RPC over stdio (guide)
- Chat bridge: in-process Telegram / Slack / Mattermost adapters with multi-reviewer fan-out, router-initiated conversations, interactive question UI (buttons + inline keyboards),
router_sendagent tool, single-writer election, and per-identity health reporting (guide) - Flows: deterministic multi-step agent workflows defined in YAML (guide)
- Subagents: highly customizable agents calling another agents to do work [[#Agents]]
- Cron jobs: schedule prompts to run once or recurringly via subagents, with
/loopand thecroncreate/crondelete/cronlisttools (guide) - Multiple AI providers: Anthropic, OpenAI, Google Gemini, AWS Bedrock, VertexAI, YandexCloud, Kimi (Moonshot), and self-hosted
- Tool integration: file operations, shell commands, code search, LSP code intelligence
- Structured output: enforce final agent's output with json schema, perfect for automated pipelines
- MCP support: extend capabilities via Model Context Protocol servers
- Deferred tools: keep large MCP fleets out of context until needed — matching tools are loaded on demand via
toolsearch(Anthropic server-side tool search on capable models, cache-preserving; a client-side fallback elsewhere) (guide) - Agent skills: reusable instruction sets with argument substitution and dynamic shell expansion (guide)
- Custom commands: predefined prompts with named arguments (guide)
- Langfuse observability: built-in tracing for LLM calls, tool executions, token usage, and cost (guide)
- Session management with SQLite or MySQL storage (guide)
- LSP integration with auto-install for 30+ language servers (guide)
- File change tracking during sessions
curl -fsSL https://raw.githubusercontent.com/obukhovaa/opencode/refs/heads/main/install | bash
# Specific version
curl -fsSL https://raw.githubusercontent.com/obukhovaa/opencode/refs/heads/main/install | VERSION=0.3.0 bashbrew install obukhovaa/tap/opencodego install github.com/obukhovaa/opencode@latestopencode # Start TUI
opencode -d # Debug mode
opencode -c /path/to/project # Set working directory
opencode -a hivemind # Start with a specific agent
opencode -s <session-id> # Resume or create a session
opencode -s <session-id> -D # Delete session and start fresh
opencode --auto-approve # Start with auto-approve (skip permission dialogs)opencode -p "Explain context in Go" # Single prompt
opencode -p "Explain context in Go" -f json # JSON output
opencode -p "Explain context in Go" -q # Quiet (no spinner)
opencode -p "Refactor this module" -t 5m # With 5-minute timeoutopencode -p "Explain context in Go" -F review-code -A hash=93706ee # Run review-uncommited flow with args
opencode -p "Explain context in Go" -F ralph-does -s wiggum # Run flow with the pinned session dataAll permissions are auto-approved in non-interactive mode.
Start a headless HTTP REST API server for external UIs like OpenWork:
opencode serve # Default: localhost:4096
opencode serve --port 8080 --hostname 0.0.0.0 # Custom port and hostname
opencode serve --cors "http://localhost:3000" # Restrict CORS
OPENCODE_SERVER_PASSWORD=secret opencode serve # With authenticationSee the full server guide for endpoints and SSE events.
Start an Agent Client Protocol server for editor integration (Zed, JetBrains):
opencode acp # JSON-RPC over stdio
opencode acp --cwd /path/to/project # Specific project directory| Flag | Short | Description |
|---|---|---|
--help |
-h |
Display help |
--debug |
-d |
Enable debug mode |
--cwd |
-c |
Set working directory |
--prompt |
-p |
Non-interactive single prompt |
--agent |
-a |
Agent ID to use (e.g. coder, hivemind) |
--session |
-s |
Session ID to resume or create |
--delete |
-D |
Delete the session specified by --session before starting |
--output-format |
-f |
Output format: text (default), json |
--quiet |
-q |
Hide spinner in non-interactive mode |
--timeout |
-t |
Timeout for non-interactive mode (e.g. 10s, 30m, 1h) |
--auto-approve |
Start TUI with auto-approve enabled (skip permission dialogs) | |
--flow |
-F |
Flow ID to execute, more info |
--arg |
-A |
Flow argument as key=value (repeatable) |
--args-file |
JSON file with flow arguments | |
--project-id |
-P |
Custom project ID to group sessions (overrides detected Git/basename) |
OpenCode looks for .opencode.json in:
./.opencode.json(project directory)$XDG_CONFIG_HOME/opencode/.opencode.json$HOME/.opencode.json
{
"data": {
"directory": ".opencode"
},
"providers": {
"openai": { "apiKey": "..." },
"anthropic": { "apiKey": "..." },
"gemini": { "apiKey": "..." },
"vertexai": {
"project": "your-project-id",
"location": "us-central1"
},
"yandexcloud": {
"apiKey": "..."
}
},
"agents": {
"coder": {
"model": "vertexai.claude-opus-4-6",
"maxTokens": 5000,
"reasoningEffort": "high"
},
"explorer": {
"model": "claude-4-5-sonnet[1m]",
"maxTokens": 5000
},
"summarizer": {
"model": "vertexai.gemini-3.0-flash",
"maxTokens": 5000
},
"descriptor": {
"model": "claude-4-5-sonnet[1m]",
"maxTokens": 80
}
},
"shell": {
"path": "/bin/bash",
"args": ["-l"]
},
"mcpServers": {
"example": {
"type": "stdio",
"command": "path/to/mcp-server",
"args": []
}
},
"lsp": {
"gopls": {
"initialization": { "codelenses": { "test": true } }
}
},
"sessionProvider": { "type": "sqlite" },
"skills": { "paths": ["~/my-skills"] },
"permission": {
"skill": { "*": "ask" },
"rules": {
"bash": { "*": "ask", "git *": "allow" },
"edit": { "*": "allow" },
"read": { "/proc/*": "deny" }
}
},
"webSearch": {
"providers": {
"tavily": {
"baseUrl": "https://api.tavily.com/search",
"apiKey": "env:TAVILY_API_KEY",
"description": "Web search via Tavily"
}
}
},
"autoCompact": true,
"debug": false
}Each built-in agent can be customized:
| Agent | Mode | Purpose |
|---|---|---|
coder |
agent | Main coding agent (all tools) |
hivemind |
agent | Supervisory agent for coordinating subagents |
explorer |
subagent | Fast codebase exploration (read-only tools) |
workhorse |
subagent | Autonomous coding subagent (all tools) |
summarizer |
subagent | Session summarization |
descriptor |
subagent | Session title generation |
Agent fields:
| Field | Description |
|---|---|
model |
Model ID to use |
maxTokens |
Maximum response tokens |
maxTurns |
Maximum tool calls before agent stops |
reasoningEffort |
low, medium, high (default), max |
mode |
agent (primary, switchable via tab) or subagent (invoked via task tool) |
name |
Display name for the agent |
description |
Short description of agent's purpose |
permission |
Agent-specific permission overrides (supports granular glob patterns) |
tools |
Enable/disable specific tools (e.g., {"skill": false}) |
deferredTools |
On-demand tool loading — matching tools stay out of context until discovered via toolsearch (e.g., {"jira_*": true}, guide) |
parallelToolUse |
Enable/disable parallel tool invocation if tool allows it |
color |
Badge color for subagent indication in TUI |
prompt |
Custom system prompt (mutually exclusive with langfusePromptPath) |
langfusePromptPath |
Path of a prompt in Langfuse Prompt Management to use as the system prompt (guide) |
langfusePromptLabel |
Langfuse label to resolve for langfusePromptPath (default production) |
Define custom agents as markdown files with YAML frontmatter. Discovery locations (merge priority, lowest to highest):
agentPathsin.opencode.json— custom directories scanned for*.mdagents (lowest priority)~/.config/opencode/agents/*.md— Global agents~/.agents/types/*.md— Global agents.opencode/agents/*.md— Project agents.agents/types/*.md— Project agents.opencode.jsonagentsconfig — Highest priority
agentPaths accepts absolute paths, ~ (home directory), and relative paths (resolved against the working directory). Each directory is scanned non-recursively for *.md files, mirroring the skills.paths option:
{
"agentPaths": ["~/.my-agents", ".team/agents"]
}Example .opencode/agents/reviewer.md:
---
name: Code Reviewer
description: Reviews code for quality and best practices
mode: subagent
model: vertexai.claude-opus-4-6
color: info
tools:
bash: false
write: false
---
You are a code review specialist...The file basename (without .md) becomes the agent ID. Custom agents default to subagent mode.
An agent's system prompt can live in Langfuse Prompt Management instead of in the definition, so prompt changes ship from the Langfuse UI with no deploy. In a markdown agent it is a frontmatter key, and the body — which is the inline prompt — must be empty:
---
name: Code Reviewer
description: Reviews code for quality and best practices
mode: subagent
langfusePromptPath: agents/reviewer/system
langfusePromptLabel: production # optional; this is the default
---The same two keys work in the .opencode.json agents block alongside
prompt (note the JSON schema requires model on an agent entry, so
restate it there even when only the prompt is changing):
{
"agents": {
"reviewer": {
"model": "vertexai.claude-sonnet-4-5-m",
"langfusePromptPath": "agents/reviewer/system",
"langfusePromptLabel": "staging"
}
}
}Declaring both an inline prompt and a langfusePromptPath — a non-empty
markdown body counts — is a load-time error rather than a precedence rule.
Each definition layer is a partial override, so a higher-priority layer may
declare langfusePromptLabel alone to re-label a path a lower one supplied;
a label that ends up with no path anywhere is dropped with a warning rather
than failing the boot.
Resolution happens when the agent is constructed, not when the registry is
loaded. For subagents (built per task spawn) and flow-step agents that
means an edit in the Langfuse UI reaches the next run bounded by cacheTTL,
with no restart. Primary agents (mode: agent) are built once at startup
and held for the process lifetime, so their prompt is pinned until
restart — see the freshness table in
telemetry.md.
A reference that cannot be resolved and has nothing cached fails agent construction naming the path; the agent never runs on an empty system prompt and never silently falls back to the built-in prompt for its name.
When enabled (default), automatically summarizes conversations approaching the context window limit (95%) and continues in a new session.
{ "autoCompact": true }Auto-approve mode skips interactive permission dialogs for ask-resolved permissions during a session. deny rules and disabled tools are still enforced — auto-approve only promotes ask decisions to allow.
- Toggle in TUI: type
/auto-approveto enable/disable for the current session - CLI flag:
opencode --auto-approvestarts the TUI with auto-approve on the first session - Scope: per-session only — new sessions start without auto-approve
- Subagents: child task sessions inherit auto-approve from the parent
- Non-interactive mode: already auto-approves all permissions, flag is ignored
Override the default shell (falls back to $SHELL or /bin/bash):
{
"shell": {
"path": "/bin/zsh",
"args": ["-l"]
}
}{
"mcpServers": {
"stdio-example": {
"type": "stdio",
"command": "path/to/server",
"env": [],
"args": []
},
"sse-example": {
"type": "sse",
"url": "https://example.org/mcp",
"headers": { "Authorization": "Bearer token" }
},
"http-example": {
"type": "http",
"url": "https://example.com/mcp",
"headers": { "Authorization": "Bearer token" }
}
}
}Optional per-server tuning:
callToolTimeoutSeconds— override the per-tool-call timeout (default 5 minutes). Raise it for slow servers.callToolMaxOutputBytes— cap a single tool call's output kept in the model context (default51200, i.e. 50KB). Output beyond the cap is spilled to a temp file and replaced with a head+tail preview that points the agent at the file, which it can thengrep/read/sed. This protects the context window from tools that return very large payloads (e.g. multi-MB CI build logs). Set a higher value to keep more inline, or a negative value to disable the cap entirely (unbounded — a single result can then overflow the context).
Every wait on an MCP server is bounded, so a server that starts but never answers cannot park an agent turn:
| Wait | Budget | Tunable |
|---|---|---|
Transport start (Start) |
20s (SSE only — a stdio transport is started by its constructor, and streamable-HTTP never fails here) | no |
Protocol handshake (initialize) |
30s | no |
Tool call (tools/call) |
5 min | callToolTimeoutSeconds |
| Client close | 30s, then the wait is abandoned | no |
| Shared client-cache entry | 30s backstop | no |
The handshake and cache budgets are deliberately not per-server tunable: initialize is one request/response with no work behind it, so a server that misses the budget is broken rather than slow. Only tool latency is genuinely server-specific. A blown budget surfaces as a normal tool error, so the agent can try another approach instead of hanging.
The close budget matters because a stdio transport's Close blocks in cmd.Wait() honouring no context: a child that ignores stdin EOF would otherwise hold the agent turn immediately after its tool call had correctly timed out. On timeout the close is abandoned rather than the caller blocked, which leaks one goroutine and one child process per wedged server for the life of the process — deliberately, since leaking the turn is worse and the transport exposes no handle to signal the child.
OpenCode auto-detects and starts LSP servers for your project's languages. Over 30 servers are built-in with auto-install support. See the full LSP guide for details.
{
"lsp": {
"gopls": {
"env": { "GOFLAGS": "-mod=vendor" },
"initialization": { "codelenses": { "test": true } }
},
"typescript": { "disabled": true },
"my-lsp": {
"command": "my-lsp-server",
"args": ["--stdio"],
"extensions": [".custom"]
}
},
"disableLSPDownload": false
}Disable auto-download of LSP binaries via config ("disableLSPDownload": true) or env var (OPENCODE_DISABLE_LSP_DOWNLOAD=true).
Local endpoint:
export LOCAL_ENDPOINT=http://localhost:1235/v1
export LOCAL_ENDPOINT_API_KEY=secret{
"agents": {
"coder": {
"model": "local.granite-3.3-2b-instruct@q8_0"
}
}
}YandexCloud AI Studio provides an OpenAI-compatible API. Set both environment variables:
export YANDEXCLOUD_API_KEY="AQVN..."
export YANDEXCLOUD_FOLDER_ID="b1g..."The folder ID is required for constructing model URIs (gpt://<folder_id>/<model_path>). If not set, the provider will fail with a clear error message.
Kimi K3 is served through Moonshot's Anthropic-compatible endpoint (https://api.moonshot.ai/anthropic) — the same integration path Moonshot documents for Claude Code — so it gets streaming thinking, tool-call streaming, vision, and reasoning replay out of the box:
export MOONSHOT_API_KEY="sk-..." # KIMI_API_KEY works as an aliasKimi K3 reasons by default; when reasoningEffort is not set for an agent it resolves to max (the only effort level K3 exposes at launch). Override the endpoint with providers.kimi.baseURL if needed:
{
"providers": { "kimi": { "apiKey": "sk-..." } },
"agents": { "coder": { "model": "kimi.kimi-k3" } }
}LiteLLM proxy:
{
"providers": {
"vertexai": {
"apiKey": "litellm-api-key",
"baseURL": "https://localhost/vertex_ai",
"headers": {
"x-litellm-api-key": "litellm-api-key"
}
}
}
}| Variable | Default | Purpose |
|---|---|---|
ANTHROPIC_API_KEY |
Anthropic Claude models | |
OPENAI_API_KEY |
OpenAI models | |
GEMINI_API_KEY |
Google Gemini models | |
VERTEXAI_PROJECT |
Google Cloud VertexAI | |
VERTEXAI_LOCATION |
Google Cloud VertexAI | |
VERTEXAI_LOCATION_COUNT |
VertexAI token count endpoint (global doesn't support) | |
AWS_ACCESS_KEY_ID |
AWS Bedrock | |
AWS_SECRET_ACCESS_KEY |
AWS Bedrock | |
AWS_REGION |
AWS Bedrock | |
YANDEXCLOUD_API_KEY |
YandexCloud AI Studio API key | |
YANDEXCLOUD_FOLDER_ID |
YandexCloud folder ID (required for model URI) | |
MOONSHOT_API_KEY |
Kimi (Moonshot) models | |
KIMI_API_KEY |
Alias for MOONSHOT_API_KEY |
|
LOCAL_ENDPOINT |
Self-hosted model endpoint | |
LOCAL_ENDPOINT_API_KEY |
Self-hosted model API key | |
LANGFUSE_PUBLIC_KEY |
Langfuse public key (guide) | |
LANGFUSE_SECRET_KEY |
Langfuse secret key | |
LANGFUSE_BASE_URL |
https://cloud.langfuse.com |
Langfuse host URL |
OPENCODE_USER_ID |
User ID for telemetry (overrides config) | |
SHELL |
Default shell | |
OPENCODE_SESSION_PROVIDER_TYPE |
sqlite |
Session storage backend (sqlite or mysql) |
OPENCODE_MYSQL_DSN |
MySQL connection string | |
OPENCODE_FILE_OP_TIMEOUT |
180 |
Timeout in seconds for glob/grep file operations |
OPENCODE_PROVIDER_STREAM_INACTIVITY_TIMEOUT |
300 |
Seconds to wait for next SSE event before treating stream as stalled |
OPENCODE_MAX_REPEAT_CALLS |
3 |
Max identical consecutive tool calls before loop detection triggers |
OPENCODE_SERVER_PASSWORD |
HTTP Basic Auth password for opencode serve (guide) |
|
OPENCODE_DEV_DEBUG |
false |
Enable development debug logging |
OPENCODE_DISABLE_LSP_DOWNLOAD |
false |
Disable automatic LSP binary downloads |
OPENCODE_DISABLE_CLAUDE_SKILLS |
false |
Disable .claude/skills/ discovery |
OPENCODE_DISABLE_CRON |
Disable cron scheduling entirely (guide) |
| Provider | Models |
|---|---|
| OpenAI | GPT-5, O3 Mini, O4 Mini |
| Anthropic | Claude 5 Opus (1M), Claude Fable 5 (1M), Claude 4.8 Opus (1M), Claude 4.7 Opus (1M), Claude 5 Sonnet (1M), Claude 4.6 Sonnet (1M), Claude 4.6 Opus (1M), Claude 4.5 Haiku |
| Google Gemini | Gemini 3.0 Pro, Gemini 3.0 Flash |
| AWS Bedrock | Claude 5 Opus (1M)(EU/Global), Claude Fable 5 (1M)(EU/Global), Claude 4.8 Opus (1M)(EU/Global), Claude 4.7 Opus (1M)(EU/Global), Claude 5 Sonnet (1M)(EU/Global), Claude 4.6 Sonnet (1M)(EU/Global), Claude 4.6 Opus (1M)(EU/Global), Claude 4.5 Haiku (EU/Global) |
| VertexAI | Gemini 3.0 Pro, Gemini 3.0 Flash, Claude 5 Opus (1M), Claude Fable 5 (1M), Claude 4.8 Opus (1M), Claude 4.7 Opus (1M), Claude 5 Sonnet (1M), Claude 4.6 Sonnet (1M), Claude 4.6 Opus (1M), Claude 4.5 Haiku |
| YandexCloud | Alice AI LLM, YandexGPT Pro 5.1, YandexGPT Pro 5, YandexGPT Lite 5, DeepSeek V3.2, Qwen3 235B, Qwen3.5 35B, gpt-oss-120b |
| Kimi (Moonshot) | Kimi K3 (1M) |
| Local | Any OpenAI-compatible API |
| Tool | Description |
|---|---|
glob |
Find files by pattern |
grep |
Search file contents |
ls |
List directory contents |
read |
Read file contents |
view_image |
View image files as base64 |
write |
Write to files |
edit |
Edit files |
multiedit |
Multiple edits in one file |
patch |
Apply patches to files |
lsp |
Code intelligence (go-to-definition, references, hover, etc.) |
delete |
Delete file or directory |
| Tool | Description |
|---|---|
bash |
Execute shell commands |
webfetch |
Fetch data from URLs |
websearch |
Search internet via configured WebSearch providers |
sourcegraph |
Search public repositories |
task |
Run sub-tasks with a subagent (supports subagent_type and task_id for resumption) |
skill |
Load agent skills on-demand (supports args for argument substitution and shell expansion) |
struct_output |
Emit structured JSON conforming to a user-supplied schema |
toolsearch |
Discover and load deferred tools on demand (auto-registered only when an agent declares deferredTools, guide) |
todowrite |
Create and maintain a structured task list for multi-step sessions (progress tracking for external UIs) |
croncreate / crondelete / cronlist |
Schedule, cancel, and list cron jobs that fire prompts via subagents (guide) |
| Shortcut | Action |
|---|---|
Ctrl+C |
Quit |
Ctrl+H |
Toggle help |
Ctrl+L |
View logs |
Ctrl+A |
Switch session |
Ctrl+N |
New session |
Ctrl+P |
Prune session |
Ctrl+K |
Command dialog |
Ctrl+O |
Model selection |
Ctrl+X |
Cancel generation |
Tab |
Switch primary agent |
Esc |
Close dialog / exit mode |
| Shortcut | Action |
|---|---|
i |
Focus editor |
Ctrl+S / Enter |
Send message |
Ctrl+E |
Open external editor |
Esc |
Blur editor |
| Shortcut | Action |
|---|---|
↑/k, ↓/j |
Navigate items |
←/h, →/l |
Switch tabs/providers |
Enter |
Select |
a / A / d |
Allow / Allow for session / Deny (permissions) |
- Go 1.24.0 or higher
git clone https://github.com/obukhovaa/opencode.git
cd opencode
make buildBuild and run OpenCode in a container:
# Build the Docker image (cross-compiles a Linux binary automatically)
make docker-buildAll CLI arguments are passed through directly:
# Non-interactive prompt
docker run opencode:latest -p "Explain context in Go" -f json -q
# Run a flow
docker run opencode:latest -F my-flow -A key1=value1 -A key2=value2
# With timeout
docker run opencode:latest -p "Refactor this module" -t 5m -qMount your configuration and workspace as volumes:
docker run -ti --rm \
-e LOCAL_ENDPOINT_API_KEY="${LOCAL_ENDPOINT_API_KEY}" \
-e LOCAL_ENDPOINT="${LOCAL_ENDPOINT}" \
-e VERTEXAI_PROJECT="${VERTEXAI_PROJECT}" \
-e VERTEXAI_LOCATION="${VERTEXAI_LOCATION:-global}" \
-e VERTEXAI_LOCATION_COUNT="${VERTEXAI_LOCATION_COUNT:-us-east5}" \
-v ~/.opencode.json:/workspace/.opencode.json \
-v $(pwd):/workspace \
--network opencode_default \
opencode:latest # you can pass args here, e.g. -p "Analyze this codebase"
To run non interactivly (you can pass [[#Command-Line Flags]])
docker run --rm \
-v ~/.opencode.json:/workspace/.opencode.json \
-v $(pwd):/workspace \
--network opencode_default \
opencode:latest -p "Analyze this codebase" -q
The container uses /workspace as its working directory. Mount .opencode.json there to provide configuration — it is not baked into the image.
make release SCOPE=patch
# or
make release SCOPE=minor- @isaacphi — mcp-language-server, foundation for the LSP client
- @adamdottv — Design direction and UI/UX architecture
- @kujtimiihoxha – Original OpenCode implementation
MIT — see LICENSE.
- Fork the repository
- Create a feature branch
- Commit your changes
- Open a Pull Request
