# ACP (Agent Client Protocol)
Source: https://anomalyco-opencode.mintlify.app/acp
Use OpenCode in any ACP-compatible editor via the Agent Client Protocol
OpenCode supports the [Agent Client Protocol](https://agentclientprotocol.com) (ACP), allowing you to use it directly in compatible editors and IDEs. ACP is an open protocol that standardizes communication between code editors and AI coding agents.
For a list of editors and tools that support ACP, check out the [ACP progress report](https://zed.dev/blog/acp-progress-report#available-now).
***
## What is ACP?
The Agent Client Protocol is an open standard for connecting AI coding agents to code editors. It enables:
* **Editor Integration** - Use OpenCode directly within your favorite editor
* **Unified Experience** - Consistent AI assistance across different development environments
* **Tool Interoperability** - Switch between different agents and editors seamlessly
* **Native UI** - Agents appear as native features in your editor's interface
OpenCode's ACP implementation provides full feature parity with the standalone TUI and web interfaces.
***
## Configuration
To use OpenCode via ACP, configure your editor to run the `opencode acp` command. The command starts OpenCode as an ACP-compatible subprocess that communicates with your editor over JSON-RPC via stdio.
***
## Supported Editors
### Zed
Add to your [Zed](https://zed.dev) settings (`~/.config/zed/settings.json`):
```json theme={null}
{
"agent_servers": {
"OpenCode": {
"command": "opencode",
"args": ["acp"]
}
}
}
```
To open OpenCode in Zed:
1. Open the Command Palette (`Cmd+Shift+P` or `Ctrl+Shift+P`)
2. Search for "agent: new thread"
3. Select the command to start a new OpenCode thread
#### Custom Keyboard Shortcut
Bind a keyboard shortcut by editing your `keymap.json`:
```json theme={null}
[
{
"bindings": {
"cmd-alt-o": [
"agent::NewExternalAgentThread",
{
"agent": {
"custom": {
"name": "OpenCode",
"command": {
"command": "opencode",
"args": ["acp"]
}
}
}
}
]
}
}
]
```
Now press `Cmd+Alt+O` (macOS) or `Ctrl+Alt+O` (Linux/Windows) to start OpenCode.
***
### JetBrains IDEs
Add to your [JetBrains IDE](https://www.jetbrains.com/) `acp.json` according to the [documentation](https://www.jetbrains.com/help/ai-assistant/acp.html):
```json theme={null}
{
"agent_servers": {
"OpenCode": {
"command": "/absolute/path/bin/opencode",
"args": ["acp"]
}
}
}
```
JetBrains requires an absolute path to the OpenCode binary. Use `which opencode` on Linux/macOS or `where opencode` on Windows to find the path.
To use OpenCode:
1. Open the AI Chat panel
2. Select "OpenCode" from the agent selector
3. Start chatting with OpenCode directly in your IDE
***
### Avante.nvim
Add to your [Avante.nvim](https://github.com/yetone/avante.nvim) configuration:
```lua theme={null}
{
acp_providers = {
["opencode"] = {
command = "opencode",
args = { "acp" }
}
}
}
```
#### With Environment Variables
If you need to pass environment variables:
```lua theme={null}
{
acp_providers = {
["opencode"] = {
command = "opencode",
args = { "acp" },
env = {
OPENCODE_API_KEY = os.getenv("OPENCODE_API_KEY")
}
}
}
}
```
***
### CodeCompanion.nvim
To use OpenCode as an ACP agent in [CodeCompanion.nvim](https://github.com/olimorris/codecompanion.nvim):
```lua theme={null}
require("codecompanion").setup({
interactions = {
chat = {
adapter = {
name = "opencode",
model = "claude-sonnet-4",
},
},
},
})
```
This configuration sets up CodeCompanion to use OpenCode as the ACP agent for chat interactions.
For environment variables like `OPENCODE_API_KEY`, refer to [Configuring Adapters: Environment Variables](https://codecompanion.olimorris.dev/getting-started#setting-an-api-key) in the CodeCompanion.nvim documentation.
***
## Features and Capabilities
OpenCode via ACP provides comprehensive feature support:
### Core Features
* **Full Tool Access** - All built-in OpenCode tools (file operations, bash commands, etc.)
* **Custom Tools** - Custom tools and slash commands from your configuration
* **MCP Servers** - Model Context Protocol servers configured in your OpenCode config
* **Project Rules** - Project-specific instructions from `AGENTS.md` files
* **Formatters and Linters** - Custom code formatters and linters
* **Agents** - Multiple agent modes with different capabilities
* **Permissions** - Fine-grained permission system for tool execution
### Session Management
* **Create Sessions** - Start new coding sessions
* **Resume Sessions** - Continue previous conversations
* **Fork Sessions** - Branch off from existing sessions to explore alternatives
* **List Sessions** - View and switch between all your sessions
### Model Selection
* **Multiple Models** - Access all models from your configured providers
* **Model Switching** - Change models mid-session
* **Model Variants** - Use extended thinking and other model variants
### Context and Tools
* **File Context** - Add files and code to the conversation
* **Image Support** - Send images for vision model analysis
* **Resource Links** - Reference external resources and documentation
* **Command Execution** - Run shell commands and see output
### Real-time Updates
* **Streaming Responses** - See AI responses as they're generated
* **Tool Progress** - Watch tool execution in real-time
* **Permission Requests** - Approve or deny tool usage interactively
* **Thinking Display** - View extended reasoning for supported models
### Collaboration
* **Session Sharing** - Share sessions with team members
* **Export** - Export conversations to Markdown
* **Comments** - Add inline comments to code sections
***
## Authentication
When using OpenCode via ACP, authentication is handled through the terminal:
```bash theme={null}
opencode auth login
```
Some editors with terminal-auth capability will automatically prompt you to run this command when authentication is needed.
***
## Permissions
OpenCode's permission system works seamlessly through ACP. When OpenCode needs to execute a tool, your editor will display a permission dialog with options:
* **Allow Once** - Execute this tool call only
* **Always Allow** - Execute this tool and automatically approve future calls
* **Reject** - Deny execution
Permissions are respected across all interfaces, so approvals in your editor apply to the TUI and web interface as well.
***
## Advanced Configuration
### Custom Working Directory
OpenCode respects the working directory set by your editor. ACP automatically provides the correct `cwd` for each session.
### MCP Server Configuration
MCP servers configured in your `opencode.json` are automatically available when using ACP:
```json theme={null}
{
"mcp": {
"my-server": {
"type": "local",
"command": ["node", "server.js"],
"environment": {
"API_KEY": "your-key"
}
}
}
}
```
### Custom Commands
Slash commands defined in your configuration work through ACP:
```json theme={null}
{
"commands": {
"build": {
"description": "Run the build",
"command": "npm run build"
}
}
}
```
Use `/build` in your editor's OpenCode interface to execute the command.
***
## Limitations
Some built-in slash commands like `/undo` and `/redo` are currently unsupported via ACP. These commands rely on Git operations that are better handled through your editor's native version control.
### Editor-Specific Limitations
Different editors may have varying levels of ACP support:
* Some editors may not display reasoning/thinking blocks
* Permission UI may vary between editors
* File diff presentation depends on editor capabilities
Check your editor's ACP documentation for specific feature support.
***
## Troubleshooting
### OpenCode Command Not Found
If your editor can't find the `opencode` command:
1. Ensure OpenCode is installed and in your PATH
2. Use the absolute path in the configuration:
```json theme={null}
{
"command": "/usr/local/bin/opencode",
"args": ["acp"]
}
```
3. Restart your editor after installation
### Authentication Errors
If you see authentication errors:
1. Run `opencode auth login` in your terminal
2. Verify your credentials are valid
3. Check that API keys are properly configured
4. Restart your editor after authentication
### Connection Issues
If the ACP connection fails:
1. Check that OpenCode is properly installed
2. Run `opencode acp` manually to verify it works
3. Check editor logs for error messages
4. Ensure no firewall or security software is blocking the process
### Performance Issues
If responses are slow:
1. Use `/compact` to reduce session context
2. Start fresh sessions for new tasks
3. Check your network connection if using remote models
4. Monitor token usage and context limits
***
## Comparison with TUI and Web
| Feature | ACP | TUI | Web |
| ------------------ | -------------------- | --------------- | ----------------- |
| File editing | Via editor | Terminal | Built-in editor |
| Diff viewing | Editor's diff viewer | Terminal output | Visual diff panel |
| Session management | Yes | Yes | Yes |
| Keyboard shortcuts | Editor-dependent | Full support | Full support |
| Multi-window | Editor-dependent | Single terminal | Multiple windows |
| Mobile support | No | Terminal apps | Yes |
| File tree | Editor's file tree | No | Built-in tree |
| Terminal | Editor's terminal | Native | Integrated panel |
Choose the interface that best fits your workflow:
* **ACP** - For integrated editor experience
* **TUI** - For keyboard-driven terminal workflows
* **Web** - For visual file browsing and collaboration
***
## Best Practices
### Session Organization
Keep sessions focused on specific tasks:
* Create separate sessions for different features
* Use descriptive session names (via `/rename` if supported)
* Fork sessions when exploring alternatives
### Context Management
Manage conversation context effectively:
* Add only relevant files to context
* Use `/compact` to summarize long conversations
* Start fresh sessions for unrelated work
### Permission Strategy
Develop a permission strategy that balances safety and convenience:
* Review tool calls before approving "Always Allow"
* Use "Allow Once" for sensitive operations
* Configure auto-approve for safe, repetitive tools
### Model Selection
Choose models appropriate for the task:
* Use extended thinking models for complex problems
* Switch to faster models for simple tasks
* Monitor costs with expensive models
***
## Future Development
The ACP protocol is actively evolving. Future improvements may include:
* Enhanced editor integration capabilities
* Additional tool execution modes
* Improved real-time collaboration features
* Extended permission granularity
Stay updated with OpenCode releases and ACP protocol developments for the latest features.
# Agents
Source: https://anomalyco-opencode.mintlify.app/agents
Configure and use specialized agents for different workflows.
Agents are specialized AI assistants that can be configured for specific tasks and workflows. They allow you to create focused tools with custom prompts, models, and tool access.
Use the Plan agent to analyze code and review suggestions without making any code changes.
You can switch between agents during a session or invoke them with the `@` mention.
***
## Types
There are two types of agents in OpenCode: primary agents and subagents.
***
### Primary agents
Primary agents are the main assistants you interact with directly. You can cycle through them using the **Tab** key, or your configured `switch_agent` keybind. These agents handle your main conversation. Tool access is configured via permissions — for example, Build has all tools enabled while Plan is restricted.
You can use the **Tab** key to switch between primary agents during a session.
OpenCode comes with two built-in primary agents: **Build** and **Plan**.
***
### Subagents
Subagents are specialized assistants that primary agents can invoke for specific tasks. You can also manually invoke them by **@ mentioning** them in your messages.
OpenCode comes with two built-in subagents: **General** and **Explore**.
***
## Built-in Agents
OpenCode comes with two built-in primary agents and two built-in subagents.
***
### Build
**Mode:** Primary
Build is the **default** primary agent with all tools enabled. This is the standard agent for development work where you need full access to file operations and system commands.
**Features:**
* Full tool access (read, write, edit, bash, etc.)
* Ideal for active development and implementation
* Can modify files and execute commands without restrictions
* Best for building features, fixing bugs, and refactoring code
***
### Plan
**Mode:** Primary
A restricted agent designed for planning and analysis. The permission system gives you more control and prevents unintended changes.
By default, all of the following are set to `ask`:
* **File edits**: All writes, patches, and edits
* **Bash commands**: All bash/shell commands
This agent is useful when you want the AI to analyze code, suggest changes, or create plans without making any actual modifications to your codebase.
**Use cases:**
* Code analysis and review
* Architecture planning
* Proposing refactoring strategies
* Exploring implementation options
***
### General
**Mode:** Subagent
A general-purpose agent for researching complex questions and executing multi-step tasks. Has full tool access (except todo), so it can make file changes when needed.
**Use cases:**
* Running multiple units of work in parallel
* Complex research tasks requiring file exploration
* Multi-step implementation tasks
* Tasks requiring both reading and writing
Use this to run multiple units of work in parallel by invoking it multiple times.
***
### Explore
**Mode:** Subagent
A fast, read-only agent for exploring codebases. Cannot modify files.
**Use cases:**
* Quickly find files by patterns
* Search code for keywords and patterns
* Answer questions about the codebase structure
* Code exploration without risk of modification
**Features:**
* Read-only access (read, grep, glob, ls)
* Fast and efficient for code discovery
* Safe for exploration without side effects
***
## Usage
### Switching Primary Agents
Use the **Tab** key to cycle through primary agents during a session. You can also use your configured `switch_agent` keybind.
### Invoking Subagents
Subagents can be invoked in two ways:
1. **Automatically** by primary agents for specialized tasks based on their descriptions
2. **Manually** by @ mentioning a subagent in your message:
```txt theme={null}
@general help me search for this function
```
```txt theme={null}
@explore find all React components in the src directory
```
### Navigation Between Sessions
When subagents create their own child sessions, you can navigate between the parent session and all child sessions:
* **Leader+Right** (or your configured `session_child_cycle` keybind) to cycle forward through parent → child1 → child2 → ... → parent
* **Leader+Left** (or your configured `session_child_cycle_reverse` keybind) to cycle backward
This allows you to seamlessly switch between the main conversation and specialized subagent work.
***
## Configure
You can customize the built-in agents or create your own through configuration. Agents can be configured in two ways:
***
### JSON Configuration
Configure agents in your `opencode.json` config file:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"agent": {
"build": {
"mode": "primary",
"model": "anthropic/claude-sonnet-4-20250514",
"prompt": "{file:./prompts/build.txt}",
"tools": {
"write": true,
"edit": true,
"bash": true
}
},
"plan": {
"mode": "primary",
"model": "anthropic/claude-haiku-4-20250514",
"tools": {
"write": false,
"edit": false,
"bash": false
}
},
"code-reviewer": {
"description": "Reviews code for best practices and potential issues",
"mode": "subagent",
"model": "anthropic/claude-sonnet-4-20250514",
"prompt": "You are a code reviewer. Focus on security, performance, and maintainability.",
"tools": {
"write": false,
"edit": false
}
}
}
}
```
***
### Markdown Configuration
You can also define agents using markdown files. Place them in:
* **Global**: `~/.config/opencode/agents/`
* **Per-project**: `.opencode/agents/`
```markdown title="~/.config/opencode/agents/review.md" theme={null}
---
description: Reviews code for quality and best practices
mode: subagent
model: anthropic/claude-sonnet-4-20250514
temperature: 0.1
tools:
write: false
edit: false
bash: false
---
You are in code review mode. Focus on:
- Code quality and best practices
- Potential bugs and edge cases
- Performance implications
- Security considerations
Provide constructive feedback without making direct changes.
```
The markdown file name becomes the agent name. For example, `review.md` creates a `review` agent.
***
## Configuration Options
### Description
Provide a brief description of what the agent does and when to use it.
```json title="opencode.json" theme={null}
{
"agent": {
"review": {
"description": "Reviews code for best practices and potential issues"
}
}
}
```
This is a **required** config option for custom agents.
***
### Mode
Control the agent's mode. The `mode` option determines how the agent can be used.
```json title="opencode.json" theme={null}
{
"agent": {
"review": {
"mode": "subagent"
}
}
}
```
The `mode` option can be set to `primary`, `subagent`, or `all`. If no `mode` is specified, it defaults to `all`.
***
### Model
Override the model for this agent. Useful for using different models optimized for different tasks.
If you don't specify a model, primary agents use the globally configured model while subagents will use the model of the primary agent that invoked them.
```json title="opencode.json" theme={null}
{
"agent": {
"plan": {
"model": "anthropic/claude-haiku-4-20250514"
}
}
}
```
The model ID uses the format `provider/model-id`. For example, if you're using OpenCode Zen, you would use `opencode/gpt-5.1-codex` for GPT 5.1 Codex.
***
### Prompt
Specify a custom system prompt file for this agent.
```json title="opencode.json" theme={null}
{
"agent": {
"review": {
"prompt": "{file:./prompts/code-review.txt}"
}
}
}
```
The path is relative to where the config file is located. This works for both the global OpenCode config and the project-specific config.
***
### Temperature
Control the randomness and creativity of the AI's responses.
```json title="opencode.json" theme={null}
{
"agent": {
"plan": {
"temperature": 0.1
},
"creative": {
"temperature": 0.8
}
}
}
```
Temperature values typically range from 0.0 to 1.0:
* **0.0-0.2**: Very focused and deterministic responses, ideal for code analysis and planning
* **0.3-0.5**: Balanced responses with some creativity, good for general development tasks
* **0.6-1.0**: More creative and varied responses, useful for brainstorming and exploration
If no temperature is specified, OpenCode uses model-specific defaults (typically 0 for most models, 0.55 for Qwen models).
***
### Tools
Control which tools are available to this agent.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"tools": {
"write": true,
"bash": true
},
"agent": {
"plan": {
"tools": {
"write": false,
"bash": false
}
}
}
}
```
The agent-specific config overrides the global config.
You can also use wildcards to control multiple tools at once:
```json title="opencode.json" theme={null}
{
"agent": {
"readonly": {
"tools": {
"mymcp_*": false,
"write": false,
"edit": false
}
}
}
}
```
[Learn more about tools](/tools).
***
### Permissions
Configure permissions to manage what actions an agent can take. Permissions for `edit`, `bash`, and `webfetch` tools can be configured to:
* `"ask"` — Prompt for approval before running the tool
* `"allow"` — Allow all operations without approval
* `"deny"` — Disable the tool
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "deny"
},
"agent": {
"build": {
"permission": {
"edit": "ask"
}
}
}
}
```
You can set permissions for specific bash commands:
```json title="opencode.json" theme={null}
{
"agent": {
"build": {
"permission": {
"bash": {
"*": "ask",
"git status *": "allow",
"git push": "ask"
}
}
}
}
}
```
[Learn more about permissions](/permissions).
***
### Max Steps
Control the maximum number of agentic iterations an agent can perform before being forced to respond with text only.
```json title="opencode.json" theme={null}
{
"agent": {
"quick-thinker": {
"description": "Fast reasoning with limited iterations",
"prompt": "You are a quick thinker. Solve problems with minimal steps.",
"steps": 5
}
}
}
```
When the limit is reached, the agent receives a special system prompt instructing it to respond with a summarization of its work and recommended remaining tasks.
***
### Task Permissions
Control which subagents an agent can invoke via the Task tool. Uses glob patterns for flexible matching.
```json title="opencode.json" theme={null}
{
"agent": {
"orchestrator": {
"mode": "primary",
"permission": {
"task": {
"*": "deny",
"orchestrator-*": "allow",
"code-reviewer": "ask"
}
}
}
}
}
```
When set to `deny`, the subagent is removed from the Task tool description entirely, so the model won't attempt to invoke it.
Rules are evaluated in order, and the **last matching rule wins**. In the example above, `orchestrator-planner` matches both `*` (deny) and `orchestrator-*` (allow), but since `orchestrator-*` comes after `*`, the result is `allow`.
Users can always invoke any subagent directly via the `@` autocomplete menu, even if the agent's task permissions would deny it.
***
### Hidden
Hide a subagent from the `@` autocomplete menu with `hidden: true`. Useful for internal subagents that should only be invoked programmatically by other agents via the Task tool.
```json title="opencode.json" theme={null}
{
"agent": {
"internal-helper": {
"mode": "subagent",
"hidden": true
}
}
}
```
Only applies to `mode: subagent` agents.
***
### Other Options
Customize the agent's visual appearance in the UI.
Use a valid hex color (e.g., `#FF5733`) or theme color: `primary`, `secondary`, `accent`, `success`, `warning`, `error`, `info`.
```json title="opencode.json" theme={null}
{
"agent": {
"creative": {
"color": "#ff6b6b"
},
"code-reviewer": {
"color": "accent"
}
}
}
```
Control response diversity with the `top_p` option. Alternative to temperature for controlling randomness.
```json title="opencode.json" theme={null}
{
"agent": {
"brainstorm": {
"top_p": 0.9
}
}
}
```
Values range from 0.0 to 1.0. Lower values are more focused, higher values more diverse.
Set to `true` to disable the agent.
```json title="opencode.json" theme={null}
{
"agent": {
"review": {
"disable": true
}
}
}
```
Any other options you specify in your agent configuration will be passed through directly to the provider as model options. This allows you to use provider-specific features.
For example, with OpenAI's reasoning models, you can control the reasoning effort:
```json title="opencode.json" theme={null}
{
"agent": {
"deep-thinker": {
"description": "Agent that uses high reasoning effort for complex problems",
"model": "openai/gpt-5",
"reasoningEffort": "high",
"textVerbosity": "low"
}
}
}
```
***
## Creating Agents
You can create new agents using the CLI command:
```bash theme={null}
opencode agent create
```
This interactive command will:
1. Ask where to save the agent (global or project-specific)
2. Request a description of what the agent should do
3. Generate an appropriate system prompt and identifier
4. Let you select which tools the agent can access
5. Create a markdown file with the agent configuration
***
## Use Cases
Here are some common use cases for different agents:
* **Build agent**: Full development work with all tools enabled
* **Plan agent**: Analysis and planning without making changes
* **Review agent**: Code review with read-only access plus documentation tools
* **Debug agent**: Focused on investigation with bash and read tools enabled
* **Docs agent**: Documentation writing with file operations but no system commands
***
## Examples
Do you have an agent you'd like to share? [Submit a PR](https://github.com/anomalyco/opencode).
### Documentation Agent
```markdown title="~/.config/opencode/agents/docs-writer.md" theme={null}
---
description: Writes and maintains project documentation
mode: subagent
tools:
bash: false
---
You are a technical writer. Create clear, comprehensive documentation.
Focus on:
- Clear explanations
- Proper structure
- Code examples
- User-friendly language
```
### Security Auditor
```markdown title="~/.config/opencode/agents/security-auditor.md" theme={null}
---
description: Performs security audits and identifies vulnerabilities
mode: subagent
tools:
write: false
edit: false
---
You are a security expert. Focus on identifying potential security issues.
Look for:
- Input validation vulnerabilities
- Authentication and authorization flaws
- Data exposure risks
- Dependency vulnerabilities
- Configuration security issues
```
# agent
Source: https://anomalyco-opencode.mintlify.app/cli/agent
Manage custom agents for specialized tasks
## Overview
The `agent` command helps you create and manage custom agents with specialized system prompts and tool configurations. Agents can be:
* **Primary agents**: Act as the main assistant
* **Subagents**: Specialized agents invoked by other agents
* **All-purpose**: Can function in both roles
## Usage
```bash theme={null}
opencode agent [command]
```
## Subcommands
Generate a new custom agent
Show all available agents
***
## create
Create a new agent with custom configuration.
### Usage
```bash theme={null}
opencode agent create
```
### Interactive Flow
The command guides you through creating an agent:
1. **Choose location**: Global or project-specific
2. **Describe purpose**: What the agent should do
3. **Generate configuration**: LLM creates system prompt
4. **Select tools**: Choose which tools to enable
5. **Set mode**: Primary, subagent, or both
### Example Session
```bash theme={null}
opencode agent create
```
```
◆ Location
│ ● Current project (/path/to/project)
│ Global (~/.config/opencode)
└
◆ Description
│ A TypeScript expert that helps write type-safe code
└
◇ Agent typescript-expert generated
◆ Select tools to enable (Space to toggle)
│ ◼ bash
│ ◼ read
│ ◼ write
│ ◼ edit
│ ◼ list
│ ◼ glob
│ ◼ grep
│ ◼ webfetch
│ ◻ task
│ ◻ todowrite
│ ◻ todoread
└
◆ Agent mode
│ ● All
│ Primary
│ Subagent
└
✓ Agent created: .opencode/agent/typescript-expert.md
✓ Done
```
### Options
Directory path to generate the agent file
What the agent should do
Agent mode: `all`, `primary`, or `subagent`
Comma-separated list of tools to enable. Empty string disables all tools.
Model to use for agent generation (format: `provider/model`). Short form: `-m`
### Non-Interactive Mode
Provide all options to create an agent without prompts:
```bash theme={null}
opencode agent create \
--path .opencode \
--description "TypeScript expert for type-safe code" \
--mode all \
--tools "bash,read,write,edit,glob,grep" \
--model anthropic/claude-4.5-sonnet
```
The command outputs the created file path:
```
.opencode/agent/typescript-expert.md
```
***
## list
Show all available agents.
### Usage
```bash theme={null}
opencode agent list
```
### Example Output
```
default (all)
{"enabled":true,"tools":["bash","read","write",...]}
typescript-expert (primary)
{"enabled":true,"tools":["bash","read","write","edit","glob","grep"]}
security-auditor (subagent)
{"enabled":true,"tools":["read","grep","bash"]}
```
Shows:
* Agent name and mode
* Permission/tool configuration
* Whether agent is enabled
***
## Agent Structure
Agents are Markdown files with YAML frontmatter:
```markdown theme={null}
---
description: When to use this agent
mode: all
tools:
bash: true
read: true
write: true
task: false
todowrite: false
---
You are a TypeScript expert specializing in type-safe code...
## Guidelines
- Always use explicit types
- Prefer interfaces over types
- Use strict mode
```
### Frontmatter Fields
Brief description of when to use this agent. Shown to other agents when selecting subagents.
Agent mode:
* `all`: Can be primary or subagent
* `primary`: Only usable as main agent
* `subagent`: Only for task delegation
Tool configuration. Omitted tools default to enabled. Set to `false` to disable:
```yaml theme={null}
tools:
bash: false
task: false
```
### Available Tools
* **bash**: Execute shell commands
* **read**: Read file contents
* **write**: Create new files
* **edit**: Modify existing files
* **list**: List directory contents
* **glob**: Find files by pattern
* **grep**: Search file contents
* **webfetch**: Fetch web pages
* **task**: Delegate to subagents
* **todowrite**: Manage task lists
* **todoread**: Read task lists
## Agent Locations
### Project Agents
Stored in `.opencode/agent/` in your project:
```
my-project/
├── .opencode/
│ └── agent/
│ ├── typescript-expert.md
│ └── api-designer.md
```
Available only in this project.
### Global Agents
Stored in `~/.config/opencode/agent/`:
```
~/.config/opencode/
└── agent/
├── security-auditor.md
└── documentation-writer.md
```
Available in all projects.
### Priority
Project agents override global agents with the same name.
## Using Agents
### Primary Agent
Select agent when starting OpenCode:
```bash theme={null}
opencode --agent typescript-expert
```
Or in configuration:
```json theme={null}
{
"agent": "typescript-expert"
}
```
Switch agents mid-session:
```
/agent security-auditor
```
### Subagent Delegation
Agents can invoke specialized subagents:
```
User: "Audit the authentication code for security issues"
Assistant uses Task tool:
subagent_type: security-auditor
description: Review auth.ts for security vulnerabilities
```
The main agent:
1. Identifies the need for specialized help
2. Selects appropriate subagent based on descriptions
3. Delegates the subtask
4. Receives results and continues
## Agent Generation
When you create an agent, OpenCode uses an LLM to generate:
* **Identifier**: Filename-safe name (e.g., `typescript-expert`)
* **Description**: When to use this agent
* **System prompt**: Detailed instructions and guidelines
### Generation Model
By default, uses your configured default model. Override with `--model`:
```bash theme={null}
opencode agent create --model anthropic/claude-4.5-sonnet
```
### Generation Quality
For best results:
* Be specific in your description
* Mention key technologies or domains
* Include constraints or preferences
* Reference coding standards if applicable
Example descriptions:
```bash theme={null}
# ✓ Good
"React expert specializing in hooks, performance optimization, and accessibility"
# ✗ Too vague
"Help with React"
```
## Example Agents
### Security Auditor
```markdown theme={null}
---
description: Security expert for finding vulnerabilities and suggesting fixes
mode: subagent
tools:
read: true
grep: true
bash: true
write: false
edit: false
---
You are a security expert specializing in code audits...
## Focus Areas
- Authentication and authorization
- Input validation and sanitization
- SQL injection and XSS prevention
- Secure API design
```
### Documentation Writer
```markdown theme={null}
---
description: Technical writer for creating clear, comprehensive documentation
mode: all
tools:
read: true
write: true
edit: true
glob: true
---
You are a technical writer who creates clear documentation...
## Style Guide
- Use active voice
- Write in second person
- Include code examples
- Structure with clear headings
```
### Test Specialist
```markdown theme={null}
---
description: Testing expert for writing unit and integration tests
mode: subagent
tools:
read: true
write: true
bash: true
---
You specialize in writing comprehensive tests...
## Testing Principles
- Test behavior, not implementation
- Arrange-Act-Assert pattern
- Meaningful test names
- Mock external dependencies
```
## Best Practices
Create specialized agents for specific domains rather than generalists
Write descriptions that help other agents know when to delegate
Only enable tools the agent needs to reduce confusion
Follow the same structure across your agent files
## Troubleshooting
### Agent Not Found
**Problem**: `agent "name" not found`
**Solutions**:
* Run `opencode agent list` to see available agents
* Check filename matches agent name
* Verify file is in correct location (`.opencode/agent/` or `~/.config/opencode/agent/`)
* Ensure file has `.md` extension
### Generation Failed
**Problem**: LLM fails to generate agent
**Solutions**:
* Check you're authenticated: `opencode auth list`
* Try a different model with `--model`
* Make description more specific
* Check internet connectivity
### Subagent Mode Error
**Problem**: `agent "name" is a subagent, not a primary agent`
**Solutions**:
* Use a different agent marked as `primary` or `all`
* Change the agent's mode to `all` in its frontmatter
* Run `opencode agent list` to see agent modes
### File Already Exists
**Problem**: `Agent file already exists`
**Solutions**:
* Delete or rename the existing agent file
* Use a different description that generates a unique identifier
* Manually create with a custom filename
## Related Topics
Learn about how agents work
Understand available tools
Configure agent permissions
Set default agent in config
# attach
Source: https://anomalyco-opencode.mintlify.app/cli/attach
Attach terminal to running OpenCode backend server
## Overview
The `attach` command connects a terminal UI (TUI) to an already running OpenCode backend server started via [`serve`](/cli/serve) or [`web`](/cli/web) commands. This allows you to:
* Use the TUI with a remote OpenCode backend
* Access the same session from multiple terminals
* Connect to a server running on another machine
* Maintain long-running server instances
## Usage
```bash theme={null}
opencode attach [url]
```
If no URL is provided, OpenCode will prompt you to select from recently connected servers or enter a new URL.
## Options
URL of the running OpenCode server (e.g., `http://localhost:4096` or `http://192.168.1.100:4096`)
Working directory to start TUI in. This is the directory context on the remote server.
Session ID to continue. Short form: `-s`
## Examples
### Basic Usage
Start a server in one terminal:
```bash theme={null}
# Terminal 1
opencode serve --port 4096
```
Attach from another terminal:
```bash theme={null}
# Terminal 2
opencode attach http://localhost:4096
```
### Remote Server Connection
Connect to OpenCode running on a remote machine:
```bash theme={null}
# On remote server (192.168.1.100)
opencode web --hostname 0.0.0.0 --port 4096
# On local machine
opencode attach http://192.168.1.100:4096
```
### Specify Working Directory
Set the working directory context on the remote server:
```bash theme={null}
opencode attach http://localhost:4096 --dir /path/to/project
```
### Continue Specific Session
Attach and continue a specific session:
```bash theme={null}
opencode attach http://localhost:4096 --session abc123def456
```
## Use Cases
### Development Workflow
Maintain a persistent server while working:
```bash theme={null}
# Start server once in a tmux/screen session
opencode serve --port 4096
# Attach from any terminal window as needed
opencode attach http://localhost:4096
# Detach with Ctrl+C, server keeps running
```
### Team Collaboration
Multiple team members can attach to the same server:
```bash theme={null}
# Team member 1 starts server
opencode web --hostname 0.0.0.0 --port 4096
# Team member 2 attaches via TUI
opencode attach http://192.168.1.100:4096
# Team member 3 accesses via browser
open http://192.168.1.100:4096
```
### Remote Development
Run OpenCode on a powerful remote machine:
```bash theme={null}
# On remote dev server with GPU
ssh dev-server
opencode serve --hostname 0.0.0.0 --port 4096
# On local laptop
opencode attach http://dev-server:4096
```
### Multiple Projects
Run separate servers for different projects:
```bash theme={null}
# Project 1
opencode serve --port 4096 &
# Project 2
opencode serve --port 4097 &
# Attach to either project
opencode attach http://localhost:4096 # Project 1
opencode attach http://localhost:4097 # Project 2
```
## Authentication
If the server requires authentication, provide credentials:
```bash theme={null}
# Set credentials in environment
export OPENCODE_SERVER_USERNAME="admin"
export OPENCODE_SERVER_PASSWORD="your-password"
# Or use URL format (not recommended for scripts)
opencode attach http://admin:password@localhost:4096
```
Avoid including credentials directly in commands or scripts. Use environment variables instead.
## Comparison with Other Modes
| Feature | TUI (default) | attach | serve | web |
| ------------------ | ------------- | -------- | ----- | --- |
| Terminal interface | ✓ | ✓ | ✗ | ✗ |
| Web interface | ✗ | ✗ | ✗ | ✓ |
| API access | ✗ | ✓ | ✓ | ✓ |
| Remote access | ✗ | ✓ | ✓ | ✓ |
| Multiple clients | ✗ | ✓ | ✓ | ✓ |
| Background server | ✗ | Required | ✓ | ✓ |
## Detaching
To detach from the server without stopping it:
1. Press `Ctrl+C` in the attached terminal
2. The TUI session ends
3. The server continues running
4. Attach again anytime to resume
To stop the server:
1. Connect to the terminal running `serve` or `web`
2. Press `Ctrl+C` to stop the server
3. All attached clients will disconnect
## Session Management
When you attach without specifying a session:
* OpenCode shows you available sessions
* Select an existing session to continue
* Or create a new session
To attach to a specific session directly:
```bash theme={null}
opencode attach http://localhost:4096 --session
```
## Troubleshooting
### Connection Refused
**Problem**: `Error: connect ECONNREFUSED`
**Solutions**:
* Verify the server is running
* Check the URL and port are correct
* Ensure firewall allows the connection
* Confirm the server uses `--hostname 0.0.0.0` for network access
### Authentication Failed
**Problem**: `Error: 401 Unauthorized`
**Solutions**:
* Verify `OPENCODE_SERVER_PASSWORD` matches the server
* Check `OPENCODE_SERVER_USERNAME` if customized
* Ensure credentials are set in environment variables
### Session Not Found
**Problem**: Session ID doesn't exist
**Solutions**:
* Omit `--session` to see available sessions
* Verify the session ID is correct
* Check if the session was deleted
### Network Timeout
**Problem**: Connection times out
**Solutions**:
* Check network connectivity: `ping `
* Verify server is reachable: `curl http://:`
* Check firewall rules and network segmentation
* Try using IP address instead of hostname
## Advanced Configuration
### SSH Tunneling
Securely connect to a remote server over SSH:
```bash theme={null}
# Create SSH tunnel
ssh -L 4096:localhost:4096 user@remote-server
# In another terminal, attach via tunnel
opencode attach http://localhost:4096
```
### Reverse Proxy
Use a reverse proxy (nginx, Apache) for TLS and advanced routing:
```nginx theme={null}
# nginx config
location /opencode/ {
proxy_pass http://localhost:4096/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
```
Then attach:
```bash theme={null}
opencode attach https://example.com/opencode/
```
## Related Commands
Start headless backend server
Start server with web interface
Run commands non-interactively
Configure network access
# auth
Source: https://anomalyco-opencode.mintlify.app/cli/auth
Manage provider credentials and login
## Overview
The `auth` command manages authentication credentials for AI model providers. OpenCode uses [models.dev](https://models.dev) provider list, allowing you to use API keys from any supported provider.
Credentials are stored in `~/.local/share/opencode/auth.json` and loaded automatically when OpenCode starts.
## Usage
```bash theme={null}
opencode auth [command]
```
## Subcommands
Add credentials for a provider
Show all authenticated providers
Remove credentials for a provider
***
## login
Authenticate with a provider by storing API credentials.
### Usage
```bash theme={null}
opencode auth login [url]
```
### Interactive Flow
When run without arguments, the command guides you through authentication:
1. **Select provider** from the list
2. **Enter API key** when prompted
3. **Credentials saved** to auth file
```bash theme={null}
opencode auth login
```
Example interaction:
```
◆ Select provider
│ ● Anthropic (Claude Max or API key)
│ OpenAI (ChatGPT Plus/Pro or API key)
│ Google
│ OpenRouter
│ GitHub Copilot
│ Other
└
◆ Enter your API key
│ sk-ant-...
└
✓ Done
```
### Provider-Specific Instructions
#### OpenCode
Recommended for best experience:
```bash theme={null}
opencode auth login
# Select: OpenCode
# Get API key: https://opencode.ai/auth
```
#### Anthropic
For Claude models:
```bash theme={null}
opencode auth login
# Select: Anthropic
# Get API key: https://console.anthropic.com/
```
#### OpenAI
For GPT models:
```bash theme={null}
opencode auth login
# Select: OpenAI
# Get API key: https://platform.openai.com/api-keys
```
#### GitHub Copilot
Use your Copilot subscription:
```bash theme={null}
opencode auth login
# Select: GitHub Copilot
# Follow OAuth flow
```
#### Amazon Bedrock
Bedrock uses AWS credential chain:
```bash theme={null}
opencode auth login
# Select: Amazon Bedrock
# Configure via AWS CLI or environment variables
```
Authentication priority:
1. Bearer token (`AWS_BEARER_TOKEN_BEDROCK` or `/connect`)
2. AWS credential chain (profile, access keys, IAM roles)
Configure in `opencode.json`:
```json theme={null}
{
"provider": {
"amazon-bedrock": {
"profile": "default",
"region": "us-east-1",
"endpoint": "https://bedrock-runtime.us-east-1.amazonaws.com"
}
}
}
```
#### Vercel AI Gateway
```bash theme={null}
opencode auth login
# Select: Vercel
# Get API key: https://vercel.link/ai-gateway-token
```
#### Custom Providers
For providers not in the default list:
```bash theme={null}
opencode auth login
# Select: Other
# Enter provider ID (lowercase, hyphens only)
# Enter API key
```
Then configure the provider in `opencode.json`. See [provider documentation](/providers) for details.
### URL-Based Authentication
For custom authentication servers:
```bash theme={null}
opencode auth login https://custom-provider.com
```
The provider must expose `/.well-known/opencode` with authentication information.
***
## list
Display all authenticated providers.
### Usage
```bash theme={null}
opencode auth list
```
Alias:
```bash theme={null}
opencode auth ls
```
### Example Output
```
Credentials ~/.local/share/opencode/auth.json
◇ Anthropic (api)
◇ OpenAI (api)
◇ GitHub Copilot (oauth)
◇ OpenCode (api)
4 credentials
```
If environment variables are set, they're also shown:
```
Environment
◇ Google (GOOGLE_GENERATIVE_AI_API_KEY)
◇ OpenRouter (OPENROUTER_API_KEY)
2 environment variables
```
### Authentication Types
The command shows the authentication method:
* **api**: API key authentication
* **oauth**: OAuth-based authentication
* **wellknown**: Custom authentication endpoint
***
## logout
Remove stored credentials for a provider.
### Usage
```bash theme={null}
opencode auth logout
```
### Interactive Flow
1. Shows list of authenticated providers
2. Select provider to remove
3. Credentials deleted from auth file
```bash theme={null}
opencode auth logout
```
Example:
```
◆ Select provider
│ ● Anthropic (api)
│ OpenAI (api)
│ GitHub Copilot (oauth)
└
✓ Logout successful
```
***
## Credential Storage
Credentials are stored in:
```
~/.local/share/opencode/auth.json
```
Format:
```json theme={null}
{
"anthropic": {
"type": "api",
"key": "sk-ant-..."
},
"openai": {
"type": "api",
"key": "sk-..."
},
"github-copilot": {
"type": "oauth",
"access": "...",
"refresh": "...",
"expires": 1234567890
}
}
```
Keep your `auth.json` file secure. It contains sensitive API keys.
## Environment Variables
You can also provide credentials via environment variables:
```bash theme={null}
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export GOOGLE_GENERATIVE_AI_API_KEY="..."
```
Or in a `.env` file in your project:
```bash theme={null}
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
```
Environment variables take precedence over stored credentials.
## Authentication Priority
OpenCode loads credentials in this order:
1. **Environment variables** (`PROVIDER_API_KEY`)
2. **Project `.env` file**
3. **Stored credentials** (`~/.local/share/opencode/auth.json`)
Higher priority sources override lower ones.
## Provider Discovery
When you run `opencode auth login`, the list of providers comes from:
1. **models.dev** - Central provider registry
2. **Plugins** - Providers registered by installed plugins
3. **Custom config** - Providers defined in `opencode.json`
Disable providers in configuration:
```json theme={null}
{
"disabled_providers": ["openrouter", "together"]
}
```
Or enable only specific providers:
```json theme={null}
{
"enabled_providers": ["anthropic", "openai", "opencode"]
}
```
## OAuth Authentication
Some providers use OAuth instead of API keys:
### Automatic Flow
For providers with automatic OAuth:
1. Select provider
2. Browser opens to authorization page
3. Grant permissions
4. Tokens saved automatically
### Manual Code Flow
For providers requiring manual code entry:
1. Select provider
2. Open authorization URL
3. Copy authorization code
4. Paste code when prompted
5. Tokens saved
### Token Refresh
OpenCode automatically refreshes OAuth tokens before they expire. If refresh fails:
```bash theme={null}
# Re-authenticate
opencode auth logout
opencode auth login
```
## Plugin Authentication
Plugins can register custom authentication methods. When you select a plugin-registered provider:
1. Plugin's authentication flow executes
2. May open browser, prompt for input, or use other methods
3. Credentials saved to auth file
See [plugin documentation](/plugins) for creating custom authentication.
## Security Best Practices
For CI/CD and automated environments
Update API keys periodically
Use least-privilege API keys when possible
Add `.env` and `auth.json` to `.gitignore`
## Troubleshooting
### Authentication Failed
**Problem**: Invalid API key error
**Solutions**:
* Verify key is correct (copy/paste carefully)
* Check key has proper permissions
* Ensure key hasn't expired
* Try generating a new key
### Provider Not Listed
**Problem**: Can't find provider in list
**Solutions**:
* Run `opencode models --refresh` to update provider list
* Select "Other" and enter provider ID manually
* Check provider is supported on [models.dev](https://models.dev)
* Configure custom provider in `opencode.json`
### OAuth Failed
**Problem**: OAuth authorization fails
**Solutions**:
* Try again (tokens may have expired)
* Clear browser cookies for the provider
* Check internet connectivity
* Verify the provider supports OAuth
### Environment Variables Not Working
**Problem**: Environment variables not recognized
**Solutions**:
* Check variable name matches provider convention
* Verify variables are exported: `export VARIABLE=value`
* Confirm `.env` file is in project root
* Run `opencode auth list` to see detected variables
## Related Commands
List available models from providers
Learn about provider configuration
Configure provider settings
Custom authentication via plugins
# /connect - Connect to Services
Source: https://anomalyco-opencode.mintlify.app/cli/commands/connect
Connect to external services and resources during a session
## Overview
The `/connect` command (when available through plugins or MCP servers) allows you to establish connections to external services during an OpenCode session. This enables real-time access to:
* Cloud resources (AWS, GCP, Azure)
* Databases (PostgreSQL, MySQL, MongoDB)
* APIs and webhooks
* Development servers
* Remote machines
The `/connect` command is provided by plugins or MCP servers. Available connection types depend on your installed integrations.
## Usage
```
/connect [credentials]
```
## Examples
### Database Connections
Connect to a database:
```
/connect postgres postgresql://user:pass@localhost:5432/mydb
/connect mongodb mongodb://localhost:27017/myapp
/connect redis redis://localhost:6379
```
Once connected, the agent can:
* Query data
* Inspect schema
* Run migrations
* Analyze performance
### Cloud Services
Connect to cloud providers:
```
/connect aws
/connect gcp project-id
/connect azure subscription-id
```
Requires appropriate credentials (environment variables, config files, or IAM roles).
### Development Servers
Connect to local or remote development servers:
```
/connect dev http://localhost:3000
/connect staging https://staging.example.com
```
## Configuration
Connections can be pre-configured in `opencode.json`:
```json theme={null}
{
"connections": {
"database": {
"type": "postgres",
"url": "postgresql://localhost:5432/myapp"
},
"api": {
"type": "http",
"baseURL": "https://api.example.com",
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
}
}
}
}
```
Then connect by name:
```
/connect database
/connect api
```
## Environment Variables
Use environment variables for sensitive credentials:
```bash theme={null}
export DATABASE_URL="postgresql://user:pass@localhost/mydb"
export API_TOKEN="your-api-token"
export AWS_PROFILE="development"
```
Reference in configuration:
```json theme={null}
{
"connections": {
"db": {
"url": "${DATABASE_URL}"
}
}
}
```
## Security
Never commit credentials to version control. Use environment variables or secure credential stores.
### Best Practices
1. **Use environment variables** for all credentials
2. **Add `.env` to `.gitignore`**
3. **Rotate credentials** regularly
4. **Use least-privilege** access
5. **Enable MFA** where available
### Credential Storage
OpenCode respects standard credential chains:
* **AWS**: `~/.aws/credentials`, IAM roles, environment variables
* **GCP**: Application Default Credentials, service accounts
* **Azure**: Azure CLI credentials, managed identities
* **Databases**: Connection strings, environment variables
## MCP Server Connections
Many [MCP servers](/mcp-servers) provide connection capabilities:
### Filesystem
```
/connect filesystem /path/to/directory
```
### GitHub
```
/connect github owner/repo
```
### Slack
```
/connect slack workspace-name
```
### Google Drive
```
/connect gdrive
```
See each MCP server's documentation for specific connection syntax.
## Plugin Connections
[Plugins](/plugins) can provide custom connection types:
```typescript theme={null}
// In a plugin
export const hooks: Hooks = {
connect: {
types: [{
name: 'myservice',
description: 'Connect to MyService',
async connect(args) {
// Establish connection
return { connected: true, client: myClient }
}
}]
}
}
```
Then use:
```
/connect myservice credentials
```
## Use Cases
### Database Analysis
```
User: /connect postgres postgresql://localhost/myapp
User: Show me the 10 slowest queries
Assistant:
- Connects to database
- Queries pg_stat_statements
- Analyzes results
- Suggests optimizations
```
### Cloud Resource Management
```
User: /connect aws
User: List all EC2 instances and their costs
Assistant:
- Authenticates with AWS
- Fetches EC2 instances
- Retrieves cost data
- Presents summary
```
### API Testing
```
User: /connect api
User: Test the user registration endpoint
Assistant:
- Connects to API
- Sends test requests
- Validates responses
- Reports issues
```
## Connection Lifecycle
1. **Connect**: Establish connection with credentials
2. **Use**: Agent accesses the service as needed
3. **Persist**: Connection remains for the session
4. **Disconnect**: Automatic cleanup on session end
## Troubleshooting
### Connection Failed
**Problem**: Cannot connect to service
**Solutions**:
* Verify credentials are correct
* Check network connectivity
* Ensure service is running
* Review firewall rules
* Check permission/IAM policies
### Authentication Error
**Problem**: Credentials rejected
**Solutions**:
* Verify credentials haven't expired
* Check credential format
* Ensure correct environment variables are set
* Test credentials outside OpenCode
### Command Not Available
**Problem**: `/connect` command not found
**Solutions**:
* Install an MCP server that provides connections
* Add a plugin with connection support
* Check that the server/plugin is properly configured
## Related Topics
Install servers with connection capabilities
Create custom connection types
Pre-configure connections
Learn about other commands
# Custom Commands
Source: https://anomalyco-opencode.mintlify.app/cli/commands/custom
Create custom workflow commands for your team
## Overview
Custom commands let you define reusable workflows that encapsulate your team's best practices, coding standards, and common tasks. They transform complex multi-step processes into simple commands.
## Creating Commands
Define commands in `opencode.json`:
```json theme={null}
{
"command": {
"review": {
"description": "Review code changes",
"template": "Review all uncommitted changes for potential issues, suggest improvements, and check against our coding standards."
},
"test": {
"description": "Generate tests for $1",
"template": "Create comprehensive unit tests for $1. Use the testing framework configured in the project. Cover edge cases and error handling."
}
}
}
```
Then use them:
```
/review
/test src/auth.ts
```
## Command Structure
### Basic Command
Minimal command definition:
```json theme={null}
{
"command": {
"docs": {
"description": "Generate documentation",
"template": "Create comprehensive documentation for the current file."
}
}
}
```
### With Arguments
Commands can accept arguments:
```json theme={null}
{
"command": {
"refactor": {
"description": "Refactor $1 using $2 pattern",
"template": "Refactor $1 to use the $2 design pattern. Maintain all existing functionality and add tests."
}
}
}
```
Usage:
```
/refactor src/users/service.ts singleton
```
### With Specific Agent
Assign commands to specialized agents:
```json theme={null}
{
"command": {
"security-audit": {
"description": "Audit code for security issues",
"template": "Review the code for security vulnerabilities including SQL injection, XSS, authentication bypasses, and insecure dependencies.",
"agent": "security-expert"
}
}
}
```
### With Specific Model
Use a specific model for a command:
```json theme={null}
{
"command": {
"explain": {
"description": "Explain $1 in simple terms",
"template": "Explain $1 in simple, beginner-friendly terms. Use analogies and examples.",
"model": "anthropic/claude-4.5-sonnet"
}
}
}
```
### As Subtask
Execute command as a subtask (uses Task tool):
```json theme={null}
{
"command": {
"optimize": {
"description": "Optimize performance of $1",
"template": "Analyze $1 for performance bottlenecks. Profile the code, identify slow operations, and implement optimizations.",
"subtask": true
}
}
}
```
Subtasks:
* Run in isolated context
* Can use specialized agents
* Results are summarized back to main agent
* Useful for complex, independent tasks
## Template Syntax
### Positional Arguments
Use `$1`, `$2`, `$3`, etc. for individual arguments:
```json theme={null}
{
"template": "Update $1 to use $2 instead of $3"
}
```
Usage:
```
/command file.ts async/await callbacks
```
Expands to:
```
Update file.ts to use async/await instead of callbacks
```
### All Arguments
Use `$ARGUMENTS` for all arguments as a single string:
```json theme={null}
{
"template": "Research and explain: $ARGUMENTS"
}
```
Usage:
```
/command how do React hooks work internally?
```
Expands to:
```
Research and explain: how do React hooks work internally?
```
### Escaping
To use literal `$` in templates:
```json theme={null}
{
"template": "Set environment variable \$API_KEY to $1"
}
```
### Multi-line Templates
Use arrays for multi-line templates:
```json theme={null}
{
"command": {
"feature": {
"description": "Implement new feature $1",
"template": [
"Implement the feature: $1",
"",
"Follow these steps:",
"1. Create necessary files and structure",
"2. Implement core functionality",
"3. Add error handling",
"4. Write tests",
"5. Update documentation"
]
}
}
}
```
Arrays are joined with newlines.
## Real-World Examples
### Development Workflow
```json theme={null}
{
"command": {
"pr": {
"description": "Create pull request",
"template": "Create a pull request for the current branch. Write a clear title and description summarizing changes. Use conventional commit format. Run tests before creating PR."
},
"commit": {
"description": "Create conventional commit",
"template": "Review staged changes and create a conventional commit message. Format: type(scope): description. Types: feat, fix, docs, refactor, test, chore."
},
"changelog": {
"description": "Update CHANGELOG.md",
"template": "Update CHANGELOG.md with recent changes. Group by Added, Changed, Fixed, Removed. Follow Keep a Changelog format."
}
}
}
```
### Testing Commands
```json theme={null}
{
"command": {
"test-file": {
"description": "Generate tests for $1",
"template": "Create comprehensive unit tests for $1. Use Jest/Vitest. Cover: happy path, edge cases, error conditions, boundary values. Aim for >90% coverage.",
"agent": "test-expert"
},
"e2e": {
"description": "Create E2E test for $1",
"template": "Create end-to-end test for $1 using Playwright/Cypress. Test the complete user flow including: navigation, interactions, assertions, error states."
},
"fix-tests": {
"description": "Fix failing tests",
"template": "Run the test suite. For each failing test, analyze the failure, determine root cause, and fix the issue. Ensure fix doesn't break other tests."
}
}
}
```
### Code Quality
```json theme={null}
{
"command": {
"lint": {
"description": "Fix linting issues",
"template": "Run linter and fix all auto-fixable issues. For issues requiring manual fixes, make appropriate changes following project conventions."
},
"types": {
"description": "Fix TypeScript errors in $1",
"template": "Fix all TypeScript errors in $1. Add proper types, fix inference issues, resolve imports. Avoid using 'any' unless absolutely necessary."
},
"cleanup": {
"description": "Clean up code in $1",
"template": "Clean up $1: remove unused code, fix formatting, improve naming, add comments, extract magic numbers, simplify complex logic."
}
}
}
```
### Documentation
```json theme={null}
{
"command": {
"readme": {
"description": "Update README",
"template": "Update README.md with current project info. Include: overview, installation, usage, examples, API reference, contributing guidelines. Use clear headings and formatting.",
"agent": "technical-writer"
},
"api-docs": {
"description": "Document API in $1",
"template": "Generate API documentation for $1. Document all public functions/classes with JSDoc/TSDoc: description, parameters, return types, examples, edge cases."
},
"inline": {
"description": "Add inline docs to $1",
"template": "Add inline documentation to $1. Comment complex logic, explain non-obvious code, document assumptions. Keep comments concise and valuable."
}
}
}
```
### Refactoring
```json theme={null}
{
"command": {
"extract": {
"description": "Extract $1 from $2",
"template": "Extract $1 from $2 into a separate, reusable module. Create proper exports, maintain same interface, update all imports, add tests."
},
"modernize": {
"description": "Modernize $1",
"template": "Modernize $1 to use current best practices: async/await, arrow functions, destructuring, optional chaining, nullish coalescing. Maintain functionality."
},
"dry": {
"description": "Apply DRY principle to $1",
"template": "Refactor $1 to eliminate code duplication. Extract common logic into reusable functions, use composition, maintain readability."
}
}
}
```
### Database
```json theme={null}
{
"command": {
"migration": {
"description": "Create migration for $1",
"template": "Create a database migration for $1. Include: schema changes, indexes, foreign keys, data transformations. Add both up and down migrations."
},
"seed": {
"description": "Create seed data for $1",
"template": "Create realistic seed data for $1. Include various scenarios, edge cases, relationships. Use faker for generated data."
},
"query": {
"description": "Optimize query in $1",
"template": "Analyze and optimize the database query in $1. Add indexes, rewrite for efficiency, use proper joins, explain the improvements.",
"subtask": true
}
}
}
```
## Command Discovery
Users can discover commands through:
### Autocomplete
Type `/` to see all available commands:
```
/
init - Create/update AGENTS.md
review - Review code changes
test - Generate tests for $1
docs - Generate documentation
...
```
### Command Help
Commands show their descriptions in autocomplete with argument hints:
```
/refactor $1 $2 - Refactor $1 using $2 pattern
/test $1 - Generate tests for $1
```
### List Commands Programmatically
Via API:
```bash theme={null}
curl http://localhost:4096/api/command
```
## Sharing Commands
### Team Commands
Commit `opencode.json` to share commands with your team:
```bash theme={null}
git add opencode.json
git commit -m "Add custom workflow commands"
git push
```
Team members get the commands automatically.
### Global Commands
Add personal commands globally:
```json theme={null}
// ~/.config/opencode/opencode.json
{
"command": {
"note": {
"description": "Add note to $1",
"template": "Add a detailed comment note to $1 explaining: $ARGUMENTS"
}
}
}
```
Global commands work in all projects.
### Priority
When the same command name exists in multiple places:
1. **Project** - `.opencode/opencode.json` (highest priority)
2. **Project root** - `opencode.json`
3. **Global** - `~/.config/opencode/opencode.json`
4. **Built-in** - `/init`, `/review` (lowest priority)
## Command Libraries
Create reusable command libraries:
```json theme={null}
// commands/testing.json
{
"command": {
"test-file": { ... },
"test-suite": { ... },
"coverage": { ... }
}
}
```
Import in your `opencode.json`:
```json theme={null}
{
"extends": ["./commands/testing.json", "./commands/docs.json"]
}
```
## Best Practices
Write detailed templates that clearly describe expectations
Assign specialized agents to appropriate commands
Include examples in templates to guide the agent
Use concise descriptions that explain when to use the command
## Related Topics
Full command configuration reference
Create specialized agents
Learn about built-in commands
Add commands from MCP servers
# In-Session Commands
Source: https://anomalyco-opencode.mintlify.app/cli/commands/init
Commands available during an OpenCode session
## Overview
While OpenCode is running, you can execute special commands by prefixing them with `/`. These commands control the session, invoke custom workflows, and manage your environment.
## Built-in Commands
### Session Control
Change the model for the current session.
```
/model anthropic/claude-4.5-sonnet
/model openai/gpt-4o
```
The model persists for the rest of the session. Use the format `provider/model` as shown in [`opencode models`](/cli/models).
Change the agent for the current session.
```
/agent typescript-expert
/agent default
```
Switches to a different agent configuration. See [agent command](/cli/agent) for managing agents.
Undo the last assistant message and return to the previous state.
```
/undo
```
Removes the last message from history and reverts any changes. Can be used multiple times to step backwards.
Redo a previously undone message.
```
/redo
```
Reapplies messages that were undone. Only works if you haven't sent new messages after undoing.
Create a shareable link for the current session.
```
/share
```
Generates a URL (like `https://opncd.ai/s/abc123`) that others can view. See [sharing documentation](/share) for details.
Create a new session from the current point.
```
/fork
```
Creates a branch of the conversation, allowing you to explore different directions without affecting the original.
### Custom Commands
You can define custom commands that execute predefined prompts with arguments.
Built-in command to create or update `AGENTS.md` in your project.
```
/init
```
This documents your project structure and conventions for OpenCode to reference.
Built-in command to review code changes.
```
/review
/review commit
/review branch
/review pr
```
Reviews uncommitted changes by default, or specify:
* `commit` - Review the last commit
* `branch` - Review all changes in the current branch
* `pr` - Review changes in a pull request
## Defining Custom Commands
Create custom commands in `opencode.json`:
```json theme={null}
{
"command": {
"test": {
"description": "Run tests and fix failures",
"template": "Run the test suite. If any tests fail, analyze the failures and fix them. Test files are in the tests/ directory."
},
"docs": {
"description": "Generate documentation for $1",
"template": "Generate comprehensive documentation for $1. Include usage examples, API reference, and best practices.",
"agent": "documentation-writer"
},
"optimize": {
"description": "Optimize performance of $1",
"template": "Analyze $1 for performance issues. Suggest and implement optimizations. Use profiling tools if available.",
"subtask": true
}
}
}
```
### Command Configuration
Short description shown in command autocomplete
The prompt template to execute. Can include:
* `$1`, `$2`, etc. - Numbered arguments
* `$ARGUMENTS` - All arguments as a single string
Specific agent to use for this command
Specific model to use (format: `provider/model`)
Whether to execute as a subtask (uses Task tool)
### Using Arguments
Commands can accept arguments:
```json theme={null}
{
"command": {
"refactor": {
"description": "Refactor $1 to use $2 pattern",
"template": "Refactor the code in $1 to follow the $2 design pattern. Maintain existing functionality while improving structure."
}
}
}
```
Usage:
```
/refactor src/app.ts factory
```
Expands to:
```
Refactor the code in src/app.ts to follow the factory design pattern...
```
### Argument Placeholders
* **`$1`, `$2`, `$3`...** - Individual positional arguments
* **`$ARGUMENTS`** - All arguments combined into a single string
Example with `$ARGUMENTS`:
```json theme={null}
{
"command": {
"ask": {
"description": "Ask the TypeScript expert",
"template": "You are a TypeScript expert. Answer this question: $ARGUMENTS",
"agent": "typescript-expert"
}
}
}
```
Usage:
```
/ask What's the difference between type and interface?
```
## MCP Prompts
If you have [MCP servers](/mcp-servers) configured, their prompts are automatically available as commands:
```
/fetch-url https://example.com
/search-github typescript utility types
```
MCP prompts:
* Use the server's prompt name as the command
* Accept arguments as defined by the server
* Are dynamically loaded from connected servers
## Skills as Commands
[Skills](/skills) are automatically available as commands:
```
/mintlify create docs/api/users.mdx
/supabase generate migration users
```
Skills:
* Use the skill name as the command
* Inject their content as the prompt
* Can bundle templates, scripts, and references
## Command Discovery
To see available commands:
1. **Autocomplete**: Type `/` in the TUI to see suggestions
2. **Description**: Commands show their description in autocomplete
3. **Help**: Commands with arguments show hints (e.g., `$1`, `$2`)
## Command Priority
When multiple sources define a command with the same name:
1. **Built-in commands** (e.g., `/init`, `/review`)
2. **User-defined commands** (in `opencode.json`)
3. **MCP prompts**
4. **Skills**
Higher priority commands override lower ones.
## Examples
### Development Workflow
```json theme={null}
{
"command": {
"commit": {
"description": "Create a conventional commit",
"template": "Review the staged changes and create a conventional commit message (type(scope): description). Stage files if needed."
},
"pr": {
"description": "Create a pull request",
"template": "Create a pull request for the current branch. Write a clear title and description summarizing all changes. Use gh CLI."
},
"lint": {
"description": "Fix linting issues",
"template": "Run the linter and fix all issues automatically. For issues that can't be auto-fixed, make manual corrections."
}
}
}
```
### Testing Workflows
```json theme={null}
{
"command": {
"test-file": {
"description": "Generate tests for $1",
"template": "Create comprehensive unit tests for $1. Use the testing framework already configured in the project. Include edge cases and error scenarios.",
"agent": "test-specialist"
},
"coverage": {
"description": "Improve test coverage",
"template": "Run test coverage analysis. Identify untested code paths and create tests to improve coverage. Focus on critical business logic."
}
}
}
```
### Documentation
```json theme={null}
{
"command": {
"readme": {
"description": "Update README.md",
"template": "Update README.md with current project information. Include: overview, installation, usage, examples, and contribution guidelines."
},
"api-docs": {
"description": "Generate API documentation for $1",
"template": "Generate API documentation for $1. Include all public methods, parameters, return types, and usage examples. Use JSDoc/TSDoc format.",
"agent": "documentation-writer"
}
}
}
```
### Refactoring
```json theme={null}
{
"command": {
"extract": {
"description": "Extract $1 from $2",
"template": "Extract $1 from $2 into a separate, reusable module. Maintain the same interface and functionality. Update imports."
},
"modernize": {
"description": "Modernize $1",
"template": "Modernize $1 using current best practices. Update syntax, patterns, and dependencies. Ensure backwards compatibility."
}
}
}
```
## Running Commands Programmatically
Use the `run` command with `--command` flag:
```bash theme={null}
opencode run --command test "src/app.test.ts"
```
This executes the `test` command with the given arguments without opening the TUI.
## Best Practices
Write concise descriptions that explain when to use each command
Be explicit in templates about what the agent should do
Make commands flexible with argument placeholders
Assign specialized agents to appropriate commands
## Related Topics
Learn more about command configuration
Create specialized agents
Add MCP prompts as commands
Install and use skills
# /share - Share Sessions
Source: https://anomalyco-opencode.mintlify.app/cli/commands/share
Create shareable links to your OpenCode conversations
## Overview
The `/share` command creates a public URL for your OpenCode session that others can view. This allows you to:
* Share conversations with teammates
* Get help by showing your session
* Document problem-solving workflows
* Create tutorials and examples
* Collaborate on code reviews
## Usage
```
/share
```
The command generates a shareable URL:
```
https://opncd.ai/s/abc123def456
```
## What Gets Shared
Shared sessions include:
* **Full conversation** - All messages and responses
* **Tool executions** - Commands run and outputs
* **File contexts** - Code and files referenced
* **Metadata** - Model, agent, timestamps
Shared sessions are **public**. Anyone with the link can view the content. Don't share sensitive information.
## Viewing Shared Sessions
Recipients can:
1. **Open in browser** - View the conversation
2. **Copy messages** - Extract code or content
3. **See tool outputs** - Understand what was executed
4. **Import session** - Load into their OpenCode
### Import a Shared Session
Anyone can import a shared session:
```bash theme={null}
opencode import https://opncd.ai/s/abc123def456
```
This creates a local copy they can continue working with.
## Automatic Sharing
Enable automatic sharing for all sessions:
### Via Environment Variable
```bash theme={null}
export OPENCODE_AUTO_SHARE=true
opencode
```
### Via Configuration
In `opencode.json`:
```json theme={null}
{
"share": "auto"
}
```
### Via CLI Flag
With the `run` command:
```bash theme={null}
opencode run --share "Fix the bug in app.ts"
```
With auto-share enabled, every session is shared automatically and the URL is displayed:
```
~ https://opncd.ai/s/abc123def456
```
## Disabling Sharing
To prevent sharing in your organization or workspace:
### Via Configuration
```json theme={null}
{
"share": "disabled"
}
```
With sharing disabled:
```
User: /share
Assistant: ! Sharing is disabled in this workspace
```
### Per-Session Control
Sharing can be controlled per session:
```json theme={null}
{
"share": "manual" // Default, requires /share command
}
```
Options:
* `"auto"` - Share all sessions automatically
* `"manual"` - Share only when `/share` is used (default)
* `"disabled"` - Prevent all sharing
## Privacy Considerations
Shared sessions are **publicly accessible**. Review content before sharing.
### What NOT to Share
Avoid sharing sessions containing:
* API keys or credentials
* Private code or intellectual property
* Personal information (PII)
* Internal URLs or infrastructure details
* Confidential business logic
* Security vulnerabilities (before they're fixed)
### Redacting Sensitive Data
Before sharing:
1. Review the full conversation
2. Undo messages with sensitive content
3. Fork the session and remove sensitive parts
4. Use environment variables instead of hardcoded secrets
### Revoking Shared Sessions
Currently, shared sessions cannot be deleted after creation. Be cautious about what you share.
Future versions may support revoking or editing shared sessions.
## Use Cases
### Getting Help
Share your session when asking for help:
```
User: /share
User: [copies link]
# In Slack/Discord:
"I'm stuck on this error. Here's my OpenCode session: https://opncd.ai/s/..."
```
### Code Review
Share a session showing your problem-solving process:
```
User: /share
User: "Created PR #123. Here's how we solved it: https://opncd.ai/s/..."
```
### Documentation
Create tutorials by sharing instructional sessions:
```
User: /share
User: "Tutorial on setting up authentication: https://opncd.ai/s/..."
```
### Bug Reports
Include session links in bug reports:
```markdown theme={null}
## Bug Report
**Description**: API endpoint returns 500 error
**Session**: https://opncd.ai/s/abc123def456
OpenCode helped me identify the issue is in the middleware...
```
### Team Collaboration
Share sessions for pair programming:
```
User: /share
# Team member imports and continues:
opencode import https://opncd.ai/s/abc123def456
```
## Programmatic Sharing
Share sessions via the API:
```typescript theme={null}
import { createOpencodeClient } from '@opencode-ai/sdk/v2'
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096'
})
const result = await client.session.share({
sessionID: 'session-id'
})
console.log(result.data.share.url)
// https://opncd.ai/s/abc123def456
```
## Share URL Format
Share URLs follow this pattern:
```
https://opncd.ai/s/
│ │
│ └─ Unique session identifier
└───── Share path
```
The slug is:
* Unique per session
* URL-safe (alphanumeric + hyphens)
* Permanent (doesn't expire)
## Import Command
Import shared sessions locally:
```bash theme={null}
# From URL
opencode import https://opncd.ai/s/abc123def456
# Or just the slug
opencode import abc123def456
```
This:
1. Downloads the session data
2. Creates a local session
3. Preserves all messages and context
4. Allows you to continue the conversation
### Import Options
You can also import from files:
```bash theme={null}
# From local JSON file
opencode import session.json
# From another session export
opencode export > backup.json
opencode import backup.json
```
## Export vs Share
| Feature | /share | export |
| ------------------ | ------ | ------ |
| Creates public URL | ✓ | ✗ |
| Exports to file | ✗ | ✓ |
| Requires internet | ✓ | ✗ |
| Public access | ✓ | ✗ |
| Local backup | ✗ | ✓ |
Use `/share` for collaboration, `export` for backups.
## Troubleshooting
### Sharing Failed
**Problem**: Cannot create share link
**Solutions**:
* Check internet connectivity
* Verify sharing isn't disabled in config
* Ensure OpenCode can reach `opncd.ai`
* Check firewall/proxy settings
### Sharing Disabled
**Problem**: `Sharing is disabled` error
**Solutions**:
* Check `opencode.json` for `"share": "disabled"`
* Remove or change to `"manual"` or `"auto"`
* Contact workspace admin if in enterprise
### Cannot Access Shared Link
**Problem**: Link shows 404 or error
**Solutions**:
* Verify the URL is complete and correct
* Check internet connectivity
* Try opening in incognito/private mode
* The share service may be temporarily down
### Import Failed
**Problem**: Cannot import shared session
**Solutions**:
* Verify the URL is valid
* Check internet connectivity
* Ensure you have disk space
* Try importing to a different directory
## Enterprise Considerations
For organizations:
### Disable Public Sharing
Completely disable sharing:
```json theme={null}
{
"share": "disabled"
}
```
### Self-Hosted Sharing
Set up internal sharing service:
```json theme={null}
{
"share": {
"url": "https://opencode-share.internal.company.com",
"enabled": true
}
}
```
### Audit Trail
Log all share events:
```json theme={null}
{
"audit": {
"share": true
}
}
```
## Related Topics
Import shared sessions
Export sessions to files
Managing sessions
Configure sharing behavior
# Undo and Redo
Source: https://anomalyco-opencode.mintlify.app/cli/commands/undo-redo
Revert and reapply changes during an OpenCode session
## Overview
OpenCode provides `/undo` and `/redo` commands to navigate through your conversation history and revert unwanted changes. This is essential when:
* The assistant made an incorrect change
* You want to try a different approach
* You need to backtrack to an earlier state
* You made a mistake in your request
## /undo Command
Revert the last assistant message and any changes it made.
### Usage
```
/undo
```
### What Gets Undone
When you undo:
1. **Message removed** - The last assistant message is removed from history
2. **File changes reverted** - Any file modifications are undone
3. **Tool executions canceled** - Effects of tool calls are reversed
4. **State restored** - Session returns to the previous state
### Multiple Undo
You can undo multiple times to go further back:
```
/undo # Undo last message
/undo # Undo previous message
/undo # Undo message before that
```
Each `/undo` steps back one assistant message.
### Example
```
User: Fix the typo in README.md
Assistant:
$ Edit README.md
[Changes made]
User: /undo # Reverts the edit
User: Actually, fix all typos in the docs/ directory
```
## /redo Command
Reapply a previously undone message.
### Usage
```
/redo
```
### When Redo Works
Redo is available when:
* You've used `/undo` at least once
* You haven't sent new messages since undoing
* The undone messages are still in history
### Multiple Redo
Redo multiple times to reapply several undone messages:
```
/redo # Redo first undone message
/redo # Redo second undone message
/redo # Redo third undone message
```
### Redo Limits
You cannot redo after:
* Sending a new message
* Closing and reopening the session
* Using `/fork` to branch the conversation
### Example
```
User: Refactor the login function
Assistant:
$ Edit auth.ts
[Makes changes]
User: /undo # Changed my mind
User: /redo # Actually, those changes were good
```
## How It Works
### Message History
OpenCode maintains a complete history of messages:
```
1. User: "Create a new component"
2. Assistant: [creates component]
3. User: "Add prop validation"
4. Assistant: [adds validation]
5. User: "/undo"
→ State returns to after message #2
6. User: "Add TypeScript types instead"
7. Assistant: [adds types]
```
### File State
File changes are tracked:
* **Before undo**: File has changes from assistant
* **After undo**: File restored to previous state
* **After redo**: Changes reapplied
### Tool Effects
Some tool effects can't be fully undone:
* **Bash commands**: Side effects persist (use with caution)
* **API calls**: External changes not reverted
* **Deleted files**: Moved to trash, can be recovered
## Use Cases
### Correcting Mistakes
```
User: Update the API endpoint to /api/v2/users
Assistant: [Makes changes]
User: /undo # Wrong endpoint
User: Update the API endpoint to /api/v2/customers
```
### Exploring Alternatives
```
User: Implement authentication with JWT
Assistant: [Implements JWT auth]
User: /undo
User: Implement authentication with sessions instead
Assistant: [Implements session auth]
User: /undo
User: /redo # JWT was better
```
### Recovering from Errors
```
User: Delete all unused imports
Assistant: [Accidentally deletes important imports]
User: /undo # Restore imports
User: Remove only unused imports from utils.ts
```
### Iterative Refinement
```
User: Make the button larger
Assistant: [Increases size to 60px]
User: /undo
User: Make the button slightly larger
Assistant: [Increases size to 40px]
User: Perfect!
```
## Best Practices
Undo right away if you spot an issue
Check what was changed before deciding to undo
After undoing, give clearer instructions
Fork instead of undo/redo for major changes
## Limitations
### Cannot Undo User Messages
You can only undo assistant messages, not your own:
```
User: Wrong command here
# Cannot undo this - just send a correction
```
### External Side Effects
Some actions can't be fully undone:
```
# These have side effects:
Assistant runs: npm install package
Assistant calls: curl -X POST https://api.com/create
Assistant executes: git push origin main
# /undo won't reverse these
```
Be cautious with destructive operations. They may not be fully reversible.
### Context Compaction
After [context compaction](/config#compaction), some undo history may be lost:
* Recent messages remain undoable
* Very old messages may not be reversible
* Snapshots preserve key states
## Alternatives to Undo/Redo
### Fork Instead
Use `/fork` to branch without losing history:
```
/fork # Create new branch
# Try experimental changes
# Original conversation preserved
```
### Git for Recovery
Leverage git for file recovery:
```bash theme={null}
# Check what was changed
git diff
# Restore specific file
git checkout -- file.ts
# Undo all uncommitted changes
git reset --hard
```
### Snapshots
OpenCode creates automatic snapshots:
```bash theme={null}
# View snapshots
ls ~/.local/share/opencode/snapshots/
# Restore from snapshot
opencode import snapshot-.json
```
## Technical Details
### Storage
Undo/redo state is maintained:
* In-memory for the current session
* In the session database
* Not affected by closing/reopening TUI (when using same session)
### Session Continuity
When continuing a session:
```bash theme={null}
# Continue last session
opencode --continue
# Undo still works for recent messages
/undo
```
### Fork Impact
Forking clears redo stack:
```
/undo # Undo a message
/fork # Create fork
/redo # No longer available
```
## Troubleshooting
### Undo Not Working
**Problem**: `/undo` doesn't revert changes
**Solutions**:
* Check if there's anything to undo
* Verify files aren't read-only
* Ensure session database is writable
* Try undoing again (may need multiple undos)
### Redo Not Available
**Problem**: `/redo` command fails
**Solutions**:
* You may have sent a new message (clears redo stack)
* Session was forked (clears redo)
* All undone messages already redone
### Changes Persist
**Problem**: Some changes remain after undo
**Solutions**:
* External API calls can't be undone
* Bash commands may have side effects
* File system operations outside project scope
* Use `git reset` for file recovery
## Keyboard Shortcuts
In the TUI, you can also use:
* **Ctrl+Z** (or Cmd+Z on macOS): Undo
* **Ctrl+Shift+Z** (or Cmd+Shift+Z): Redo
See [keybinds documentation](/keybinds) for customization.
## Related Topics
Branch conversations
Managing sessions
Automatic state snapshots
Using git with OpenCode
# models
Source: https://anomalyco-opencode.mintlify.app/cli/models
List all available models from configured providers
## Overview
The `models` command displays all AI models available from your configured providers. Use this to:
* Discover available models
* Find exact model names for configuration
* Filter models by provider
* View model metadata and pricing
## Usage
```bash theme={null}
opencode models [provider]
```
Models are displayed in the format `provider/model`, which is the format used in [configuration files](/config) and the `--model` flag.
## Options
Optional provider ID to filter models. Only shows models from this provider.
Refresh the models cache from models.dev. Use this when new models are released.
Show detailed model information including pricing, context windows, and capabilities.
## Examples
### List All Models
Show models from all configured providers:
```bash theme={null}
opencode models
```
Output:
```
anthropic/claude-4.5-sonnet
anthropic/claude-4.0-opus
anthropic/claude-3.5-sonnet
anthropic/claude-3-haiku
openai/gpt-4o
openai/gpt-4-turbo
openai/gpt-3.5-turbo
opencode/claude-max
opencode/gpt-plus
...
```
### Filter by Provider
Show only Anthropic models:
```bash theme={null}
opencode models anthropic
```
Output:
```
anthropic/claude-4.5-sonnet
anthropic/claude-4.0-opus
anthropic/claude-3.5-sonnet
anthropic/claude-3-haiku
```
### Show Detailed Information
View model metadata:
```bash theme={null}
opencode models anthropic --verbose
```
Output:
```json theme={null}
anthropic/claude-4.5-sonnet
{
"id": "claude-4.5-sonnet",
"name": "Claude 4.5 Sonnet",
"contextWindow": 200000,
"maxTokens": 8192,
"pricing": {
"input": 0.003,
"output": 0.015
},
"capabilities": ["vision", "function-calling", "streaming"]
}
```
### Refresh Model Cache
Update the cached model list from models.dev:
```bash theme={null}
opencode models --refresh
```
This fetches the latest model information and is useful when:
* New models are released
* Model pricing changes
* You add a new provider
## Model Format
All models follow the `provider/model` format:
```
provider/model
│ │
│ └─ Model ID within provider
└─────────Provider ID from models.dev
```
Examples:
* `anthropic/claude-4.5-sonnet`
* `openai/gpt-4o`
* `google/gemini-2.0-flash-exp`
* `opencode/claude-max`
## Using Model Names
### In Configuration
Set default model in `opencode.json`:
```json theme={null}
{
"model": "anthropic/claude-4.5-sonnet"
}
```
### With CLI Flags
Specify model when starting OpenCode:
```bash theme={null}
opencode --model anthropic/claude-4.5-sonnet
```
Or with the run command:
```bash theme={null}
opencode run --model openai/gpt-4o "Explain recursion"
```
### Model Switching
Switch models mid-session using the `/model` command:
```
/model anthropic/claude-4.5-sonnet
```
## Provider Order
Models are displayed in a specific order:
1. **OpenCode providers** (e.g., `opencode/claude-max`) - shown first
2. **Other providers** - alphabetically by provider ID
Within each provider, models are alphabetically sorted.
## Model Information
With `--verbose`, you can see:
* **Context window**: Maximum input tokens
* **Max tokens**: Maximum output tokens
* **Pricing**: Input and output costs per 1K tokens
* **Capabilities**: Features like vision, function calling, streaming
* **Modalities**: Supported input/output types
## Understanding Provider IDs
Common provider IDs:
| Provider | ID | Authentication |
| -------------- | ---------------- | --------------------------- |
| OpenCode | `opencode` | OpenCode API key |
| Anthropic | `anthropic` | Claude API key |
| OpenAI | `openai` | OpenAI API key |
| Google | `google` | Google AI API key |
| GitHub Copilot | `github-copilot` | GitHub Copilot subscription |
| OpenRouter | `openrouter` | OpenRouter API key |
| Vercel | `vercel` | Vercel API key |
| Amazon Bedrock | `amazon-bedrock` | AWS credentials |
## Models.dev
OpenCode uses [models.dev](https://models.dev) as the central registry for AI models. This provides:
* Unified model metadata
* Up-to-date pricing information
* Capability and feature information
* Support for 50+ providers
### Cache Management
Model information is cached locally for performance. The cache:
* Updates automatically on first run each day
* Can be manually refreshed with `--refresh`
* Is stored in `~/.local/share/opencode/models.json`
## Provider Configuration
Before using models from a provider, you must authenticate:
```bash theme={null}
# Add authentication for a provider
opencode auth login
# Select provider and enter credentials
# Then list models from that provider
opencode models
```
See the [auth command](/cli/auth) for authentication details.
## Filtering and Searching
While the command doesn't have built-in search, you can use standard Unix tools:
### Search by Name
```bash theme={null}
opencode models | grep claude
opencode models | grep -i gpt
```
### Count Models
```bash theme={null}
opencode models | wc -l
opencode models anthropic | wc -l
```
### Show Specific Range
```bash theme={null}
opencode models | head -10
opencode models | tail -20
```
## Troubleshooting
### No Models Shown
**Problem**: Command returns empty or shows no models
**Solutions**:
* Run `opencode models --refresh` to update cache
* Check internet connectivity
* Verify you've authenticated with `opencode auth login`
* Check provider is enabled in config
### Provider Not Found
**Problem**: `Provider not found: `
**Solutions**:
* Check provider ID spelling
* Run `opencode models` without arguments to see available providers
* Ensure provider is configured in `opencode.json`
* Authenticate with the provider: `opencode auth login`
### Models Out of Date
**Problem**: New models missing from list
**Solutions**:
* Run `opencode models --refresh`
* Check [models.dev](https://models.dev) for latest models
* Verify OpenCode is up to date: `opencode upgrade`
## Environment Variables
The models command respects these environment variables:
| Variable | Description |
| ------------------------------------- | ------------------------------------------- |
| `OPENCODE_DISABLE_MODELS_FETCH` | Disable fetching models from remote sources |
| `OPENCODE_MODELS_URL` | Custom URL for fetching model configuration |
| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | Show experimental/beta models |
## Related Commands
Authenticate with model providers
Configure default models
Learn about available providers
Using models in the interface
# CLI Overview
Source: https://anomalyco-opencode.mintlify.app/cli/overview
Complete reference for OpenCode command-line interface
The OpenCode CLI provides both interactive and non-interactive modes for working with AI-powered coding assistance.
## Getting Started
By default, OpenCode starts the [TUI](/tui) when run without arguments:
```bash theme={null}
opencode
```
You can also run commands programmatically:
```bash theme={null}
opencode run "Explain how closures work in JavaScript"
```
## Available Commands
Start a headless OpenCode server for API access
Start server with web interface
Attach terminal to running backend server
List all available models from providers
Manage provider credentials and login
Manage custom agents
Update to latest or specific version
## TUI Mode
Start the OpenCode terminal user interface:
```bash theme={null}
opencode [project]
```
### Options
Continue the last session. Short form: `-c`
Session ID to continue. Short form: `-s`
Fork the session when continuing (use with `--continue` or `--session`)
Initial prompt to use
Model to use in the form of `provider/model`. Short form: `-m`
Agent to use
Port to listen on
Hostname to listen on
### Examples
```bash theme={null}
# Start in current directory
opencode
# Continue last session
opencode --continue
# Use specific model
opencode --model anthropic/claude-4.5-sonnet
# Fork previous session
opencode --session abc123 --fork
```
## Run Mode
Run OpenCode non-interactively with a direct message:
```bash theme={null}
opencode run [message..]
```
See the full [run command documentation](/cli/commands/init) for details.
## Global Flags
These flags work with any command:
Display help information. Short form: `-h`
Print version number. Short form: `-v`
Print logs to stderr
Set log level: `DEBUG`, `INFO`, `WARN`, or `ERROR`
## Environment Variables
OpenCode can be configured using environment variables:
| Variable | Type | Description |
| --------------------------------- | ------- | -------------------------------------------------- |
| `OPENCODE_AUTO_SHARE` | boolean | Automatically share sessions |
| `OPENCODE_CONFIG` | string | Path to config file |
| `OPENCODE_CONFIG_DIR` | string | Path to config directory |
| `OPENCODE_CONFIG_CONTENT` | string | Inline JSON config content |
| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | Disable automatic update checks |
| `OPENCODE_DISABLE_PRUNE` | boolean | Disable pruning of old data |
| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | Disable automatic terminal title updates |
| `OPENCODE_SERVER_PASSWORD` | string | Enable basic auth for serve/web |
| `OPENCODE_SERVER_USERNAME` | string | Override basic auth username (default: `opencode`) |
| `OPENCODE_CLIENT` | string | Client identifier (defaults to `cli`) |
For a complete list of environment variables including experimental features, see the [source documentation](https://github.com/anomalyco/opencode).
## Next Steps
Learn about commands you can use during a session
Customize OpenCode to your needs
# serve
Source: https://anomalyco-opencode.mintlify.app/cli/serve
Start a headless OpenCode server for API access
## Overview
The `serve` command starts a headless HTTP server that provides API access to OpenCode functionality without the TUI interface. This is ideal for:
* Programmatic API access
* Running on remote servers
* Integrating with other tools
* Avoiding MCP server cold boot times
## Usage
```bash theme={null}
opencode serve
```
The server starts on `127.0.0.1:0` (random port) by default. Use flags to customize the network settings.
## Options
Port to listen on. Default is `0` (random available port).
Hostname to listen on. Use `0.0.0.0` to allow external connections.
Enable mDNS service discovery. When enabled, defaults hostname to `0.0.0.0`.
Custom domain name for mDNS service.
Additional browser origins to allow for CORS. Can be specified multiple times.
## Examples
### Basic Server
Start a local server on a random port:
```bash theme={null}
opencode serve
```
### Fixed Port
Start server on port 4096:
```bash theme={null}
opencode serve --port 4096
```
### Network-Accessible Server
Allow connections from other machines:
```bash theme={null}
opencode serve --port 4096 --hostname 0.0.0.0
```
### With mDNS Discovery
Enable service discovery on local network:
```bash theme={null}
opencode serve --mdns
```
### Custom CORS Origins
Allow specific origins for web access:
```bash theme={null}
opencode serve --cors https://example.com --cors https://app.example.com
```
## Authentication
By default, the server runs without authentication. Always set `OPENCODE_SERVER_PASSWORD` in production environments.
Enable HTTP basic authentication by setting environment variables:
```bash theme={null}
export OPENCODE_SERVER_PASSWORD="your-secure-password"
opencode serve
```
Optionally customize the username (defaults to `opencode`):
```bash theme={null}
export OPENCODE_SERVER_USERNAME="admin"
export OPENCODE_SERVER_PASSWORD="your-secure-password"
opencode serve
```
## Using with Run Command
You can attach to a running `serve` instance to avoid MCP server cold boot times:
```bash theme={null}
# Terminal 1: Start the server
opencode serve --port 4096
# Terminal 2: Run commands that attach to it
opencode run --attach http://localhost:4096 "Explain async/await"
opencode run --attach http://localhost:4096 "Fix the bug in app.ts"
```
## API Access
The server exposes a full HTTP API. See the [Server API documentation](/server) for details on available endpoints.
Basic example using the JavaScript SDK:
```javascript theme={null}
import { createOpencodeClient } from '@opencode-ai/sdk/v2'
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
auth: {
username: 'opencode',
password: process.env.OPENCODE_SERVER_PASSWORD
}
})
// Create a session and send a message
const session = await client.session.create({})
await client.session.prompt({
sessionID: session.data.id,
parts: [{ type: 'text', text: 'Hello!' }]
})
```
## Configuration
You can set default server options in your global configuration file (`~/.config/opencode/config.json`):
```json theme={null}
{
"server": {
"port": 4096,
"hostname": "127.0.0.1",
"mdns": false,
"mdnsDomain": "opencode.local",
"cors": ["https://example.com"]
}
}
```
Command-line flags always override configuration file settings.
## Related Commands
Start server with web interface
Attach TUI to running server
Run commands non-interactively
View full HTTP API documentation
# upgrade
Source: https://anomalyco-opencode.mintlify.app/cli/upgrade
Update OpenCode to latest or specific version
## Overview
The `upgrade` command updates OpenCode to the latest version or a specific target version. It automatically detects your installation method and uses the appropriate upgrade mechanism.
## Usage
```bash theme={null}
opencode upgrade [target]
```
### Upgrade to Latest
```bash theme={null}
opencode upgrade
```
### Upgrade to Specific Version
```bash theme={null}
opencode upgrade v0.1.48
# or
opencode upgrade 0.1.48
```
## Options
Version to upgrade to (e.g., `0.1.48` or `v0.1.48`). If omitted, upgrades to the latest version.
Installation method to use. Short form: `-m`
Supported methods:
* `curl` - Shell script installation
* `npm` - Node Package Manager
* `pnpm` - PNPM package manager
* `bun` - Bun package manager
* `brew` - Homebrew (macOS/Linux)
* `choco` - Chocolatey (Windows)
* `scoop` - Scoop (Windows)
## Installation Methods
### Automatic Detection
OpenCode automatically detects how it was installed:
```bash theme={null}
opencode upgrade
```
Output:
```
Using method: npm
From 0.1.47 → 0.1.48
Upgrading...
✓ Upgrade complete
```
### Manual Method
Override automatic detection:
```bash theme={null}
opencode upgrade --method brew
```
## Examples
### Update to Latest
```bash theme={null}
$ opencode upgrade
___ ___ _
/ _ \ _ __ ___ _ __ / ___|___ __| | ___
| | | | '_ \ / _ \ '_ \\ | / _ \ / _` |/ _ \
| |_| | |_) | __/ | | | |_| (_) | (_| | __/
\___/| .__/ \___|_| |_|\____\___/ \__,_|\___|
|_|
◇ Upgrade
◇ Using method: npm
◇ From 0.1.47 → 0.1.48
◇ Upgrading...
✓ Upgrade complete
✓ Done
```
### Specific Version
```bash theme={null}
opencode upgrade v0.1.45
```
### Force npm Method
```bash theme={null}
opencode upgrade --method npm
```
### Downgrade
You can also downgrade to an earlier version:
```bash theme={null}
opencode upgrade v0.1.40
```
## Installation Method Details
### curl (Shell Script)
For installations via the shell script:
```bash theme={null}
curl -fsSL https://opencode.ai/install.sh | sh
```
Upgrade:
```bash theme={null}
opencode upgrade --method curl
```
This re-downloads and installs the latest version.
### npm/pnpm/bun
For Node.js package managers:
```bash theme={null}
npm install -g opencode
pnpm install -g opencode
bun install -g opencode
```
Upgrade:
```bash theme={null}
opencode upgrade --method npm
# or
opencode upgrade --method pnpm
# or
opencode upgrade --method bun
```
This runs the equivalent of `npm update -g opencode`.
### brew (Homebrew)
For macOS and Linux Homebrew installations:
```bash theme={null}
brew install opencode
```
Upgrade:
```bash theme={null}
opencode upgrade --method brew
```
Runs `brew upgrade opencode`.
### choco (Chocolatey)
For Windows Chocolatey:
```bash theme={null}
choco install opencode
```
Upgrade:
```bash theme={null}
opencode upgrade --method choco
```
Chocolatey requires an elevated (Administrator) command prompt for upgrades.
### scoop (Scoop)
For Windows Scoop:
```bash theme={null}
scoop install opencode
```
Upgrade:
```bash theme={null}
opencode upgrade --method scoop
```
## Version Detection
### Check Current Version
```bash theme={null}
opencode --version
```
Or:
```bash theme={null}
opencode -v
```
### Already Up to Date
If you're already on the target version:
```
◇ opencode upgrade skipped: 0.1.48 is already installed
✓ Done
```
## Troubleshooting
### Unknown Installation Method
**Problem**: `opencode is installed to /path/to/opencode and may be managed by a package manager`
**Solutions**:
* Specify the method explicitly: `opencode upgrade --method npm`
* Uninstall and reinstall using a supported method
* Use your package manager directly
**Prompt to install anyway**:
The command will ask if you want to proceed:
```
◆ Install anyways?
│ ● Yes
│ No
└
```
Select "Yes" to install using the default method (curl).
### Permission Denied
**Problem**: Permission errors during upgrade
**Solutions**:
**macOS/Linux**:
```bash theme={null}
sudo opencode upgrade
```
**Windows (Chocolatey)**:
Run Command Prompt or PowerShell as Administrator:
```bash theme={null}
opencode upgrade --method choco
```
### Network Errors
**Problem**: Cannot download new version
**Solutions**:
* Check internet connectivity
* Verify firewall allows downloads
* Try again later (registry may be temporarily unavailable)
* Check proxy settings if behind corporate firewall
### Upgrade Failed
**Problem**: Generic upgrade failure
**Solutions**:
* Check stderr output for specific error
* Try a different installation method
* Manually uninstall and reinstall
* Report the issue on GitHub
### Cannot Find Version
**Problem**: Specified version doesn't exist
**Solutions**:
* Check available versions on [GitHub Releases](https://github.com/anomalyco/opencode/releases)
* Verify version format (use `v0.1.48` or `0.1.48`)
* Omit version to get latest: `opencode upgrade`
## Automatic Updates
By default, OpenCode checks for updates and notifies you:
```
! A new version (0.1.48) is available. Run `opencode upgrade` to update.
```
### Disable Auto-Update Checks
Set environment variable:
```bash theme={null}
export OPENCODE_DISABLE_AUTOUPDATE=true
```
Or in `opencode.json`:
```json theme={null}
{
"disableAutoUpdate": true
}
```
## Upgrade Strategies
### Stable Releases
For production use:
```bash theme={null}
# Stay on latest stable
opencode upgrade
```
### Specific Versions
For consistency across team:
```bash theme={null}
# Pin to specific version
opencode upgrade v0.1.48
```
Document the version in your project:
```json theme={null}
// package.json or similar
{
"opencode": "^0.1.48"
}
```
### Testing New Releases
Test pre-release versions:
```bash theme={null}
# Try beta/rc versions
opencode upgrade v0.2.0-beta.1
```
Pre-release versions may have bugs. Don't use in production.
## Post-Upgrade
After upgrading:
1. **Verify version**:
```bash theme={null}
opencode --version
```
2. **Check release notes**:
Visit [GitHub Releases](https://github.com/anomalyco/opencode/releases) for changelog
3. **Test functionality**:
```bash theme={null}
opencode --help
```
4. **Review breaking changes**:
Major version upgrades may have breaking changes
## Manual Installation
If automatic upgrade fails, manually install:
### Shell Script
```bash theme={null}
curl -fsSL https://opencode.ai/install.sh | sh
```
### npm
```bash theme={null}
npm install -g opencode-ai@latest
```
### Homebrew
```bash theme={null}
brew upgrade opencode
```
### From Source
```bash theme={null}
git clone https://github.com/anomalyco/opencode.git
cd opencode
bun install
bun run build
bun link
```
## Related Topics
Initial installation guide
View version changelogs
Remove OpenCode completely
Resolve common issues
# web
Source: https://anomalyco-opencode.mintlify.app/cli/web
Start OpenCode server with web interface
## Overview
The `web` command starts an HTTP server and automatically opens a web browser to access OpenCode through a web interface. This provides:
* Browser-based UI for OpenCode
* Access from any device on your network
* Shareable link for team collaboration
* Same functionality as the TUI
## Usage
```bash theme={null}
opencode web
```
The command will:
1. Start the OpenCode server
2. Display local and network access URLs
3. Automatically open your default browser
## Options
Port to listen on. Default is `0` (random available port).
Hostname to listen on. Use `0.0.0.0` to allow external connections.
Enable mDNS service discovery. When enabled, defaults hostname to `0.0.0.0`.
Custom domain name for mDNS service.
Additional browser origins to allow for CORS. Can be specified multiple times.
## Examples
### Local Access Only
Start web interface on localhost:
```bash theme={null}
opencode web
```
Output:
```
Local access: http://localhost:3456
```
### Network-Wide Access
Allow connections from other devices on your network:
```bash theme={null}
opencode web --hostname 0.0.0.0 --port 4096
```
Output:
```
Local access: http://localhost:4096
Network access: http://192.168.1.100:4096
```
Share the network URL with teammates to collaborate.
### With mDNS Discovery
Make the server discoverable via mDNS:
```bash theme={null}
opencode web --mdns
```
Output:
```
Local access: http://localhost:3456
Network access: http://192.168.1.100:3456
mDNS: opencode.local:3456
```
### Custom Domain and Port
```bash theme={null}
opencode web --mdns --mdns-domain myproject.local --port 8080
```
## Authentication
When exposing the web interface to your network, always enable authentication to prevent unauthorized access.
Enable HTTP basic authentication:
```bash theme={null}
export OPENCODE_SERVER_PASSWORD="your-secure-password"
opencode web --hostname 0.0.0.0
```
The browser will prompt for credentials:
* **Username**: `opencode` (or set `OPENCODE_SERVER_USERNAME`)
* **Password**: Value of `OPENCODE_SERVER_PASSWORD`
## Network Access Patterns
When you start with `--hostname 0.0.0.0`, OpenCode displays multiple access URLs:
### Local Access
Use `http://localhost:` from the same machine.
### Network Access
OpenCode automatically detects your local network IPs (excluding Docker bridges and IPv6). Share these URLs with others on your network:
```
Network access: http://192.168.1.100:4096
Network access: http://10.0.0.50:4096
```
### mDNS Access
If mDNS is enabled, use the friendly domain name:
```
mDNS: opencode.local:4096
```
## Mobile and Remote Access
The web interface works on mobile devices:
1. Start the server with network access:
```bash theme={null}
opencode web --hostname 0.0.0.0 --port 4096
```
2. On your mobile device, navigate to the network URL:
```
http://192.168.1.100:4096
```
3. Add to home screen for app-like experience
## Configuration
Set default web server options in your global configuration (`~/.config/opencode/config.json`):
```json theme={null}
{
"server": {
"port": 4096,
"hostname": "0.0.0.0",
"mdns": true,
"mdnsDomain": "myproject.local",
"cors": []
}
}
```
## Differences from Serve
The `web` command is similar to [`serve`](/cli/serve) but:
* Automatically opens a browser
* Shows formatted access URLs with visual styling
* Displays both local and network addresses when using `0.0.0.0`
* Filters out Docker bridge networks for cleaner output
Both commands expose the same HTTP API.
## Security Considerations
Never expose an unauthenticated server to the internet. Use authentication and firewall rules.
* **Always** set `OPENCODE_SERVER_PASSWORD` for network access
* Use firewall rules to restrict access to trusted networks
* Consider using a reverse proxy with TLS for production
* Don't use Docker bridge IPs (filtered automatically)
## Troubleshooting
### Browser Doesn't Open
The command may fail to open the browser automatically in some environments (SSH sessions, headless servers, etc.). Simply copy the URL from the output and paste it into your browser.
### Can't Connect from Other Devices
1. Verify you used `--hostname 0.0.0.0`
2. Check firewall rules allow the port
3. Ensure devices are on the same network
4. Try the IP address instead of `localhost`
### mDNS Not Working
1. Ensure mDNS/Bonjour is installed on your system
2. Check that port 5353 (UDP) is not blocked
3. Verify both devices support mDNS
4. Try using the IP address as a fallback
## Related Commands
Start headless server without browser
Attach TUI to running web server
Learn more about network setup
View full HTTP API documentation
# Commands
Source: https://anomalyco-opencode.mintlify.app/commands
Create custom commands for repetitive tasks.
Custom commands let you specify a prompt you want to run when that command is executed in the TUI.
```bash frame="none" theme={null}
/my-command
```
Custom commands are in addition to the built-in commands like `/init`, `/undo`, `/redo`, `/share`, `/help`. [Learn more](/tui#commands).
***
## Create command files
Create markdown files in the `commands/` directory to define custom commands.
Create `.opencode/commands/test.md`:
```md title=".opencode/commands/test.md" theme={null}
---
description: Run tests with coverage
agent: build
model: anthropic/claude-3-5-sonnet-20241022
---
Run the full test suite with coverage report and show any failures.
Focus on the failing tests and suggest fixes.
```
The frontmatter defines command properties. The content becomes the template.
Use the command by typing `/` followed by the command name.
```bash frame="none" theme={null}
"/test"
```
***
## Configure
You can add custom commands through the OpenCode config or by creating markdown files in the `commands/` directory.
### JSON
Use the `command` option in your OpenCode [config](/config):
```json title="opencode.jsonc" {4-12} theme={null}
{
"$schema": "https://opencode.ai/config.json",
"command": {
// This becomes the name of the command
"test": {
// This is the prompt that will be sent to the LLM
"template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes.",
// This is shown as the description in the TUI
"description": "Run tests with coverage",
"agent": "build",
"model": "anthropic/claude-3-5-sonnet-20241022"
}
}
}
```
Now you can run this command in the TUI:
```bash frame="none" theme={null}
/test
```
### Markdown
You can also define commands using markdown files. Place them in:
* Global: `~/.config/opencode/commands/`
* Per-project: `.opencode/commands/`
```markdown title="~/.config/opencode/commands/test.md" theme={null}
---
description: Run tests with coverage
agent: build
model: anthropic/claude-3-5-sonnet-20241022
---
Run the full test suite with coverage report and show any failures.
Focus on the failing tests and suggest fixes.
```
The markdown file name becomes the command name. For example, `test.md` lets
you run:
```bash frame="none" theme={null}
/test
```
***
## Prompt config
The prompts for the custom commands support several special placeholders and syntax.
### Arguments
Pass arguments to commands using the `$ARGUMENTS` placeholder.
```md title=".opencode/commands/component.md" theme={null}
---
description: Create a new component
---
Create a new React component named $ARGUMENTS with TypeScript support.
Include proper typing and basic structure.
```
Run the command with arguments:
```bash frame="none" theme={null}
/component Button
```
And `$ARGUMENTS` will be replaced with `Button`.
You can also access individual arguments using positional parameters:
* `$1` - First argument
* `$2` - Second argument
* `$3` - Third argument
* And so on...
For example:
```md title=".opencode/commands/create-file.md" theme={null}
---
description: Create a new file with content
---
Create a file named $1 in the directory $2
with the following content: $3
```
Run the command:
```bash frame="none" theme={null}
/create-file config.json src "{ \"key\": \"value\" }"
```
This replaces:
* `$1` with `config.json`
* `$2` with `src`
* `$3` with `{ "key": "value" }`
### Shell output
Use *!`command`* to inject [bash command](/tui#bash-commands) output into your prompt.
For example, to create a custom command that analyzes test coverage:
```md title=".opencode/commands/analyze-coverage.md" theme={null}
---
description: Analyze test coverage
---
Here are the current test results:
!`npm test`
Based on these results, suggest improvements to increase coverage.
```
Or to review recent changes:
```md title=".opencode/commands/review-changes.md" theme={null}
---
description: Review recent changes
---
Recent git commits:
!`git log --oneline -10`
Review these changes and suggest any improvements.
```
Commands run in your project's root directory and their output becomes part of the prompt.
### File references
Include files in your command using `@` followed by the filename.
```md title=".opencode/commands/review-component.md" theme={null}
---
description: Review component
---
Review the component in @src/components/Button.tsx.
Check for performance issues and suggest improvements.
```
The file content gets included in the prompt automatically.
***
## Options
Let's look at the configuration options in detail.
### Template
The `template` option defines the prompt that will be sent to the LLM when the command is executed.
```json title="opencode.json" theme={null}
{
"command": {
"test": {
"template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes."
}
}
}
```
This is a **required** config option.
### Description
Use the `description` option to provide a brief description of what the command does.
```json title="opencode.json" theme={null}
{
"command": {
"test": {
"description": "Run tests with coverage"
}
}
}
```
This is shown as the description in the TUI when you type in the command.
### Agent
Use the `agent` config to optionally specify which [agent](/agents) should execute this command.
If this is a [subagent](/agents#subagents) the command will trigger a subagent invocation by default.
To disable this behavior, set `subtask` to `false`.
```json title="opencode.json" theme={null}
{
"command": {
"review": {
"agent": "plan"
}
}
}
```
This is an **optional** config option. If not specified, defaults to your current agent.
### Subtask
Use the `subtask` boolean to force the command to trigger a [subagent](/agents#subagents) invocation.
This is useful if you want the command to not pollute your primary context and will **force** the agent to act as a subagent,
even if `mode` is set to `primary` on the [agent](/agents) configuration.
```json title="opencode.json" theme={null}
{
"command": {
"analyze": {
"subtask": true
}
}
}
```
This is an **optional** config option.
### Model
Use the `model` config to override the default model for this command.
```json title="opencode.json" theme={null}
{
"command": {
"analyze": {
"model": "anthropic/claude-3-5-sonnet-20241022"
}
}
}
```
This is an **optional** config option.
***
## How commands work
Understanding the command system helps you create more powerful custom commands.
### Command discovery
OpenCode discovers commands from multiple sources:
1. **Built-in commands**: `/init`, `/review`, etc.
2. **Config file commands**: Defined in `opencode.json`
3. **MCP prompts**: Exposed by MCP servers as commands
4. **Skills**: Skills can be invoked as commands (if no command with that name exists)
5. **Markdown files**: Files in `.opencode/commands/` directories
Commands are loaded in this order, with later sources taking precedence:
```ts theme={null}
// From command/index.ts
const result: Record = {
[Default.INIT]: { /* ... */ },
[Default.REVIEW]: { /* ... */ },
}
// Add config commands
for (const [name, command] of Object.entries(cfg.command ?? {})) {
result[name] = { /* ... */ }
}
// Add MCP prompts as commands
for (const [name, prompt] of Object.entries(await MCP.prompts())) {
result[name] = { /* ... */ }
}
// Add skills as invokable commands
for (const skill of await Skill.all()) {
if (result[skill.name]) continue // Skip if command exists
result[skill.name] = { /* ... */ }
}
```
### Template processing
Command templates support several special syntaxes that are processed before being sent to the LLM:
**Argument substitution**:
* `$ARGUMENTS` - Replaced with all arguments as a single string
* `$1`, `$2`, `$3`, etc. - Replaced with individual positional arguments
**Shell execution**:
* `` !`command` `` - Runs the command and injects its output
**File inclusion**:
* `@path/to/file` - Includes the file content
The system tracks which placeholders are available:
```ts theme={null}
// From command/index.ts
export function hints(template: string): string[] {
const result: string[] = []
const numbered = template.match(/\$\d+/g)
if (numbered) {
for (const match of [...new Set(numbered)].sort()) result.push(match)
}
if (template.includes("$ARGUMENTS")) result.push("$ARGUMENTS")
return result
}
```
These hints are used by the TUI to show which arguments are expected.
### Command execution
When a command is executed:
1. The command is looked up by name
2. The template is retrieved (may be async for MCP prompts)
3. Placeholders are substituted with actual values
4. The processed template is sent as a user message to the LLM
5. If `agent` is specified, the message is routed to that agent
6. If `subtask` is true, it's executed as a subagent invocation
The `command.executed` event is published:
```ts theme={null}
export const Event = {
Executed: BusEvent.define(
"command.executed",
z.object({
name: z.string(),
sessionID: Identifier.schema("session"),
arguments: z.string(),
messageID: Identifier.schema("message"),
}),
),
}
```
***
## Examples
### Code review command
```md title=".opencode/commands/review.md" theme={null}
---
description: Review code changes for issues
agent: plan
subtask: true
---
Review the following changes:
!`git diff`
Check for:
- Potential bugs or logic errors
- Security vulnerabilities
- Performance issues
- Code style violations
- Missing tests
Provide specific suggestions for improvements.
```
### Documentation generator
```md title=".opencode/commands/document.md" theme={null}
---
description: Generate documentation for a file
---
Generate comprehensive documentation for the following file:
@$1
Include:
- Purpose and overview
- Function/class descriptions
- Parameter descriptions
- Return value descriptions
- Usage examples
- Edge cases and limitations
```
Usage: `/document src/utils/parser.ts`
### Test generator
```md title=".opencode/commands/generate-tests.md" theme={null}
---
description: Generate tests for a module
model: anthropic/claude-3-5-sonnet-20241022
---
Generate comprehensive unit tests for:
@$1
Requirements:
- Use the existing test framework in the project
- Cover all public functions/methods
- Include edge cases and error handling
- Follow existing test patterns in: @tests/example.test.ts
- Aim for >90% coverage
```
Usage: `/generate-tests src/parser.ts`
### Release notes
```md title=".opencode/commands/release-notes.md" theme={null}
---
description: Generate release notes from commits
subtask: true
---
Generate release notes for the following commits:
!`git log $(git describe --tags --abbrev=0)..HEAD --oneline`
Format:
## Features
- List new features
## Fixes
- List bug fixes
## Breaking Changes
- List breaking changes
Group similar changes together and use clear, user-friendly language.
```
### Architecture review
```md title=".opencode/commands/architecture.md" theme={null}
---
description: Review project architecture
agent: plan
---
Analyze the project structure:
!`tree -L 3 -I 'node_modules|.git'`
Evaluate:
- Overall architecture and organization
- Separation of concerns
- Module dependencies and coupling
- Potential architectural improvements
- Scalability considerations
Provide specific recommendations.
```
### Performance analysis
```md title=".opencode/commands/perf.md" theme={null}
---
description: Analyze performance issues
model: anthropic/claude-3-5-sonnet-20241022
---
Analyze performance for:
@$1
Check for:
- Inefficient algorithms (O(n²) or worse)
- Unnecessary loops or iterations
- Memory leaks or excessive allocations
- Blocking operations that could be async
- Missing caching opportunities
- Database query optimization
Suggest specific optimizations with code examples.
```
Usage: `/perf src/api/handler.ts`
### Migration helper
```md title=".opencode/commands/migrate.md" theme={null}
---
description: Help migrate from $1 to $2
---
Help migrate the codebase from $1 to $2.
Current usage of $1:
!`rg "$1" --type ts -l`
Provide:
1. Migration strategy and steps
2. Code transformation examples
3. Potential breaking changes
4. Testing recommendations
5. Rollback plan
```
Usage: `/migrate lodash ramda`
***
## Built-in commands
opencode includes several built-in commands like `/init`, `/undo`, `/redo`, `/share`, `/help`; [learn more](/tui#commands).
### `/init`
Creates or updates the `AGENTS.md` file by analyzing your project structure.
### `/review`
Reviews code changes (commits, branches, or PRs). Defaults to reviewing uncommitted changes.
Usage:
* `/review` - Review uncommitted changes
* `/review commit abc123` - Review specific commit
* `/review branch feature-x` - Review branch changes
* `/review pr 42` - Review pull request #42
The `/review` command is configured with `subtask: true`, meaning it runs as a subagent and doesn't pollute your main context.
Custom commands can override built-in commands. If you define a custom command with the same name, it will override the built-in command.
***
## Best practices
### Use clear descriptions
The description appears in the TUI's command list. Make it clear and concise:
```yaml theme={null}
# Good
description: Run tests with coverage and suggest fixes
# Bad
description: test stuff
```
### Leverage shell commands
Use shell commands to provide context:
```md theme={null}
Current git status:
!`git status`
Recent commits:
!`git log -5 --oneline`
```
### Combine with file references
```md theme={null}
Review the changes in @$1 compared to:
!`git show HEAD:$1`
```
### Use appropriate agents
Route commands to specialized agents:
```yaml theme={null}
# Planning tasks
agent: plan
# Build/test tasks
agent: build
# Code changes
agent: code # default
```
### Set subtask when appropriate
Use `subtask: true` for:
* Analysis tasks that don't need to persist context
* Review operations
* One-off queries
### Provide specific instructions
Be explicit about what you want:
```md theme={null}
# Good
Review @$1 for:
- Potential null pointer errors
- Missing error handling
- SQL injection vulnerabilities
Provide specific line numbers and fixes.
# Bad
Review @$1
```
### Version control your commands
Commit your `.opencode/commands/` directory to share commands with your team:
```bash theme={null}
git add .opencode/commands/
git commit -m "Add custom review command"
```
# Contributing
Source: https://anomalyco-opencode.mintlify.app/community/contributing
Guide to contributing to OpenCode
We want to make it easy for you to contribute to OpenCode. This guide covers everything you need to know about contributing to the project.
## Welcome Contributions
The following types of changes are commonly accepted:
Help us squash bugs and improve stability
Add support for new language servers and formatters
Improvements to AI model behavior and performance
Support for new AI providers
Platform-specific quirks and edge cases
Improve docs, guides, and examples
**UI or core product features** must go through a design review with the core team before implementation. Open an issue first to discuss.
## Finding Issues
Looking for something to work on? Check out these labels:
* [`help wanted`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted) - Issues where we'd love community help
* [`good first issue`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) - Great starting points for new contributors
* [`bug`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) - Known bugs that need fixing
* [`perf`](https://github.com/anomalyco/opencode/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22perf%22) - Performance improvements
Want to take on an issue? Leave a comment and a maintainer may assign it to you. Please wait for assignment before starting work.
## Adding New Providers
New providers shouldn't require code changes to OpenCode. First make a PR to:
[https://github.com/anomalyco/models.dev](https://github.com/anomalyco/models.dev)
## Development Setup
### Requirements
* Bun 1.3 or higher
### Getting Started
Install dependencies and start the dev server from the repo root:
```bash theme={null}
bun install
bun dev
```
### Running Against Different Directories
By default, `bun dev` runs OpenCode in the `packages/opencode` directory. To run it against a different directory:
```bash theme={null}
bun dev
```
To run OpenCode in the root of the opencode repo itself:
```bash theme={null}
bun dev .
```
### Building a Local Binary
To compile a standalone executable:
```bash theme={null}
./packages/opencode/script/build.ts --single
```
Then run it with:
```bash theme={null}
./packages/opencode/dist/opencode-/bin/opencode
```
Replace `` with your platform (e.g., `darwin-arm64`, `linux-x64`).
## Project Structure
Core pieces of the codebase:
* `packages/opencode` - OpenCode core business logic & server
* `packages/opencode/src/cli/cmd/tui/` - The TUI code, written in SolidJS with [opentui](https://github.com/sst/opentui)
* `packages/app` - The shared web UI components, written in SolidJS
* `packages/desktop` - The native desktop app, built with Tauri
* `packages/plugin` - Source for `@opencode-ai/plugin`
## Development Workflows
During development, `bun dev` is the local equivalent of the built `opencode` command. Both run the same CLI interface:
```bash theme={null}
# Development (from project root)
bun dev --help # Show all available commands
bun dev serve # Start headless API server
bun dev web # Start server + open web interface
bun dev # Start TUI in specific directory
# Production
opencode --help # Show all available commands
opencode serve # Start headless API server
opencode web # Start server + open web interface
opencode # Start TUI in specific directory
```
To start the OpenCode headless API server:
```bash theme={null}
bun dev serve
```
This starts the headless server on port 4096 by default. You can specify a different port:
```bash theme={null}
bun dev serve --port 8080
```
To test UI changes during development:
1. **First, start the OpenCode server:**
```bash theme={null}
bun dev serve
```
2. **Then run the web app:**
```bash theme={null}
bun run --cwd packages/app dev
```
This starts a local dev server at [http://localhost:5173](http://localhost:5173). Most UI changes can be tested here, but the server must be running for full functionality.
The desktop app is a native Tauri application that wraps the web UI.
To run the native desktop app:
```bash theme={null}
bun run --cwd packages/desktop tauri dev
```
This starts the web dev server on [http://localhost:1420](http://localhost:1420) and opens the native window.
If you only want the web dev server (no native shell):
```bash theme={null}
bun run --cwd packages/desktop dev
```
To create a production build:
```bash theme={null}
bun run --cwd packages/desktop tauri build
```
Running the desktop app requires additional Tauri dependencies (Rust toolchain, platform-specific libraries). See the [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) for setup instructions.
Bun debugging is currently rough around the edges. The most reliable way to debug OpenCode is to run it manually in a terminal via `bun run --inspect= dev ...` and attach your debugger via that URL.
**Caveats:**
* If you want to debug server code with the TUI, you might need to run `bun dev spawn` instead of `bun dev`
* You can debug the server separately:
* Debug server: `bun run --inspect=ws://localhost:6499/ --cwd packages/opencode ./src/index.ts serve --port 4096`
* Then attach TUI: `opencode attach http://localhost:4096`
* Debug TUI: `bun run --inspect=ws://localhost:6499/ --cwd packages/opencode --conditions=browser ./src/index.ts`
**Tips:**
* Use `--inspect-wait` or `--inspect-brk` for different workflows
* Set `export BUN_OPTIONS=--inspect=ws://localhost:6499/` to avoid repeating the flag
**VSCode Setup:**
See `.vscode/settings.example.json` and `.vscode/launch.example.json` for example configurations.
If you make changes to the API or SDK (e.g. `packages/opencode/src/server/server.ts`), run `./script/generate.ts` to regenerate the SDK and related files.
Please try to follow the [style guide](https://github.com/anomalyco/opencode/blob/dev/AGENTS.md).
## Pull Request Guidelines
### Issue First Policy
**All PRs must reference an existing issue.** PRs without a linked issue may be closed without review.
Before opening a PR, open an issue describing the bug or feature. This helps maintainers triage and prevents duplicate work.
* Use `Fixes #123` or `Closes #123` in your PR description to link the issue
* For small fixes, a brief issue is fine - just enough context to understand the problem
### General Requirements
* Keep pull requests small and focused
* Explain the issue and why your change fixes it
* Before adding new functionality, ensure it doesn't already exist elsewhere in the codebase
### UI Changes
If your PR includes UI changes, please include screenshots or videos showing the before and after. This helps maintainers review faster.
### Logic Changes
For non-UI changes (bug fixes, new features, refactors), explain **how you verified it works**:
* What did you test?
* How can a reviewer reproduce/confirm the fix?
### No AI-Generated Walls of Text
Long, AI-generated PR descriptions and issues are not acceptable and may be ignored.
Respect the maintainers' time:
* Write short, focused descriptions
* Explain what changed and why in your own words
* If you can't explain it briefly, your PR might be too large
### PR Titles
PR titles should follow conventional commit standards:
* `feat:` new feature or functionality
* `fix:` bug fix
* `docs:` documentation or README changes
* `chore:` maintenance tasks, dependency updates, etc.
* `refactor:` code refactoring without changing behavior
* `test:` adding or updating tests
You can optionally include a scope:
* `feat(app):` feature in the app package
* `fix(desktop):` bug fix in the desktop package
* `chore(opencode):` maintenance in the opencode package
**Examples:**
* `docs: update contributing guidelines`
* `fix: resolve crash on startup`
* `feat: add dark mode support`
* `feat(app): add dark mode support`
* `fix(desktop): resolve crash on startup`
* `chore: bump dependency versions`
### Style Preferences
These are general guidelines, not strictly enforced:
* **Functions:** Keep logic within a single function unless breaking it out adds clear reuse or composition benefits
* **Destructuring:** Avoid unnecessary destructuring of variables
* **Control flow:** Avoid `else` statements
* **Error handling:** Prefer `.catch(...)` instead of `try`/`catch` when possible
* **Types:** Use precise types and avoid `any`
* **Variables:** Stick to immutable patterns and avoid `let`
* **Naming:** Choose concise single-word identifiers when descriptive
* **Runtime APIs:** Use Bun helpers such as `Bun.file()` when appropriate
## Feature Requests
For net-new functionality, start with a design conversation. Open an issue describing:
* The problem you're trying to solve
* Your proposed approach (optional)
* Why it belongs in OpenCode
Wait for core team approval before opening a feature PR.
## Trust & Vouch System
This project uses [vouch](https://github.com/mitchellh/vouch) to manage contributor trust. The vouch list is maintained in `.github/VOUCHED.td`.
### How it works
* **Vouched users** are explicitly trusted contributors
* **Denounced users** are explicitly blocked. Issues and pull requests from denounced users are automatically closed
* **Everyone else** can participate normally — you don't need to be vouched to open issues or PRs
### For maintainers
Collaborators with write access can manage the vouch list by commenting on any issue:
* `vouch` — vouch for the issue author
* `vouch @username` — vouch for a specific user
* `denounce` — denounce the issue author
* `denounce @username` — denounce a specific user
* `denounce @username ` — denounce with a reason
* `unvouch` / `unvouch @username` — remove someone from the list
Changes are committed automatically to `.github/VOUCHED.td`.
### Denouncement Policy
Denouncement is reserved for users who repeatedly submit low-quality AI-generated contributions, spam, or otherwise act in bad faith. It is not used for disagreements or honest mistakes.
## Issue Requirements
All issues **must** use one of our issue templates:
* **Bug report** — for reporting bugs (requires a description)
* **Feature request** — for suggesting enhancements (requires verification checkbox and description)
* **Question** — for asking questions (requires the question)
Blank issues are not allowed. When a new issue is opened, an automated check verifies that it follows a template and meets our contributing guidelines. If an issue doesn't meet the requirements, you'll receive a comment explaining what needs to be fixed and have **2 hours** to edit the issue. After that, it will be automatically closed.
Issues may be flagged for:
* Not using a template
* Required fields left empty or filled with placeholder text
* AI-generated walls of text
* Missing meaningful content
If you believe your issue was incorrectly flagged, let a maintainer know.
## Community
Join the OpenCode community:
* [Discord](https://opencode.ai/discord) - Chat with the community and maintainers
* [GitHub Discussions](https://github.com/anomalyco/opencode/discussions) - Ask questions and share ideas
* [Twitter](https://twitter.com/opencode_ai) - Stay updated with the latest news
Thank you for contributing to OpenCode!
# Ecosystem
Source: https://anomalyco-opencode.mintlify.app/community/ecosystem
Community projects and integrations built with OpenCode
A collection of community projects built on OpenCode.
Want to add your OpenCode related project to this list? Submit a PR to the [OpenCode repository](https://github.com/anomalyco/opencode).
You can also check out [awesome-opencode](https://github.com/awesome-opencode/awesome-opencode) and [opencode.cafe](https://opencode.cafe), a community hub that aggregates the ecosystem and connects contributors.
## Plugins
Extend OpenCode's functionality with community-built plugins.
Automatically run OpenCode sessions in isolated Daytona sandboxes with git sync and live previews
Automatically inject Helicone session headers for request grouping
Auto-inject TypeScript/Svelte types into file reads with lookup tools
Use your ChatGPT Plus/Pro subscription instead of API credits
Use your existing Gemini plan instead of API billing
Use Antigravity's free models instead of API billing
Multi-branch devcontainer isolation with shallow clones and auto-assigned ports
Google Antigravity OAuth Plugin, with support for Google Search, and more robust API handling
Optimize token usage by pruning obsolete tool outputs
Add native websearch support for supported providers with Google grounded style
Enables AI agents to run background processes in a PTY, send interactive input to them
Instructions for non-interactive shell commands - prevents hangs from TTY-dependent operations
Track OpenCode usage with Wakatime
Clean up markdown tables produced by LLMs
10x faster code editing with Morph Fast Apply API and lazy edit markers
Background agents, pre-built LSP/AST/MCP tools, curated agents, Claude Code compatible
Desktop notifications and sound alerts for OpenCode sessions
Desktop notifications and sound alerts for permission, completion, and error events
AI-powered automatic Zellij session naming based on OpenCode context
Allow OpenCode agents to lazy load prompts on demand with skill discovery and injection
Persistent memory across sessions using Supermemory
Interactive plan review with visual annotation and private/offline sharing
Extend opencode /commands into a powerful orchestration system with granular flow control
Schedule recurring jobs using launchd (Mac) or systemd (Linux) with cron syntax
Structured Brainstorm → Plan → Implement workflow with session continuity
Interactive browser UI for AI brainstorming with multi-question forms
Claude Code-style background agents with async delegation and context persistence
Native OS notifications for OpenCode – know when tasks complete
Bundled multi-agent orchestration harness – 16 components, one install
Zero-friction git worktrees for OpenCode
## Projects
Applications and tools built on top of OpenCode.
Discord bot to control OpenCode sessions, built on the SDK
Neovim plugin for editor-aware prompts, built on the API
Mobile-first web UI for OpenCode over Tailscale/VPN
Template for building OpenCode plugins
Neovim frontend for opencode - a terminal-based AI coding agent
Vercel AI SDK provider for using OpenCode via @opencode-ai/sdk
Web / Desktop App and VS Code Extension for OpenCode
Obsidian plugin that embeds OpenCode in Obsidian's UI
An open-source alternative to Claude Cowork, powered by OpenCode
OpenCode extension manager with portable, isolated profiles
Desktop, Web, Mobile and Remote Client App for OpenCode
## Agents
Pre-configured agents and agent frameworks for specialized workflows.
Modular AI agents and commands for structured development
Configs, prompts, agents, and plugins for enhanced workflows
## Get Involved
Build something with OpenCode? Share it with the community:
* Submit a PR to add your project to this list
* Join the [OpenCode Discord](https://opencode.ai/discord)
* Check out [awesome-opencode](https://github.com/awesome-opencode/awesome-opencode) for more resources
# FAQ
Source: https://anomalyco-opencode.mintlify.app/community/faq
Frequently asked questions about OpenCode
Common questions about OpenCode, installation, usage, and troubleshooting.
## General
OpenCode is an AI coding agent CLI tool that helps developers with software engineering tasks. It's a powerful command-line interface that uses large language models to assist with coding, debugging, refactoring, and more.
OpenCode can:
* Write and edit code across multiple files
* Debug and fix issues
* Refactor and optimize code
* Generate documentation
* Answer questions about your codebase
OpenCode is designed as a CLI-first tool that gives you full control over your development workflow. Unlike IDE-specific tools, OpenCode:
* Works in any terminal environment
* Supports multiple AI providers (OpenAI, Anthropic, Google, and more)
* Offers a plugin system for extensibility
* Provides both TUI (Terminal UI) and web interfaces
* Has an active open-source community building plugins and integrations
OpenCode itself is free and open-source software licensed under the MIT license. However, you'll need API access to AI providers (like OpenAI or Anthropic) which have their own pricing.
Some community plugins provide alternative authentication methods that can reduce or eliminate API costs by using existing subscriptions.
OpenCode supports multiple AI providers including:
* OpenAI (GPT-4, GPT-3.5)
* Anthropic (Claude)
* Google (Gemini)
* And many more through community plugins
To add support for a new provider, contribute to [models.dev](https://github.com/anomalyco/models.dev).
## Installation & Setup
The easiest way to install OpenCode is via npm:
```bash theme={null}
npm install -g opencode
```
Or using your preferred package manager:
```bash theme={null}
# Using Yarn
yarn global add opencode
# Using pnpm
pnpm add -g opencode
```
For development, see the [Contributing guide](/community/contributing) for instructions on building from source.
OpenCode will prompt you to configure your API keys when you first run it. You can also manually configure them:
```bash theme={null}
opencode config
```
Your configuration is stored securely in your home directory.
OpenCode requires:
* Node.js 18 or higher (for npm installation)
* Bun 1.3+ (for development)
* A terminal emulator with modern features
* API access to at least one supported AI provider
OpenCode runs on macOS, Linux, and Windows (via WSL).
## Usage
Navigate to your project directory and run:
```bash theme={null}
opencode
```
This will start the TUI (Terminal User Interface) where you can interact with the AI agent. You can also use specific commands:
```bash theme={null}
# Start web interface
opencode web
# Start headless server
opencode serve
# Run in a specific directory
opencode /path/to/project
```
OpenCode supports various commands:
* `opencode` - Start TUI in current directory
* `opencode web` - Start web interface
* `opencode serve` - Start headless API server
* `opencode attach ` - Attach to existing server
* `opencode --help` - Show all available commands
Within the TUI, you can use slash commands like `/help`, `/clear`, `/exit`, and more.
Yes! OpenCode is designed to work alongside your IDE. Some community projects provide deeper integration:
* [opencode.nvim](https://github.com/NickvanDyke/opencode.nvim) - Neovim plugin
* [OpenChamber](https://github.com/btriapitsyn/openchamber) - VS Code Extension
* [OpenCode-Obsidian](https://github.com/mtymek/opencode-obsidian) - Obsidian plugin
Check out the [Ecosystem](/community/ecosystem) page for more integrations.
OpenCode has a plugin system that allows you to extend functionality. To install plugins, you typically:
1. Install the plugin package via npm
2. Configure it in your OpenCode settings
Some plugins may have additional setup steps. Check the plugin's documentation for specific instructions.
See the [Ecosystem](/community/ecosystem) page for available plugins.
## Troubleshooting
If OpenCode appears to hang:
1. Check if the AI provider's API is responsive
2. Verify your API key is valid and has sufficient credits
3. Try restarting OpenCode
4. Check for any error messages in the terminal
If you're using shell commands that require interactive input, consider using the [opencode-shell-strategy](https://github.com/JRedeker/opencode-shell-strategy) plugin to prevent TTY-related hangs.
Rate limit errors mean you're making too many requests to the AI provider's API. To resolve:
1. Wait for the rate limit to reset (usually a few minutes)
2. Consider upgrading your API plan for higher limits
3. Use the [opencode-dynamic-context-pruning](https://github.com/Tarquinen/opencode-dynamic-context-pruning) plugin to optimize token usage
To help the AI better understand your codebase:
1. Provide clear, specific instructions
2. Reference specific files or functions
3. Use the [opencode-type-inject](https://github.com/nick-vi/opencode-type-inject) plugin for better type awareness
4. Break down complex tasks into smaller steps
To report a bug:
1. Check if the issue already exists in [GitHub Issues](https://github.com/anomalyco/opencode/issues)
2. Use the Bug Report template when creating a new issue
3. Include:
* OpenCode version (`opencode --version`)
* Your operating system
* Steps to reproduce
* Expected vs. actual behavior
* Any error messages
See the [Contributing guide](/community/contributing) for more details.
If you need help:
* Join the [Discord community](https://opencode.ai/discord)
* Check [GitHub Discussions](https://github.com/anomalyco/opencode/discussions)
* Search existing [GitHub Issues](https://github.com/anomalyco/opencode/issues)
* Read the documentation at [opencode.ai/docs](https://opencode.ai/docs)
## Development & Contributing
We welcome contributions! See the [Contributing guide](/community/contributing) for detailed instructions.
The most common contributions are:
* Bug fixes
* Documentation improvements
* New LSP/formatter support
* Provider integrations
* Performance improvements
To build a plugin:
1. Use the [opencode plugin template](https://github.com/zenobi-us/opencode-plugin-template/) as a starting point
2. Follow the plugin API documentation
3. Test your plugin locally with `bun dev`
4. Submit your plugin to the [Ecosystem](/community/ecosystem)
Check out existing plugins on the [Ecosystem](/community/ecosystem) page for inspiration.
OpenCode is open-source and available on GitHub:
[github.com/anomalyco/opencode](https://github.com/anomalyco/opencode)
The repository includes:
* Core OpenCode logic
* TUI and web interfaces
* Desktop app
* Plugin system
* Documentation
The development workflow is:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Test locally with `bun dev`
5. Open a PR following the [Contributing guidelines](/community/contributing)
See the [Contributing guide](/community/contributing) for detailed setup instructions.
## Community
Join the OpenCode community:
* [Discord](https://opencode.ai/discord) - Chat with users and maintainers
* [GitHub Discussions](https://github.com/anomalyco/opencode/discussions) - Ask questions and share ideas
* [Twitter](https://twitter.com/opencode_ai) - Latest news and updates
* [opencode.cafe](https://opencode.cafe) - Community hub
Check out the [Ecosystem](/community/ecosystem) page for:
* Plugins that extend OpenCode
* Projects built on OpenCode
* Pre-configured agents
* Integrations with other tools
You can also browse [awesome-opencode](https://github.com/awesome-opencode/awesome-opencode) for a curated list.
Yes! If you've built something with OpenCode:
1. Fork the [OpenCode repository](https://github.com/anomalyco/opencode)
2. Add your project to the ecosystem documentation
3. Submit a PR with a brief description
See the [Contributing guide](/community/contributing) for PR guidelines.
## Still have questions?
Chat with the community and get help from maintainers and other users.
# Configuration
Source: https://anomalyco-opencode.mintlify.app/config
Using the OpenCode JSON config to customize your settings
You can configure OpenCode using a JSON config file.
## Format
OpenCode supports both **JSON** and **JSONC** (JSON with Comments) formats.
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
// Theme configuration
"theme": "opencode",
"model": "anthropic/claude-sonnet-4-5",
"autoupdate": true
}
```
## Locations
You can place your config in a couple of different locations and they have a different order of precedence.
Configuration files are **merged together**, not replaced.
Configuration files are merged together, not replaced. Settings from the following config locations are combined. Later configs override earlier ones only for conflicting keys. Non-conflicting settings from all configs are preserved.
For example, if your global config sets `theme: "opencode"` and `autoupdate: true`, and your project config sets `model: "anthropic/claude-sonnet-4-5"`, the final configuration will include all three settings.
### Precedence order
Config sources are loaded in this order (later sources override earlier ones):
1. **Remote config** (from `.well-known/opencode`) - organizational defaults
2. **Global config** (`~/.config/opencode/opencode.json`) - user preferences
3. **Custom config** (`OPENCODE_CONFIG` env var) - custom overrides
4. **Project config** (`opencode.json` in project) - project-specific settings
5. **`.opencode` directories** - agents, commands, plugins
6. **Inline config** (`OPENCODE_CONFIG_CONTENT` env var) - runtime overrides
This means project configs can override global defaults, and global configs can override remote organizational defaults.
The `.opencode` and `~/.config/opencode` directories use **plural names** for subdirectories: `agents/`, `commands/`, `modes/`, `plugins/`, `skills/`, `tools/`, and `themes/`. Singular names (e.g., `agent/`) are also supported for backwards compatibility.
### Remote
Organizations can provide default configuration via the `.well-known/opencode` endpoint. This is fetched automatically when you authenticate with a provider that supports it.
Remote config is loaded first, serving as the base layer. All other config sources (global, project) can override these defaults.
For example, if your organization provides MCP servers that are disabled by default:
```json title="Remote config from .well-known/opencode" theme={null}
{
"mcp": {
"jira": {
"type": "remote",
"url": "https://jira.example.com/mcp",
"enabled": false
}
}
}
```
You can enable specific servers in your local config:
```json title="opencode.json" theme={null}
{
"mcp": {
"jira": {
"type": "remote",
"url": "https://jira.example.com/mcp",
"enabled": true
}
}
}
```
### Global
Place your global OpenCode config in `~/.config/opencode/opencode.json`. Use global config for user-wide preferences like themes, providers, or keybinds.
Global config overrides remote organizational defaults.
### Per project
Add `opencode.json` in your project root. Project config has the highest precedence among standard config files - it overrides both global and remote configs.
Place project specific config in the root of your project.
When OpenCode starts up, it looks for a config file in the current directory or traverse up to the nearest Git directory.
This is also safe to be checked into Git and uses the same schema as the global one.
### Custom path
Specify a custom config file path using the `OPENCODE_CONFIG` environment variable.
```bash theme={null}
export OPENCODE_CONFIG=/path/to/my/custom-config.json
opencode run "Hello world"
```
Custom config is loaded between global and project configs in the precedence order.
### Custom directory
Specify a custom config directory using the `OPENCODE_CONFIG_DIR` environment variable. This directory will be searched for agents, commands, modes, and plugins just like the standard `.opencode` directory, and should follow the same structure.
```bash theme={null}
export OPENCODE_CONFIG_DIR=/path/to/my/config-directory
opencode run "Hello world"
```
The custom directory is loaded after the global config and `.opencode` directories, so it **can override** their settings.
## Schema
The config file has a schema that's defined in [**`opencode.ai/config.json`**](https://opencode.ai/config.json).
Your editor should be able to validate and autocomplete based on the schema.
## Configuration Options
### TUI
You can configure TUI-specific settings through the `tui` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"tui": {
"scroll_speed": 3,
"scroll_acceleration": {
"enabled": true
},
"diff_style": "auto"
}
}
```
Enable macOS-style scroll acceleration. Takes precedence over `scroll_speed`.
Custom scroll speed multiplier (minimum: `1`). Ignored if `scroll_acceleration.enabled` is `true`.
Control diff rendering. `"auto"` adapts to terminal width, `"stacked"` always shows single column.
### Server
You can configure server settings for the `opencode serve` and `opencode web` commands through the `server` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"server": {
"port": 4096,
"hostname": "0.0.0.0",
"mdns": true,
"mdnsDomain": "myproject.local",
"cors": ["http://localhost:5173"]
}
}
```
Port to listen on.
Hostname to listen on. When `mdns` is enabled and no hostname is set, defaults to `0.0.0.0`.
Enable mDNS service discovery. This allows other devices on the network to discover your OpenCode server.
Custom domain name for mDNS service. Useful for running multiple instances on the same network.
Additional origins to allow for CORS when using the HTTP server from a browser-based client. Values must be full origins (scheme + host + optional port), eg `https://app.example.com`.
### Tools
You can manage the tools an LLM can use through the `tools` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"tools": {
"write": false,
"bash": false
}
}
```
### Models
You can configure the providers and models you want to use in your OpenCode config through the `provider`, `model` and `small_model` options.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {},
"model": "anthropic/claude-sonnet-4-5",
"small_model": "anthropic/claude-haiku-4-5"
}
```
The `small_model` option configures a separate model for lightweight tasks like title generation. By default, OpenCode tries to use a cheaper model if one is available from your provider, otherwise it falls back to your main model.
Provider options can include `timeout` and `setCacheKey`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"anthropic": {
"options": {
"timeout": 600000,
"setCacheKey": true
}
}
}
}
```
Request timeout in milliseconds. Set to `false` to disable.
Ensure a cache key is always set for designated provider.
#### Provider-Specific Options
Some providers support additional configuration options beyond the generic `timeout` and `apiKey` settings.
##### Amazon Bedrock
Amazon Bedrock supports AWS-specific configuration:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"amazon-bedrock": {
"options": {
"region": "us-east-1",
"profile": "my-aws-profile",
"endpoint": "https://bedrock-runtime.us-east-1.vpce-xxxxx.amazonaws.com"
}
}
}
}
```
AWS region for Bedrock (defaults to `AWS_REGION` env var or `us-east-1`).
AWS named profile from `~/.aws/credentials` (defaults to `AWS_PROFILE` env var).
Custom endpoint URL for VPC endpoints. This is an alias for the generic `baseURL` option using AWS-specific terminology. If both are specified, `endpoint` takes precedence.
Bearer tokens (`AWS_BEARER_TOKEN_BEDROCK` or `/connect`) take precedence over profile-based authentication.
### Themes
You can configure the theme you want to use in your OpenCode config through the `theme` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"theme": "opencode"
}
```
### Agents
You can configure specialized agents for specific tasks through the `agent` option.
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"agent": {
"code-reviewer": {
"description": "Reviews code for best practices and potential issues",
"model": "anthropic/claude-sonnet-4-5",
"prompt": "You are a code reviewer. Focus on security, performance, and maintainability.",
"tools": {
// Disable file modification tools for review-only agent
"write": false,
"edit": false
}
}
}
}
```
You can also define agents using markdown files in `~/.config/opencode/agents/` or `.opencode/agents/`.
### Default agent
You can set the default agent using the `default_agent` option. This determines which agent is used when none is explicitly specified.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"default_agent": "plan"
}
```
The default agent must be a primary agent (not a subagent). This can be a built-in agent like `"build"` or `"plan"`, or a custom agent you've defined. If the specified agent doesn't exist or is a subagent, OpenCode will fall back to `"build"` with a warning.
This setting applies across all interfaces: TUI, CLI (`opencode run`), desktop app, and GitHub Action.
### Sharing
You can configure the share feature through the `share` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"share": "manual"
}
```
This takes:
* `"manual"` - Allow manual sharing via commands (default)
* `"auto"` - Automatically share new conversations
* `"disabled"` - Disable sharing entirely
By default, sharing is set to manual mode where you need to explicitly share conversations using the `/share` command.
### Commands
You can configure custom commands for repetitive tasks through the `command` option.
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"command": {
"test": {
"template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes.",
"description": "Run tests with coverage",
"agent": "build",
"model": "anthropic/claude-haiku-4-5"
},
"component": {
"template": "Create a new React component named $ARGUMENTS with TypeScript support.\nInclude proper typing and basic structure.",
"description": "Create a new component"
}
}
}
```
You can also define commands using markdown files in `~/.config/opencode/commands/` or `.opencode/commands/`.
### Keybinds
You can customize your keybinds through the `keybinds` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"keybinds": {}
}
```
### Autoupdate
OpenCode will automatically download any new updates when it starts up. You can disable this with the `autoupdate` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"autoupdate": false
}
```
If you don't want updates but want to be notified when a new version is available, set `autoupdate` to `"notify"`.
This only works if it was not installed using a package manager such as Homebrew.
### Formatters
You can configure code formatters through the `formatter` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"prettier": {
"disabled": true
},
"custom-prettier": {
"command": ["npx", "prettier", "--write", "$FILE"],
"environment": {
"NODE_ENV": "development"
},
"extensions": [".js", ".ts", ".jsx", ".tsx"]
}
}
}
```
### Permissions
By default, opencode **allows all operations** without requiring explicit approval. You can change this using the `permission` option.
For example, to ensure that the `edit` and `bash` tools require user approval:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "ask",
"bash": "ask"
}
}
```
### Compaction
You can control context compaction behavior through the `compaction` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"compaction": {
"auto": true,
"prune": true,
"reserved": 10000
}
}
```
Automatically compact the session when context is full.
Remove old tool outputs to save tokens.
Token buffer for compaction. Leaves enough window to avoid overflow during compaction.
### Watcher
You can configure file watcher ignore patterns through the `watcher` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"watcher": {
"ignore": ["node_modules/**", "dist/**", ".git/**"]
}
}
```
Patterns follow glob syntax. Use this to exclude noisy directories from file watching.
### MCP servers
You can configure MCP servers you want to use through the `mcp` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {}
}
```
### Plugins
Plugins extend OpenCode with custom tools, hooks, and integrations.
Place plugin files in `.opencode/plugins/` or `~/.config/opencode/plugins/`. You can also load plugins from npm through the `plugin` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-helicone-session", "@my-org/custom-plugin"]
}
```
### Instructions
You can configure the instructions for the model you're using through the `instructions` option.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"instructions": ["CONTRIBUTING.md", "docs/guidelines.md", ".cursor/rules/*.md"]
}
```
This takes an array of paths and glob patterns to instruction files.
### Disabled providers
You can disable providers that are loaded automatically through the `disabled_providers` option. This is useful when you want to prevent certain providers from being loaded even if their credentials are available.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"disabled_providers": ["openai", "gemini"]
}
```
The `disabled_providers` takes priority over `enabled_providers`.
The `disabled_providers` option accepts an array of provider IDs. When a provider is disabled:
* It won't be loaded even if environment variables are set.
* It won't be loaded even if API keys are configured through the `/connect` command.
* The provider's models won't appear in the model selection list.
### Enabled providers
You can specify an allowlist of providers through the `enabled_providers` option. When set, only the specified providers will be enabled and all others will be ignored.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"enabled_providers": ["anthropic", "openai"]
}
```
This is useful when you want to restrict OpenCode to only use specific providers rather than disabling them one by one.
The `disabled_providers` takes priority over `enabled_providers`.
If a provider appears in both `enabled_providers` and `disabled_providers`, the `disabled_providers` takes priority for backwards compatibility.
### Experimental
The `experimental` key contains options that are under active development.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"experimental": {}
}
```
Experimental options are not stable. They may change or be removed without notice.
## Variables
You can use variable substitution in your config files to reference environment variables and file contents.
### Env vars
Use `{env:VARIABLE_NAME}` to substitute environment variables:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"model": "{env:OPENCODE_MODEL}",
"provider": {
"anthropic": {
"models": {},
"options": {
"apiKey": "{env:ANTHROPIC_API_KEY}"
}
}
}
}
```
If the environment variable is not set, it will be replaced with an empty string.
### Files
Use `{file:path/to/file}` to substitute the contents of a file:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"instructions": ["./custom-instructions.md"],
"provider": {
"openai": {
"options": {
"apiKey": "{file:~/.secrets/openai-key}"
}
}
}
}
```
File paths can be:
* Relative to the config file directory
* Or absolute paths starting with `/` or `~`
These are useful for:
* Keeping sensitive data like API keys in separate files.
* Including large instruction files without cluttering your config.
* Sharing common configuration snippets across multiple config files.
# Custom Tools
Source: https://anomalyco-opencode.mintlify.app/custom-tools
Create tools the LLM can call in opencode.
Custom tools are functions you create that the LLM can call during conversations. They work alongside opencode's [built-in tools](https://opencode.ai/tools) like `read`, `write`, and `bash`.
***
## Creating a tool
Tools are defined as **TypeScript** or **JavaScript** files. However, the tool definition can invoke scripts written in **any language** — TypeScript or JavaScript is only used for the tool definition itself.
### Location
They can be defined:
* Locally by placing them in the `.opencode/tools/` directory of your project.
* Or globally, by placing them in `~/.config/opencode/tools/`.
### Structure
The easiest way to create tools is using the `tool()` helper which provides type-safety and validation.
```ts title=".opencode/tools/database.ts" {1} theme={null}
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Query the project database",
args: {
query: tool.schema.string().describe("SQL query to execute"),
},
async execute(args) {
// Your database logic here
return `Executed query: ${args.query}`
},
})
```
The **filename** becomes the **tool name**. The above creates a `database` tool.
#### Multiple tools per file
You can also export multiple tools from a single file. Each export becomes **a separate tool** with the name **`_`**:
```ts title=".opencode/tools/math.ts" theme={null}
import { tool } from "@opencode-ai/plugin"
export const add = tool({
description: "Add two numbers",
args: {
a: tool.schema.number().describe("First number"),
b: tool.schema.number().describe("Second number"),
},
async execute(args) {
return args.a + args.b
},
})
export const multiply = tool({
description: "Multiply two numbers",
args: {
a: tool.schema.number().describe("First number"),
b: tool.schema.number().describe("Second number"),
},
async execute(args) {
return args.a * args.b
},
})
```
This creates two tools: `math_add` and `math_multiply`.
***
## Tool API
### Tool definition
The `tool()` function accepts an object with the following properties:
```ts theme={null}
import { tool } from "@opencode-ai/plugin"
export default tool({
description: string,
args: ZodRawShape,
async execute(args, context) {
// Implementation
return string
},
})
```
* **`description`** (required): A clear description of what the tool does. The LLM uses this to decide when to call your tool.
* **`args`** (required): A Zod schema object defining the tool's parameters. Use `tool.schema` (which is Zod) to define types.
* **`execute`** (required): An async function that implements the tool's logic. Must return a string that will be shown to the LLM.
### Arguments
You can use `tool.schema`, which is just [Zod](https://zod.dev), to define argument types.
```ts "tool.schema" theme={null}
args: {
query: tool.schema.string().describe("SQL query to execute")
}
```
You can also import [Zod](https://zod.dev) directly and return a plain object:
```ts {6} theme={null}
import { z } from "zod"
export default {
description: "Tool description",
args: {
param: z.string().describe("Parameter description"),
},
async execute(args, context) {
// Tool implementation
return "result"
},
}
```
#### Available schema types
Since `tool.schema` is Zod, you have access to all Zod types:
```ts theme={null}
tool.schema.string() // String
tool.schema.number() // Number
tool.schema.boolean() // Boolean
tool.schema.array(z.string()) // Array of strings
tool.schema.object({...}) // Nested object
tool.schema.enum([...]) // Enum
tool.schema.optional() // Optional field
tool.schema.default(value) // Default value
```
Always add `.describe()` to help the LLM understand what each parameter is for:
```ts theme={null}
args: {
name: tool.schema.string().describe("The user's name"),
age: tool.schema.number().optional().describe("The user's age (optional)"),
role: tool.schema.enum(["admin", "user"]).describe("The user's role"),
}
```
### Context
Tools receive context about the current session:
```ts title=".opencode/tools/project.ts" {8} theme={null}
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Get project information",
args: {},
async execute(args, context) {
// Access context information
const { agent, sessionID, messageID, directory, worktree } = context
return `Agent: ${agent}, Session: ${sessionID}, Message: ${messageID}, Directory: ${directory}, Worktree: ${worktree}`
},
})
```
#### Context properties
```ts theme={null}
type ToolContext = {
sessionID: string // Current session ID
messageID: string // Current message ID
agent: string // Current agent name
directory: string // Current working directory
worktree: string // Git worktree root
abort: AbortSignal // Signal to detect cancellation
metadata(input: { // Update tool execution metadata
title?: string
metadata?: Record
}): void
ask(input: { // Request permissions during execution
permission: string
patterns: string[]
always: string[]
metadata: Record
}): Promise
}
```
* **`directory`**: Use this instead of `process.cwd()` when resolving relative paths
* **`worktree`**: Useful for generating stable relative paths with `path.relative(worktree, absPath)`
* **`abort`**: Check `abort.aborted` to detect if the user cancelled the operation
* **`metadata()`**: Update the tool's title or add custom metadata shown in the UI
* **`ask()`**: Request user permission during tool execution
#### Using metadata
```ts theme={null}
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Process large dataset",
args: {
path: tool.schema.string(),
},
async execute(args, context) {
context.metadata({ title: "Processing dataset..." })
// Long-running operation
const result = await processData(args.path)
context.metadata({
title: "Dataset processed",
metadata: { rowCount: result.rows }
})
return `Processed ${result.rows} rows`
},
})
```
#### Handling cancellation
```ts theme={null}
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Long running task",
args: {},
async execute(args, context) {
for (let i = 0; i < 1000; i++) {
if (context.abort.aborted) {
return "Task was cancelled"
}
await doWork(i)
}
return "Task completed"
},
})
```
***
## Examples
### Write a tool in Python
You can write your tools in any language you want. Here's an example that adds two numbers using Python.
First, create the tool as a Python script:
```python title=".opencode/tools/add.py" theme={null}
import sys
a = int(sys.argv[1])
b = int(sys.argv[2])
print(a + b)
```
Then create the tool definition that invokes it:
```ts title=".opencode/tools/python-add.ts" {10} theme={null}
import { tool } from "@opencode-ai/plugin"
import path from "path"
export default tool({
description: "Add two numbers using Python",
args: {
a: tool.schema.number().describe("First number"),
b: tool.schema.number().describe("Second number"),
},
async execute(args, context) {
const script = path.join(context.worktree, ".opencode/tools/add.py")
const result = await Bun.$`python3 ${script} ${args.a} ${args.b}`.text()
return result.trim()
},
})
```
Here we are using the [`Bun.$`](https://bun.com/docs/runtime/shell) utility to run the Python script.
### Database query tool
Create a tool that executes SQL queries:
```ts title=".opencode/tools/db-query.ts" theme={null}
import { tool } from "@opencode-ai/plugin"
import { Database } from "bun:sqlite"
import path from "path"
export default tool({
description: "Execute SQL queries on the project database",
args: {
query: tool.schema.string().describe("SQL query to execute"),
},
async execute(args, context) {
const dbPath = path.join(context.worktree, "data.db")
const db = new Database(dbPath, { readonly: true })
try {
const results = db.query(args.query).all()
return JSON.stringify(results, null, 2)
} catch (error) {
return `Error executing query: ${error.message}`
} finally {
db.close()
}
},
})
```
### API client tool
Create a tool that calls an external API:
```ts title=".opencode/tools/github-search.ts" theme={null}
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Search GitHub repositories",
args: {
query: tool.schema.string().describe("Search query"),
limit: tool.schema.number().default(5).describe("Number of results"),
},
async execute(args, context) {
context.metadata({ title: `Searching GitHub for "${args.query}"...` })
const response = await fetch(
`https://api.github.com/search/repositories?q=${encodeURIComponent(args.query)}&per_page=${args.limit}`
)
if (!response.ok) {
return `GitHub API error: ${response.status} ${response.statusText}`
}
const data = await response.json()
const repos = data.items.map((repo: any) => ({
name: repo.full_name,
description: repo.description,
stars: repo.stargazers_count,
url: repo.html_url,
}))
context.metadata({
title: `Found ${data.total_count} repositories`,
metadata: { totalCount: data.total_count }
})
return JSON.stringify(repos, null, 2)
},
})
```
### File system tool
Create a tool that performs custom file operations:
```ts title=".opencode/tools/count-lines.ts" theme={null}
import { tool } from "@opencode-ai/plugin"
import { readdir, stat } from "fs/promises"
import path from "path"
export default tool({
description: "Count total lines of code in a directory",
args: {
directory: tool.schema.string().describe("Directory to analyze"),
extensions: tool.schema.array(tool.schema.string()).default([".ts", ".js", ".tsx", ".jsx"]).describe("File extensions to include"),
},
async execute(args, context) {
const targetDir = path.isAbsolute(args.directory)
? args.directory
: path.join(context.directory, args.directory)
let totalLines = 0
let fileCount = 0
async function countDir(dir: string) {
const entries = await readdir(dir, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
await countDir(fullPath)
} else if (args.extensions.some(ext => entry.name.endsWith(ext))) {
const content = await Bun.file(fullPath).text()
const lines = content.split('\n').length
totalLines += lines
fileCount++
}
}
}
await countDir(targetDir)
return `Found ${fileCount} files with ${totalLines} total lines of code`
},
})
```
### Shell command tool
Create a tool that wraps shell commands:
```ts title=".opencode/tools/docker-ps.ts" theme={null}
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "List running Docker containers",
args: {
all: tool.schema.boolean().default(false).describe("Show all containers, not just running ones"),
},
async execute(args, context) {
const cmd = args.all ? "docker ps -a" : "docker ps"
const result = await Bun.$`${cmd}`.text()
return result
},
})
```
***
## Tool registration via plugins
You can also register tools through [plugins](./plugins.mdx) instead of separate files:
```ts title=".opencode/plugins/my-tools.ts" theme={null}
import { type Plugin, tool } from "@opencode-ai/plugin"
export const MyToolsPlugin: Plugin = async (ctx) => {
return {
tool: {
greet: tool({
description: "Greet a user",
args: {
name: tool.schema.string().describe("Name to greet"),
},
async execute(args) {
return `Hello, ${args.name}!`
},
}),
calculate: tool({
description: "Perform a calculation",
args: {
expression: tool.schema.string().describe("Math expression to evaluate"),
},
async execute(args) {
try {
const result = eval(args.expression)
return `Result: ${result}`
} catch (error) {
return `Error: ${error.message}`
}
},
}),
},
}
}
```
This approach is useful when:
* You want to group related tools together
* Your tools need shared state or initialization
* You want to conditionally register tools based on plugin configuration
***
## Best practices
### Write clear descriptions
The LLM relies on your tool's description to decide when to use it. Be specific:
```ts theme={null}
// Good
description: "Search GitHub repositories by keyword and return name, description, stars, and URL"
// Bad
description: "Search GitHub"
```
### Validate inputs
Use Zod's validation features to ensure correct inputs:
```ts theme={null}
args: {
email: tool.schema.string().email().describe("User email address"),
age: tool.schema.number().min(0).max(150).describe("User age"),
url: tool.schema.string().url().describe("Website URL"),
}
```
### Return structured output
Return well-formatted output that's easy for the LLM to parse:
```ts theme={null}
// Good: structured JSON
return JSON.stringify({
status: "success",
data: results,
count: results.length
}, null, 2)
// Also good: formatted text
return `Found ${results.length} results:\n\n${results.map(r => `- ${r.name}`).join('\n')}`
```
### Handle errors gracefully
Catch errors and return helpful messages:
```ts theme={null}
async execute(args, context) {
try {
const result = await riskyOperation(args)
return `Success: ${result}`
} catch (error) {
return `Error: ${error.message}. Please check the ${args.param} parameter.`
}
}
```
### Use context appropriately
```ts theme={null}
// Good: use context.directory for relative paths
const filePath = path.join(context.directory, args.file)
// Bad: use process.cwd()
const filePath = path.join(process.cwd(), args.file)
```
### Respect cancellation
For long-running operations, check the abort signal:
```ts theme={null}
for (const item of largeArray) {
if (context.abort.aborted) {
return "Operation cancelled by user"
}
await processItem(item)
}
```
# Enterprise
Source: https://anomalyco-opencode.mintlify.app/enterprise
Using OpenCode securely in your organization
OpenCode Enterprise is for organizations that want to ensure that their code and data never leaves their infrastructure. It can do this by using a centralized config that integrates with your SSO and internal AI gateway.
OpenCode does not store any of your code or context data.
## Getting Started
To get started with OpenCode Enterprise:
Do a trial internally with your team. OpenCode is open source and does not store any of your code or context data, so your developers can simply [get started](/introduction) and carry out a trial.
[Contact us](mailto:hello@opencode.ai) to discuss pricing and implementation options.
## Data Security
OpenCode does not store your code or context data. All processing happens locally or through direct API calls to your AI provider.
You own all code produced by OpenCode. There are no licensing restrictions or ownership claims.
### Data Handling
**OpenCode does not store your code or context data.** All processing happens locally or through direct API calls to your AI provider.
This means that as long as you are using a provider you trust, or an internal AI gateway, you can use OpenCode securely.
The only caveat here is the optional `/share` feature.
### Sharing Conversations
If a user enables the `/share` feature, the conversation and the data associated with it are sent to the service we use to host these share pages at opencode.ai.
The data is currently served through our CDN's edge network, and is cached on the edge near your users.
We recommend you disable this for your trial.
```json opencode.json theme={null}
{
"$schema": "https://opencode.ai/config.json",
"share": "disabled"
}
```
[Learn more about sharing](/share).
## Pricing
We use a per-seat model for OpenCode Enterprise. If you have your own LLM gateway, we do not charge for tokens used. For further details about pricing and implementation options, [contact us](mailto:hello@opencode.ai).
## Deployment
Once you have completed your trial and you are ready to use OpenCode at your organization, you can [contact us](mailto:hello@opencode.ai) to discuss pricing and implementation options.
Set up OpenCode to use a single central config for your entire organization that integrates with your SSO provider.
Integrate with your organization's SSO provider for authentication to obtain credentials for your internal AI gateway.
Configure OpenCode to use only your internal AI gateway and disable all other AI providers.
Self-host share pages on your infrastructure to ensure your data never leaves your organization.
### Central Config
We can set up OpenCode to use a single central config for your entire organization.
This centralized config can integrate with your SSO provider and ensures all users access only your internal AI gateway.
### SSO Integration
Through the central config, OpenCode can integrate with your organization's SSO provider for authentication.
This allows OpenCode to obtain credentials for your internal AI gateway through your existing identity management system.
### Internal AI Gateway
With the central config, OpenCode can also be configured to use only your internal AI gateway.
You can also disable all other AI providers, ensuring all requests go through your organization's approved infrastructure.
### Self-hosting
While we recommend disabling the share pages to ensure your data never leaves your organization, we can also help you self-host them on your infrastructure.
This is currently on our roadmap. If you're interested, [let us know](mailto:hello@opencode.ai).
## FAQ
OpenCode Enterprise is for organizations that want to ensure that their code and data never leaves their infrastructure. It can do this by using a centralized config that integrates with your SSO and internal AI gateway.
Simply start with an internal trial with your team. OpenCode by default does not store your code or context data, making it easy to get started.
Then [contact us](mailto:hello@opencode.ai) to discuss pricing and implementation options.
We offer per-seat enterprise pricing. If you have your own LLM gateway, we do not charge for tokens used. For further details, [contact us](mailto:hello@opencode.ai) for a custom quote based on your organization's needs.
Yes. OpenCode does not store your code or context data. All processing happens locally or through direct API calls to your AI provider. With central config and SSO integration, your data remains secure within your organization's infrastructure.
OpenCode supports private npm registries through Bun's native `.npmrc` file support. If your organization uses a private registry, such as JFrog Artifactory, Nexus, or similar, ensure developers are authenticated before running OpenCode.
To set up authentication with your private registry:
```bash theme={null}
npm login --registry=https://your-company.jfrog.io/api/npm/npm-virtual/
```
This creates `~/.npmrc` with authentication details. OpenCode will automatically pick this up.
You must be logged into the private registry before running OpenCode.
Alternatively, you can manually configure a `.npmrc` file:
```bash ~/.npmrc theme={null}
registry=https://your-company.jfrog.io/api/npm/npm-virtual/
//your-company.jfrog.io/api/npm/npm-virtual/:_authToken=${NPM_AUTH_TOKEN}
```
Developers must be logged into the private registry before running OpenCode to ensure packages can be installed from your enterprise registry.
# Formatters
Source: https://anomalyco-opencode.mintlify.app/formatters
OpenCode uses language specific formatters
OpenCode automatically formats files after they are written or edited using language-specific formatters. This ensures that the code that is generated follows the code styles of your project.
## Built-in Formatters
OpenCode comes with several built-in formatters for popular languages and frameworks. Below is a list of the formatters, supported file extensions, and commands or config options it needs.
| Formatter | Extensions | Requirements |
| -------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| air | .R | `air` command available |
| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://biomejs.dev/) | `biome.json(c)` config file |
| cargofmt | .rs | `cargo fmt` command available |
| clang-format | .c, .cpp, .h, .hpp, .ino, and [more](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` config file |
| cljfmt | .clj, .cljs, .cljc, .edn | `cljfmt` command available |
| dart | .dart | `dart` command available |
| dfmt | .d | `dfmt` command available |
| gleam | .gleam | `gleam` command available |
| gofmt | .go | `gofmt` command available |
| htmlbeautifier | .erb, .html.erb | `htmlbeautifier` command available |
| ktlint | .kt, .kts | `ktlint` command available |
| mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` command available |
| nixfmt | .nix | `nixfmt` command available |
| ocamlformat | .ml, .mli | `ocamlformat` command available and `.ocamlformat` config file |
| ormolu | .hs | `ormolu` command available |
| oxfmt (Experimental) | .js, .jsx, .ts, .tsx | `oxfmt` dependency in `package.json` and an experimental env variable flag |
| pint | .php | `laravel/pint` dependency in `composer.json` |
| prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://prettier.io/docs/en/index.html) | `prettier` dependency in `package.json` |
| rubocop | .rb, .rake, .gemspec, .ru | `rubocop` command available |
| ruff | .py, .pyi | `ruff` command available with config |
| rustfmt | .rs | `rustfmt` command available |
| shfmt | .sh, .bash | `shfmt` command available |
| standardrb | .rb, .rake, .gemspec, .ru | `standardrb` command available |
| terraform | .tf, .tfvars | `terraform` command available |
| uv | .py, .pyi | `uv` command available |
| zig | .zig, .zon | `zig` command available |
So if your project has `prettier` in your `package.json`, OpenCode will automatically use it.
## How It Works
When OpenCode writes or edits a file, it:
1. Checks the file extension against all enabled formatters.
2. Runs the appropriate formatter command on the file.
3. Applies the formatting changes automatically.
This process happens in the background, ensuring your code styles are maintained without any manual steps.
## Configure Formatters
You can customize formatters through the `formatter` section in your OpenCode config.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": {}
}
```
Each formatter configuration supports the following:
Set this to `true` to disable the formatter.
The command to run for formatting. Use `$FILE` as a placeholder for the file path.
Environment variables to set when running the formatter.
File extensions this formatter should handle.
### Disabling Formatters
To disable **all** formatters globally, set `formatter` to `false`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": false
}
```
To disable a **specific** formatter, set `disabled` to `true`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"prettier": {
"disabled": true
}
}
}
```
### Custom Formatters
You can override the built-in formatters or add new ones by specifying the command, environment variables, and file extensions:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"prettier": {
"command": ["npx", "prettier", "--write", "$FILE"],
"environment": {
"NODE_ENV": "development"
},
"extensions": [".js", ".ts", ".jsx", ".tsx"]
},
"custom-markdown-formatter": {
"command": ["deno", "fmt", "$FILE"],
"extensions": [".md"]
}
}
}
```
The **`$FILE` placeholder** in the command will be replaced with the path to the file being formatted.
## Examples
### Override Prettier Configuration
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"prettier": {
"command": ["npx", "prettier", "--write", "--single-quote", "$FILE"],
"environment": {
"NODE_ENV": "production"
}
}
}
}
```
### Add Custom Python Formatter
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"black": {
"command": ["black", "$FILE"],
"extensions": [".py"]
}
}
}
```
### Configure Multiple Formatters
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"prettier": {
"command": ["npx", "prettier", "--write", "$FILE"],
"extensions": [".js", ".ts", ".jsx", ".tsx"]
},
"gofmt": {
"command": ["gofmt", "-w", "$FILE"],
"extensions": [".go"]
},
"rustfmt": {
"command": ["rustfmt", "$FILE"],
"extensions": [".rs"]
}
}
}
```
# GitHub Integration
Source: https://anomalyco-opencode.mintlify.app/github
Use OpenCode in GitHub issues and pull requests with automated workflows.
OpenCode integrates with your GitHub workflow. Mention `/opencode` or `/oc` in your comment, and OpenCode will execute tasks within your GitHub Actions runner.
## Features
* **Triage issues**: Ask OpenCode to look into an issue and explain it to you.
* **Fix and implement**: Ask OpenCode to fix an issue or implement a feature. It will work in a new branch and submit a PR with all the changes.
* **Secure**: OpenCode runs inside your GitHub's runners.
## Installation
Run the following command in a project that is in a GitHub repo:
```bash theme={null}
opencode github install
```
This will walk you through installing the GitHub app, creating the workflow, and setting up secrets.
### Manual Setup
Or you can set it up manually:
Head over to [**github.com/apps/opencode-agent**](https://github.com/apps/opencode-agent). Make sure it's installed on the target repository.
Add the following workflow file to `.github/workflows/opencode.yml` in your repo. Make sure to set the appropriate `model` and required API keys in `env`.
```yml title=".github/workflows/opencode.yml" theme={null}
name: opencode
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
opencode:
if: |
contains(github.event.comment.body, '/oc') ||
contains(github.event.comment.body, '/opencode')
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
persist-credentials: false
- name: Run OpenCode
uses: anomalyco/opencode/github@latest
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
model: anthropic/claude-sonnet-4-20250514
# share: true
# github_token: xxxx
```
In your organization or project **settings**, expand **Secrets and variables** on the left and select **Actions**. Add the required API keys.
## Configuration
The GitHub Action supports the following configuration options:
| Option | Required | Description |
| -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | Yes | The model to use with OpenCode. Takes the format of `provider/model`. |
| `agent` | No | The agent to use. Must be a primary agent. Falls back to `default_agent` from config or `"build"` if not found. |
| `share` | No | Whether to share the OpenCode session. Defaults to **true** for public repositories. |
| `prompt` | No | Optional custom prompt to override the default behavior. Use this to customize how OpenCode processes requests. |
| `token` | No | Optional GitHub access token for performing operations such as creating comments, committing changes, and opening pull requests. By default, OpenCode uses the installation access token from the OpenCode GitHub App. Alternatively, you can use the runner's built-in `GITHUB_TOKEN` or a personal access token (PAT) if preferred. |
### Using GITHUB\_TOKEN
You can use the GitHub Action runner's [built-in `GITHUB_TOKEN`](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token) without installing the OpenCode GitHub App. Just make sure to grant the required permissions in your workflow:
```yaml theme={null}
permissions:
id-token: write
contents: write
pull-requests: write
issues: write
```
## Supported Events
OpenCode can be triggered by the following GitHub events:
| Event Type | Triggered By | Details |
| ----------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `issue_comment` | Comment on an issue or PR | Mention `/opencode` or `/oc` in your comment. OpenCode reads context and can create branches, open PRs, or reply. |
| `pull_request_review_comment` | Comment on specific code lines in a PR | Mention `/opencode` or `/oc` while reviewing code. OpenCode receives file path, line numbers, and diff context. |
| `issues` | Issue opened or edited | Automatically trigger OpenCode when issues are created or modified. Requires `prompt` input. |
| `pull_request` | PR opened or updated | Automatically trigger OpenCode when PRs are opened, synchronized, or reopened. Useful for automated reviews. |
| `schedule` | Cron-based schedule | Run OpenCode on a schedule. Requires `prompt` input. Output goes to logs and PRs (no issue to comment on). |
| `workflow_dispatch` | Manual trigger from GitHub UI | Trigger OpenCode on demand via Actions tab. Requires `prompt` input. Output goes to logs and PRs. |
### Schedule Example
Run OpenCode on a schedule to perform automated tasks:
```yaml title=".github/workflows/opencode-scheduled.yml" theme={null}
name: Scheduled OpenCode Task
on:
schedule:
- cron: "0 9 * * 1" # Every Monday at 9am UTC
jobs:
opencode:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
pull-requests: write
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Run OpenCode
uses: anomalyco/opencode/github@latest
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
model: anthropic/claude-sonnet-4-20250514
prompt: |
Review the codebase for any TODO comments and create a summary.
If you find issues worth addressing, open an issue to track them.
```
For scheduled events, the `prompt` input is **required** since there's no comment to extract instructions from.
### Pull Request Review Example
Automatically review PRs when they are opened or updated:
```yaml title=".github/workflows/opencode-review.yml" theme={null}
name: opencode-review
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
review:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: read
issues: read
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: anomalyco/opencode/github@latest
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
model: anthropic/claude-sonnet-4-20250514
use_github_token: true
prompt: |
Review this pull request:
- Check for code quality issues
- Look for potential bugs
- Suggest improvements
```
For `pull_request` events, if no `prompt` is provided, OpenCode defaults to reviewing the pull request.
### Issue Triage Example
Automatically triage new issues. This example filters to accounts older than 30 days to reduce spam:
```yaml title=".github/workflows/opencode-triage.yml" theme={null}
name: Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
pull-requests: write
issues: write
steps:
- name: Check account age
id: check
uses: actions/github-script@v7
with:
script: |
const user = await github.rest.users.getByUsername({
username: context.payload.issue.user.login
});
const created = new Date(user.data.created_at);
const days = (Date.now() - created) / (1000 * 60 * 60 * 24);
return days >= 30;
result-encoding: string
- uses: actions/checkout@v6
if: steps.check.outputs.result == 'true'
with:
persist-credentials: false
- uses: anomalyco/opencode/github@latest
if: steps.check.outputs.result == 'true'
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
model: anthropic/claude-sonnet-4-20250514
prompt: |
Review this issue. If there's a clear fix or relevant docs:
- Provide documentation links
- Add error handling guidance for code examples
Otherwise, do not comment.
```
For `issues` events, the `prompt` input is **required** since there's no comment to extract instructions from.
## Custom Prompts
Override the default prompt to customize OpenCode's behavior for your workflow:
```yaml title=".github/workflows/opencode.yml" theme={null}
- uses: anomalyco/opencode/github@latest
with:
model: anthropic/claude-sonnet-4-5
prompt: |
Review this pull request:
- Check for code quality issues
- Look for potential bugs
- Suggest improvements
```
This is useful for enforcing specific review criteria, coding standards, or focus areas relevant to your project.
## Usage Examples
Here are some examples of how you can use OpenCode in GitHub:
### Explain an Issue
Add this comment in a GitHub issue:
```
/opencode explain this issue
```
OpenCode will read the entire thread, including all comments, and reply with a clear explanation.
### Fix an Issue
In a GitHub issue, say:
```
/opencode fix this
```
And OpenCode will create a new branch, implement the changes, and open a PR with the changes.
### Review PRs and Make Changes
Leave the following comment on a GitHub PR:
```
Delete the attachment from S3 when the note is removed /oc
```
OpenCode will implement the requested change and commit it to the same PR.
### Review Specific Code Lines
Leave a comment directly on code lines in the PR's "Files" tab. OpenCode automatically detects the file, line numbers, and diff context to provide precise responses.
```
[Comment on specific lines in Files tab]
/oc add error handling here
```
When commenting on specific lines, OpenCode receives:
* The exact file being reviewed
* The specific lines of code
* The surrounding diff context
* Line number information
This allows for more targeted requests without needing to specify file paths or line numbers manually.
## FAQ
No, you can use the built-in `GITHUB_TOKEN` instead. However, the OpenCode GitHub App provides a better user experience as commits and comments appear as coming from the app rather than the GitHub Actions bot.
Yes, OpenCode works with both public and private repositories. For private repositories, session sharing is disabled by default.
Use the `prompt` configuration option to provide custom instructions for how OpenCode should process requests in your workflow.
Yes, you can create multiple workflow files or use multiple event triggers in a single workflow to run OpenCode on different events with different configurations.
At minimum, OpenCode needs `id-token: write`. For full functionality (creating PRs, commenting), it needs `contents: write`, `pull-requests: write`, and `issues: write`.
# GitLab Integration
Source: https://anomalyco-opencode.mintlify.app/gitlab
Use OpenCode in GitLab issues and merge requests through CI/CD pipeline or GitLab Duo.
OpenCode integrates with your GitLab workflow through your GitLab CI/CD pipeline or with GitLab Duo.
In both cases, OpenCode will run on your GitLab runners.
## GitLab CI
OpenCode works in a regular GitLab pipeline. You can build it into a pipeline as a [CI component](https://docs.gitlab.com/ee/ci/components/).
Here we are using a community-created CI/CD component for OpenCode — [nagyv/gitlab-opencode](https://gitlab.com/nagyv/gitlab-opencode).
### Features
* **Use custom configuration per job**: Configure OpenCode with a custom configuration directory, for example `./config/#custom-directory` to enable or disable functionality per OpenCode invocation.
* **Minimal setup**: The CI component sets up OpenCode in the background, you only need to create the OpenCode configuration and the initial prompt.
* **Flexible**: The CI component supports several inputs for customizing its behavior.
### Setup
Store your OpenCode authentication JSON as a File type CI environment variable under **Settings** > **CI/CD** > **Variables**. Make sure to mark them as "Masked and hidden".
Add the following to your `.gitlab-ci.yml` file:
```yaml title=".gitlab-ci.yml" theme={null}
include:
- component: $CI_SERVER_FQDN/nagyv/gitlab-opencode/opencode@2
inputs:
config_dir: ${CI_PROJECT_DIR}/opencode-config
auth_json: $OPENCODE_AUTH_JSON # The variable name for your OpenCode authentication JSON
command: optional-custom-command
message: "Your prompt here"
```
For more inputs and use cases [check out the docs](https://gitlab.com/explore/catalog/nagyv/gitlab-opencode) for this component.
## GitLab Duo
OpenCode integrates with your GitLab workflow through GitLab Duo.
Mention `@opencode` in a comment, and OpenCode will execute tasks within your GitLab CI pipeline.
### Features
* **Triage issues**: Ask OpenCode to look into an issue and explain it to you.
* **Fix and implement**: Ask OpenCode to fix an issue or implement a feature. It will create a new branch and raise a merge request with the changes.
* **Secure**: OpenCode runs on your GitLab runners.
### Setup
OpenCode runs in your GitLab CI/CD pipeline, here's what you'll need to set it up:
Check out the [**GitLab docs**](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) for up to date instructions.
Set up your GitLab environment to support agent assistants.
Configure your GitLab CI/CD pipeline with the necessary jobs.
Obtain an API key from your AI model provider (e.g., Anthropic).
Create a GitLab service account with appropriate permissions.
Store your API keys and tokens as CI/CD variables in your GitLab project settings.
Create a flow configuration file for OpenCode. Here's an example:
```yaml theme={null}
image: node:22-slim
commands:
- echo "Installing opencode"
- npm install --global opencode-ai
- echo "Installing glab"
- export GITLAB_TOKEN=$GITLAB_TOKEN_OPENCODE
- apt-get update --quiet && apt-get install --yes curl wget gpg git && rm --recursive --force /var/lib/apt/lists/*
- curl --silent --show-error --location "https://raw.githubusercontent.com/upciti/wakemeops/main/assets/install_repository" | bash
- apt-get install --yes glab
- echo "Configuring glab"
- echo $GITLAB_HOST
- echo "Creating OpenCode auth configuration"
- mkdir --parents ~/.local/share/opencode
- |
cat > ~/.local/share/opencode/auth.json << EOF
{
"anthropic": {
"type": "api",
"key": "$ANTHROPIC_API_KEY"
}
}
EOF
- echo "Configuring git"
- git config --global user.email "opencode@gitlab.com"
- git config --global user.name "OpenCode"
- echo "Testing glab"
- glab issue list
- echo "Running OpenCode"
- |
opencode run "
You are an AI assistant helping with GitLab operations.
Context: $AI_FLOW_CONTEXT
Task: $AI_FLOW_INPUT
Event: $AI_FLOW_EVENT
Please execute the requested task using the available GitLab tools.
Be thorough in your analysis and provide clear explanations.
Please use the glab CLI to access data from GitLab. The glab CLI has already been authenticated. You can run the corresponding commands.
If you are asked to summarize an MR or issue or asked to provide more information then please post back a note to the MR/Issue so that the user can see it.
You don't need to commit or push up changes, those will be done automatically based on the file changes you make.
"
- git checkout --branch $CI_WORKLOAD_REF origin/$CI_WORKLOAD_REF
- echo "Checking for git changes and pushing if any exist"
- |
if ! git diff --quiet || ! git diff --cached --quiet || [ --not --zero "$(git ls-files --others --exclude-standard)" ]; then
echo "Git changes detected, adding and pushing..."
git add .
if git diff --cached --quiet; then
echo "No staged changes to commit"
else
echo "Committing changes to branch: $CI_WORKLOAD_REF"
git commit --message "Codex changes"
echo "Pushing changes up to $CI_WORKLOAD_REF"
git push https://gitlab-ci-token:$GITLAB_TOKEN@$GITLAB_HOST/gl-demo-ultimate-dev-ai-epic-17570/test-java-project.git $CI_WORKLOAD_REF
echo "Changes successfully pushed"
fi
else
echo "No git changes detected, skipping push"
fi
variables:
- ANTHROPIC_API_KEY
- GITLAB_TOKEN_OPENCODE
- GITLAB_HOST
```
You can refer to the [GitLab CLI agents docs](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) for detailed instructions.
## Usage Examples
Here are some examples of how you can use OpenCode in GitLab.
You can configure to use a different trigger phrase than `@opencode`.
### Explain an Issue
Add this comment in a GitLab issue:
```
@opencode explain this issue
```
OpenCode will read the issue and reply with a clear explanation.
### Fix an Issue
In a GitLab issue, say:
```
@opencode fix this
```
OpenCode will create a new branch, implement the changes, and open a merge request with the changes.
### Review Merge Requests
Leave the following comment on a GitLab merge request:
```
@opencode review this merge request
```
OpenCode will review the merge request and provide feedback.
## FAQ
Yes, OpenCode works with both GitLab.com and self-hosted GitLab instances.
No, you can use OpenCode through the GitLab CI component without GitLab Duo. However, GitLab Duo provides a more integrated experience with `@opencode` mentions.
Yes, OpenCode works with both public and private repositories on GitLab.
You can customize the trigger phrase in your flow configuration file. Change `@opencode` to any other trigger phrase you prefer.
The service account needs permissions to read repository content, create branches, create merge requests, and comment on issues and merge requests.
# IDE Integration
Source: https://anomalyco-opencode.mintlify.app/ide
The OpenCode extension for VS Code, Cursor, and other IDEs.
OpenCode integrates with VS Code, Cursor, or any IDE that supports a terminal. Just run `opencode` in the terminal to get started.
## Features
* **Quick Launch**: Use keyboard shortcuts to open OpenCode instantly in a split terminal view
* **New Session**: Start multiple OpenCode sessions with dedicated shortcuts
* **Context Awareness**: Automatically share your current selection or tab with OpenCode
* **File Reference Shortcuts**: Quickly insert file references into OpenCode prompts
## Usage
### Keyboard Shortcuts
Use `Cmd+Esc` (Mac) or `Ctrl+Esc` (Windows/Linux) to open OpenCode in a split terminal view, or focus an existing terminal session if one is already running.
Use `Cmd+Shift+Esc` (Mac) or `Ctrl+Shift+Esc` (Windows/Linux) to start a new OpenCode terminal session, even if one is already open. You can also click the OpenCode button in the UI.
Use `Cmd+Option+K` (Mac) or `Alt+Ctrl+K` (Linux/Windows) to insert file references. For example, `@File#L37-42`.
### Context Awareness
OpenCode automatically shares your current selection or active tab with the AI, providing relevant context for your requests without manual copying and pasting.
## Installation
To install OpenCode on VS Code and popular forks like Cursor, Windsurf, VSCodium:
Launch your IDE (VS Code, Cursor, Windsurf, or VSCodium).
Open the integrated terminal in your IDE.
Run `opencode` - the extension installs automatically.
If on the other hand you want to use your own IDE when you run `/editor` or `/export` from the TUI, you'll need to set `export EDITOR="code --wait"`. [Learn more](/tui#editor-setup).
### Manual Install
Search for **OpenCode** in the Extension Marketplace and click **Install**.
### Supported IDEs
OpenCode supports the following IDEs:
* **VS Code** - The standard Visual Studio Code editor
* **Cursor** - AI-first code editor built on VS Code
* **Windsurf** - Collaborative code editor
* **VSCodium** - Open-source builds of VS Code without Microsoft branding/telemetry
All VS Code-compatible IDEs that support extensions should work with OpenCode.
## Troubleshooting
If the extension fails to install automatically:
Ensure you're running `opencode` in the integrated terminal, not an external terminal.
Confirm the CLI for your IDE is installed:
* For VS Code: `code` command
* For Cursor: `cursor` command
* For Windsurf: `windsurf` command
* For VSCodium: `codium` command
If not, run `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) and search for "Shell Command: Install 'code' command in PATH" (or the equivalent for your IDE).
Ensure VS Code has permission to install extensions. Some organizations restrict extension installation.
## Editor Setup for TUI
If you want to use a specific editor when running `/editor` or `/export` from the OpenCode TUI, set the `EDITOR` environment variable:
```bash theme={null}
export EDITOR="code --wait"
```
For other editors:
```bash theme={null}
# For Cursor
export EDITOR="cursor --wait"
# For Vim
export EDITOR="vim"
# For Nano
export EDITOR="nano"
# For Sublime Text
export EDITOR="subl --wait"
```
The `--wait` flag tells the editor to wait until the file is closed before returning control to OpenCode.
## FAQ
Yes, you can run `opencode` in any terminal. The extension provides additional features like keyboard shortcuts and context awareness, but it's not required.
OpenCode works in any terminal, so you can use it with any IDE that has terminal support. The VS Code extension provides enhanced integration for VS Code and compatible editors.
Yes, you can customize keyboard shortcuts in your IDE's keyboard shortcuts settings. Search for "OpenCode" in the keyboard shortcuts editor.
The extension updates automatically through your IDE's extension marketplace. You can also manually check for updates in the Extensions view.
Yes, OpenCode works in remote development environments like GitHub Codespaces, GitPod, and VS Code Remote containers.
# Installation
Source: https://anomalyco-opencode.mintlify.app/installation
Install OpenCode on any platform
OpenCode can be installed on macOS, Linux, and Windows using multiple package managers and installation methods.
## Quick Install
The fastest way to install OpenCode is using the installation script:
```bash theme={null}
curl -fsSL https://opencode.ai/install | bash
```
This script automatically detects your platform and installs the latest version.
## Prerequisites
Before installing OpenCode, ensure you have:
1. **A modern terminal emulator** for the best experience:
* [WezTerm](https://wezterm.org) - Cross-platform, highly recommended
* [Alacritty](https://alacritty.org) - Cross-platform, GPU-accelerated
* [Ghostty](https://ghostty.org) - Linux and macOS
* [Kitty](https://sw.kovidgoyal.net/kitty/) - Linux and macOS
2. **API keys** for your preferred LLM providers (configured after installation)
## Installation Methods
### Homebrew (Recommended)
Install from the OpenCode tap for the most up-to-date releases:
```bash theme={null}
brew install anomalyco/tap/opencode
```
Or use the official Homebrew formula (updated less frequently):
```bash theme={null}
brew install opencode
```
### Node.js Package Managers
```bash npm theme={null}
npm install -g opencode-ai
```
```bash pnpm theme={null}
pnpm install -g opencode-ai
```
```bash yarn theme={null}
yarn global add opencode-ai
```
```bash bun theme={null}
bun install -g opencode-ai
```
### Mise
```bash theme={null}
mise use -g github:anomalyco/opencode
```
### Nix
```bash theme={null}
nix run nixpkgs#opencode
```
For the latest development version:
```bash theme={null}
nix run github:anomalyco/opencode
```
### Homebrew
```bash theme={null}
brew install anomalyco/tap/opencode
```
### Arch Linux
```bash Stable theme={null}
sudo pacman -S opencode
```
```bash AUR (Latest) theme={null}
paru -S opencode-bin
```
### Node.js Package Managers
```bash npm theme={null}
npm install -g opencode-ai
```
```bash pnpm theme={null}
pnpm install -g opencode-ai
```
```bash yarn theme={null}
yarn global add opencode-ai
```
```bash bun theme={null}
bun install -g opencode-ai
```
### Mise
```bash theme={null}
mise use -g github:anomalyco/opencode
```
### Nix
```bash theme={null}
nix run nixpkgs#opencode
```
For the best experience on Windows, we recommend using [Windows Subsystem for Linux (WSL)](/windows-wsl). It provides better performance and full compatibility with OpenCode's features.
### Chocolatey
```bash theme={null}
choco install opencode
```
### Scoop
```bash theme={null}
scoop install opencode
```
### Node.js Package Managers
```bash npm theme={null}
npm install -g opencode-ai
```
```bash pnpm theme={null}
pnpm install -g opencode-ai
```
```bash yarn theme={null}
yarn global add opencode-ai
```
Bun support on Windows is currently in progress.
### Mise
```bash theme={null}
mise use -g github:anomalyco/opencode
```
### Run with Docker
```bash theme={null}
docker run -it --rm ghcr.io/anomalyco/opencode
```
### Mount Your Project
```bash theme={null}
docker run -it --rm -v $(pwd):/workspace ghcr.io/anomalyco/opencode
```
Use Docker when you want an isolated environment or need to run OpenCode on a system without installing dependencies.
## Desktop App (Beta)
OpenCode is also available as a desktop application with a native GUI experience.
### Download
Download directly from the [releases page](https://github.com/anomalyco/opencode/releases) or [opencode.ai/download](https://opencode.ai/download).
| Platform | Download File |
| --------------------- | ------------------------------------- |
| macOS (Apple Silicon) | `opencode-desktop-darwin-aarch64.dmg` |
| macOS (Intel) | `opencode-desktop-darwin-x64.dmg` |
| Windows | `opencode-desktop-windows-x64.exe` |
| Linux (Debian/Ubuntu) | `opencode-desktop-linux-x64.deb` |
| Linux (Fedora/RHEL) | `opencode-desktop-linux-x64.rpm` |
| Linux (AppImage) | `opencode-desktop-linux-x64.AppImage` |
### Install via Package Manager
```bash macOS (Homebrew) theme={null}
brew install --cask opencode-desktop
```
```bash Windows (Scoop) theme={null}
scoop bucket add extras
scoop install extras/opencode-desktop
```
## Custom Installation Directory
The installation script respects environment variables for custom installation paths:
```bash theme={null}
# Custom directory
OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash
# XDG compliant path
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
```
### Priority Order
1. `$OPENCODE_INSTALL_DIR` - Custom installation directory
2. `$XDG_BIN_DIR` - XDG Base Directory Specification path
3. `$HOME/bin` - Standard user binary directory
4. `$HOME/.opencode/bin` - Default fallback
## Manual Download
Grab the binary directly from the [GitHub Releases](https://github.com/anomalyco/opencode/releases) page:
1. Download the appropriate binary for your platform
2. Extract the archive
3. Move the binary to a directory in your `$PATH`
4. Make it executable: `chmod +x opencode`
## Verify Installation
Confirm OpenCode is installed correctly:
```bash theme={null}
opencode --version
```
You should see the version number printed to the console.
## Upgrading
```bash theme={null}
brew upgrade opencode
```
```bash theme={null}
npm update -g opencode-ai
```
```bash theme={null}
choco upgrade opencode
```
```bash theme={null}
scoop update opencode
```
Remove versions older than 0.1.x before installing or upgrading to avoid compatibility issues.
## Uninstalling
To remove OpenCode from your system:
```bash theme={null}
brew uninstall opencode
```
```bash theme={null}
npm uninstall -g opencode-ai
```
```bash theme={null}
choco uninstall opencode
```
```bash theme={null}
scoop uninstall opencode
```
Remove configuration files:
```bash theme={null}
rm -rf ~/.config/opencode
rm -rf ~/.opencode
```
## Next Steps
Configure OpenCode and start your first session
Customize OpenCode to match your workflow
# Welcome to OpenCode
Source: https://anomalyco-opencode.mintlify.app/introduction
The open source AI coding agent
OpenCode is an open source AI coding agent that accelerates your software development workflow. Available as a terminal-based interface, desktop app, or IDE extension, it brings AI assistance directly into your development environment.
OpenCode is 100% open source and works with multiple LLM providers including Claude, OpenAI, Google, and local models.
## Why OpenCode?
Not locked into any single provider. Use Claude, OpenAI, Google, or local models. Switch providers anytime.
Built by terminal enthusiasts. Optimized for modern terminals with full keyboard navigation and customization.
Out-of-the-box Language Server Protocol support provides intelligent code understanding and context.
Client/server design enables remote operation. Run on your machine, control from anywhere.
## Key Features
### Intelligent Agents
OpenCode includes specialized agents for different tasks:
* **Build Agent** - Full development with all tools enabled
* **Plan Agent** - Analysis and planning without making changes
* **General Subagent** - Complex research and parallel tasks
* **Explore Subagent** - Fast, read-only codebase exploration
Switch between agents with the **Tab** key or invoke them via `@` mentions.
### Powerful Tools
Read, write, and edit files with context-aware assistance. Supports glob patterns and content search.
Execute commands directly through the terminal. Full bash command support.
Create reusable commands for repetitive tasks. Use `/command` syntax to run them instantly.
Extend functionality with Model Context Protocol servers. Connect to databases, APIs, and more.
### Collaborative Features
**Share Conversations**: Share your OpenCode sessions with your team using the `/share` command. Conversations are private by default.
**Version Control Integration**: Built-in Git support makes it easy to track changes, create commits, and manage branches.
**Undo/Redo**: Made a mistake? Use `/undo` to revert changes instantly. Redo them with `/redo`.
## Workflow Patterns
### Plan, Review, Build
Press **Tab** to enter Plan mode. Describe your feature or change.
OpenCode creates a detailed implementation plan without touching code.
Press **Tab** again to switch to Build mode. Say "go ahead" to implement.
### Ask Questions
Use the `@` key to fuzzy search and reference files:
```txt theme={null}
How is authentication handled in @src/auth/index.ts
```
### Add Features
Provide context and examples for best results:
```txt theme={null}
Add a delete confirmation modal. Use the same pattern as @src/modals/ConfirmModal.tsx
but customize the message and button styles.
```
### Make Changes
Direct requests work great for straightforward modifications:
```txt theme={null}
Update the API endpoint in @config/api.ts to use the production URL
```
## Customization
Choose from multiple built-in themes or create your own color schemes.
Customize keyboard shortcuts to match your workflow and muscle memory.
Configure code formatters like Prettier, ESLint, or language-specific tools.
## What's Next?
Install OpenCode using your preferred package manager
Get your first session running in under 5 minutes
New to AI coding agents? Check out the [Quick Start guide](/quickstart) for a hands-on tutorial.
# Keybinds
Source: https://anomalyco-opencode.mintlify.app/keybinds
Customize your keybinds
OpenCode has a list of keybinds that you can customize through the OpenCode config.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"keybinds": {
"leader": "ctrl+x",
"app_exit": "ctrl+c,ctrl+d,q",
"editor_open": "e",
"theme_list": "t",
"sidebar_toggle": "b",
"scrollbar_toggle": "none",
"username_toggle": "none",
"status_view": "s",
"tool_details": "none",
"session_export": "x",
"session_new": "n",
"session_list": "l",
"session_timeline": "g",
"session_fork": "none",
"session_rename": "none",
"session_share": "none",
"session_unshare": "none",
"session_interrupt": "escape",
"session_compact": "c",
"session_child_cycle": "right",
"session_child_cycle_reverse": "left",
"session_parent": "up",
"messages_page_up": "pageup,ctrl+alt+b",
"messages_page_down": "pagedown,ctrl+alt+f",
"messages_line_up": "ctrl+alt+y",
"messages_line_down": "ctrl+alt+e",
"messages_half_page_up": "ctrl+alt+u",
"messages_half_page_down": "ctrl+alt+d",
"messages_first": "ctrl+g,home",
"messages_last": "ctrl+alt+g,end",
"messages_next": "none",
"messages_previous": "none",
"messages_copy": "y",
"messages_undo": "u",
"messages_redo": "r",
"messages_last_user": "none",
"messages_toggle_conceal": "h",
"model_list": "m",
"model_cycle_recent": "f2",
"model_cycle_recent_reverse": "shift+f2",
"model_cycle_favorite": "none",
"model_cycle_favorite_reverse": "none",
"variant_cycle": "ctrl+t",
"command_list": "ctrl+p",
"agent_list": "a",
"agent_cycle": "tab",
"agent_cycle_reverse": "shift+tab",
"input_clear": "ctrl+c",
"input_paste": "ctrl+v",
"input_submit": "return",
"input_newline": "shift+return,ctrl+return,alt+return,ctrl+j",
"input_move_left": "left,ctrl+b",
"input_move_right": "right,ctrl+f",
"input_move_up": "up",
"input_move_down": "down",
"input_select_left": "shift+left",
"input_select_right": "shift+right",
"input_select_up": "shift+up",
"input_select_down": "shift+down",
"input_line_home": "ctrl+a",
"input_line_end": "ctrl+e",
"input_select_line_home": "ctrl+shift+a",
"input_select_line_end": "ctrl+shift+e",
"input_visual_line_home": "alt+a",
"input_visual_line_end": "alt+e",
"input_select_visual_line_home": "alt+shift+a",
"input_select_visual_line_end": "alt+shift+e",
"input_buffer_home": "home",
"input_buffer_end": "end",
"input_select_buffer_home": "shift+home",
"input_select_buffer_end": "shift+end",
"input_delete_line": "ctrl+shift+d",
"input_delete_to_line_end": "ctrl+k",
"input_delete_to_line_start": "ctrl+u",
"input_backspace": "backspace,shift+backspace",
"input_delete": "ctrl+d,delete,shift+delete",
"input_undo": "ctrl+-,super+z",
"input_redo": "ctrl+.,super+shift+z",
"input_word_forward": "alt+f,alt+right,ctrl+right",
"input_word_backward": "alt+b,alt+left,ctrl+left",
"input_select_word_forward": "alt+shift+f,alt+shift+right",
"input_select_word_backward": "alt+shift+b,alt+shift+left",
"input_delete_word_forward": "alt+d,alt+delete,ctrl+delete",
"input_delete_word_backward": "ctrl+w,ctrl+backspace,alt+backspace",
"history_previous": "up",
"history_next": "down",
"terminal_suspend": "ctrl+z",
"terminal_title_toggle": "none",
"tips_toggle": "h",
"display_thinking": "none"
}
}
```
## Leader Key
OpenCode uses a `leader` key for most keybinds. This avoids conflicts in your terminal.
By default, `ctrl+x` is the leader key and most actions require you to first press the leader key and then the shortcut. For example, to start a new session you first press `ctrl+x` and then press `n`.
You don't need to use a leader key for your keybinds but we recommend doing so.
## Available Keybinds
### Application
Leader key that prefixes most other keybinds.
Exit the application.
### Editor & UI
Open the editor.
Open the theme selection list.
Toggle the sidebar visibility.
Toggle the scrollbar visibility.
Toggle username display.
View status information.
Show tool details.
### Session Management
Export the current session.
Create a new session.
Show the session list.
View session timeline.
Fork the current session.
Rename the current session.
Share the current session.
Unshare the current session.
Interrupt the current session.
Compact the session context.
Cycle to next child session.
Cycle to previous child session.
Go to parent session.
### Messages Navigation
Scroll messages one page up.
Scroll messages one page down.
Scroll messages one line up.
Scroll messages one line down.
Scroll messages half page up.
Scroll messages half page down.
Jump to first message.
Jump to last message.
Go to next message.
Go to previous message.
Copy message content.
Undo last message action.
Redo message action.
Jump to last user message.
Toggle concealing of message details.
### Model & Agent
Open model selection list.
Cycle through recently used models.
Cycle through recently used models in reverse.
Cycle through favorite models.
Cycle through favorite models in reverse.
Cycle through model variants.
Open command list.
Open agent selection list.
Cycle to next agent.
Cycle to previous agent.
### Input Control
Clear input field.
Paste into input field.
Submit input.
Insert newline in input.
Move cursor left.
Move cursor right.
Move cursor up.
Move cursor down.
Select text to the left.
Select text to the right.
Select text upward.
Select text downward.
Move to start of line.
Move to end of line.
Select to start of line.
Select to end of line.
Move to visual start of line.
Move to visual end of line.
Select to visual start of line.
Select to visual end of line.
Move to start of buffer.
Move to end of buffer.
Select to start of buffer.
Select to end of buffer.
Delete entire line.
Delete from cursor to end of line.
Delete from cursor to start of line.
Delete character before cursor.
Delete character at cursor.
Undo input change.
Redo input change.
Move forward one word.
Move backward one word.
Select forward one word.
Select backward one word.
Delete word forward.
Delete word backward.
### History
Go to previous history item.
Go to next history item.
### Terminal
Suspend the terminal.
Toggle terminal title display.
### Other
Toggle tips display.
Display model thinking process.
## Disable Keybind
You can disable a keybind by adding the key to your config with a value of "none".
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"keybinds": {
"session_compact": "none"
}
}
```
## Desktop Prompt Shortcuts
The OpenCode desktop app prompt input supports common Readline/Emacs-style shortcuts for editing text. These are built-in and currently not configurable via `opencode.json`.
| Shortcut | Action |
| -------- | ---------------------------------------- |
| `ctrl+a` | Move to start of current line |
| `ctrl+e` | Move to end of current line |
| `ctrl+b` | Move cursor back one character |
| `ctrl+f` | Move cursor forward one character |
| `alt+b` | Move cursor back one word |
| `alt+f` | Move cursor forward one word |
| `ctrl+d` | Delete character under cursor |
| `ctrl+k` | Kill to end of line |
| `ctrl+u` | Kill to start of line |
| `ctrl+w` | Kill previous word |
| `alt+d` | Kill next word |
| `ctrl+t` | Transpose characters |
| `ctrl+g` | Cancel popovers / abort running response |
## Shift+Enter
Some terminals don't send modifier keys with Enter by default. You may need to configure your terminal to send `Shift+Enter` as an escape sequence.
### Windows Terminal
Open your `settings.json` at:
```
%LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json
```
Add this to the root-level `actions` array:
```json theme={null}
"actions": [
{
"command": {
"action": "sendInput",
"input": "\u001b[13;2u"
},
"id": "User.sendInput.ShiftEnterCustom"
}
]
```
Add this to the root-level `keybindings` array:
```json theme={null}
"keybindings": [
{
"keys": "shift+enter",
"id": "User.sendInput.ShiftEnterCustom"
}
]
```
Save the file and restart Windows Terminal or open a new tab.
# LSP Servers
Source: https://anomalyco-opencode.mintlify.app/lsp
OpenCode integrates with Language Server Protocol (LSP) servers to provide intelligent code analysis and diagnostics.
OpenCode integrates with your Language Server Protocol (LSP) servers to help the LLM interact with your codebase. It uses diagnostics to provide feedback to the LLM, enabling intelligent code understanding and error detection.
## Built-in LSP Servers
OpenCode comes with several built-in LSP servers for popular languages:
| LSP Server | Extensions | Requirements |
| ------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------ |
| astro | .astro | Auto-installs for Astro projects |
| bash | .sh, .bash, .zsh, .ksh | Auto-installs bash-language-server |
| clangd | .c, .cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++ | Auto-installs for C/C++ projects |
| csharp | .cs | `.NET SDK` installed |
| clojure-lsp | .clj, .cljs, .cljc, .edn | `clojure-lsp` command available |
| dart | .dart | `dart` command available |
| deno | .ts, .tsx, .js, .jsx, .mjs | `deno` command available (auto-detects deno.json/deno.jsonc) |
| elixir-ls | .ex, .exs | `elixir` command available |
| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue | `eslint` dependency in project |
| fsharp | .fs, .fsi, .fsx, .fsscript | `.NET SDK` installed |
| gleam | .gleam | `gleam` command available |
| gopls | .go | `go` command available |
| hls | .hs, .lhs | `haskell-language-server-wrapper` command available |
| jdtls | .java | `Java SDK (version 21+)` installed |
| julials | .jl | `julia` and `LanguageServer.jl` installed |
| kotlin-ls | .kt, .kts | Auto-installs for Kotlin projects |
| lua-ls | .lua | Auto-installs for Lua projects |
| nixd | .nix | `nixd` command available |
| ocaml-lsp | .ml, .mli | `ocamllsp` command available |
| oxlint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue, .astro, .svelte | `oxlint` dependency in project |
| php intelephense | .php | Auto-installs for PHP projects |
| prisma | .prisma | `prisma` command available |
| pyright | .py, .pyi | `pyright` dependency installed |
| ruby-lsp (rubocop) | .rb, .rake, .gemspec, .ru | `ruby` and `gem` commands available |
| rust | .rs | `rust-analyzer` command available |
| sourcekit-lsp | .swift, .objc, .objcpp | `swift` installed (`xcode` on macOS) |
| svelte | .svelte | Auto-installs for Svelte projects |
| terraform | .tf, .tfvars | Auto-installs from GitHub releases |
| tinymist | .typ, .typc | Auto-installs from GitHub releases |
| typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `typescript` dependency in project |
| vue | .vue | Auto-installs for Vue projects |
| yaml-ls | .yaml, .yml | Auto-installs Red Hat yaml-language-server |
| zls | .zig, .zon | `zig` command available |
LSP servers are automatically enabled when one of the above file extensions are detected and the requirements are met.
You can disable automatic LSP server downloads by setting the `OPENCODE_DISABLE_LSP_DOWNLOAD` environment variable to `true`.
## How It Works
When OpenCode opens a file, it:
Checks the file extension against all enabled LSP servers.
Starts the appropriate LSP server if not already running.
The LSP server provides real-time diagnostics and code intelligence to the LLM.
## Configuration
You can customize LSP servers through the `lsp` section in your OpenCode config.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"lsp": {}
}
```
Each LSP server supports the following properties:
| Property | Type | Description |
| ---------------- | --------- | ------------------------------------------------- |
| `disabled` | boolean | Set this to `true` to disable the LSP server |
| `command` | string\[] | The command to start the LSP server |
| `extensions` | string\[] | File extensions this LSP server should handle |
| `env` | object | Environment variables to set when starting server |
| `initialization` | object | Initialization options to send to the LSP server |
### Environment Variables
Use the `env` property to set environment variables when starting the LSP server:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"rust": {
"env": {
"RUST_LOG": "debug"
}
}
}
}
```
### Initialization Options
Use the `initialization` property to pass initialization options to the LSP server. These are server-specific settings sent during the LSP `initialize` request:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"typescript": {
"initialization": {
"preferences": {
"importModuleSpecifierPreference": "relative"
}
}
}
}
}
```
Initialization options vary by LSP server. Check your LSP server's documentation for available options.
### Disabling LSP Servers
To disable **all** LSP servers globally, set `lsp` to `false`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"lsp": false
}
```
To disable a **specific** LSP server, set `disabled` to `true`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"typescript": {
"disabled": true
}
}
}
```
### Custom LSP Servers
You can add custom LSP servers by specifying the command and file extensions:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"custom-lsp": {
"command": ["custom-lsp-server", "--stdio"],
"extensions": [".custom"]
}
}
}
```
## Additional Information
### PHP Intelephense
PHP Intelephense offers premium features through a license key. You can provide a license key by placing (only) the key in a text file at:
* On macOS/Linux: `$HOME/intelephense/license.txt`
* On Windows: `%USERPROFILE%/intelephense/license.txt`
The file should contain only the license key with no additional content.
## FAQ
You can check the status of LSP servers by examining the diagnostics in your OpenCode session. Active LSP servers will provide real-time feedback on errors and warnings in your code.
Yes, OpenCode can run multiple LSP servers for the same file if their extension patterns match. For example, both `typescript` and `eslint` can run for `.ts` files.
If an LSP server fails to start, OpenCode will log the error and mark the server as broken for that session. You can check your configuration and requirements to troubleshoot the issue.
Set the `OPENCODE_DISABLE_LSP_DOWNLOAD` environment variable to `true`. You'll need to manually install any required LSP servers.
# MCP Servers
Source: https://anomalyco-opencode.mintlify.app/mcp-servers
Add local and remote MCP tools to extend OpenCode's capabilities using the Model Context Protocol.
You can add external tools to OpenCode using the *Model Context Protocol*, or MCP. OpenCode supports both local and remote servers.
Once added, MCP tools are automatically available to the LLM alongside built-in tools.
MCP servers add to your context, so you want to be careful with which ones you enable. Certain MCP servers, like the GitHub MCP server, tend to add a lot of tokens and can easily exceed the context limit.
## Enable MCP Servers
You can define MCP servers in your [OpenCode Config](https://opencode.ai/config) under `mcp`. Add each MCP with a unique name. You can refer to that MCP by name when prompting the LLM.
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"name-of-mcp-server": {
// ...
"enabled": true
},
"name-of-other-mcp-server": {
// ...
}
}
}
```
You can also disable a server by setting `enabled` to `false`. This is useful if you want to temporarily disable a server without removing it from your config.
### Overriding Remote Defaults
Organizations can provide default MCP servers via their `.well-known/opencode` endpoint. These servers may be disabled by default, allowing users to opt-in to the ones they need.
To enable a specific server from your organization's remote config, add it to your local config with `enabled: true`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"jira": {
"type": "remote",
"url": "https://jira.example.com/mcp",
"enabled": true
}
}
}
```
Your local config values override the remote defaults. See [config precedence](/config#precedence-order) for more details.
## Local MCP Servers
Add local MCP servers using `type` set to `"local"` within the MCP object.
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-local-mcp-server": {
"type": "local",
// Or ["bun", "x", "my-mcp-command"]
"command": ["npx", "-y", "my-mcp-command"],
"enabled": true,
"environment": {
"MY_ENV_VAR": "my_env_var_value"
}
}
}
}
```
The command is how the local MCP server is started. You can also pass in a list of environment variables as well.
For example, here's how you can add the test [`@modelcontextprotocol/server-everything`](https://www.npmjs.com/package/@modelcontextprotocol/server-everything) MCP server:
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"mcp_everything": {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-everything"]
}
}
}
```
And to use it I can add `use the mcp_everything tool` to my prompts.
```txt theme={null}
use the mcp_everything tool to add the number 3 and 4
```
### Local MCP Options
Here are all the options for configuring a local MCP server:
| Option | Type | Required | Description |
| ------------- | ------- | -------- | ----------------------------------------------------------------------------------- |
| `type` | String | Yes | Type of MCP server connection, must be `"local"`. |
| `command` | Array | Yes | Command and arguments to run the MCP server. |
| `environment` | Object | No | Environment variables to set when running the server. |
| `enabled` | Boolean | No | Enable or disable the MCP server on startup. |
| `timeout` | Number | No | Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds). |
## Remote MCP Servers
Add remote MCP servers by setting `type` to `"remote"`.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-remote-mcp": {
"type": "remote",
"url": "https://my-mcp-server.com",
"enabled": true,
"headers": {
"Authorization": "Bearer MY_API_KEY"
}
}
}
}
```
The `url` is the URL of the remote MCP server and with the `headers` option you can pass in a list of headers.
### Remote MCP Options
| Option | Type | Required | Description |
| --------- | ------- | -------- | ----------------------------------------------------------------------------------- |
| `type` | String | Yes | Type of MCP server connection, must be `"remote"`. |
| `url` | String | Yes | URL of the remote MCP server. |
| `enabled` | Boolean | No | Enable or disable the MCP server on startup. |
| `headers` | Object | No | Headers to send with the request. |
| `oauth` | Object | No | OAuth authentication configuration. See [OAuth](#oauth-authentication) section. |
| `timeout` | Number | No | Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds). |
## OAuth Authentication
OpenCode automatically handles OAuth authentication for remote MCP servers. When a server requires authentication, OpenCode will:
Detect the 401 response and initiate the OAuth flow.
Use **Dynamic Client Registration (RFC 7591)** if supported by the server.
Store tokens securely for future requests in `~/.local/share/opencode/mcp-auth.json`.
### Automatic OAuth
For most OAuth-enabled MCP servers, no special configuration is needed. Just configure the remote server:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-oauth-server": {
"type": "remote",
"url": "https://mcp.example.com/mcp"
}
}
}
```
If the server requires authentication, OpenCode will prompt you to authenticate when you first try to use it. If not, you can [manually trigger the flow](#authenticating) with `opencode mcp auth `.
### Pre-registered OAuth Clients
If you have client credentials from the MCP server provider, you can configure them:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-oauth-server": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"oauth": {
"clientId": "{env:MY_MCP_CLIENT_ID}",
"clientSecret": "{env:MY_MCP_CLIENT_SECRET}",
"scope": "tools:read tools:execute"
}
}
}
}
```
### Authenticating
You can manually trigger authentication or manage credentials.
Authenticate with a specific MCP server:
```bash theme={null}
opencode mcp auth my-oauth-server
```
List all MCP servers and their auth status:
```bash theme={null}
opencode mcp list
```
Remove stored credentials:
```bash theme={null}
opencode mcp logout my-oauth-server
```
The `mcp auth` command will open your browser for authorization. After you authorize, OpenCode will store the tokens securely.
### Disabling OAuth
If you want to disable automatic OAuth for a server (e.g., for servers that use API keys instead), set `oauth` to `false`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-api-key-server": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"oauth": false,
"headers": {
"Authorization": "Bearer {env:MY_API_KEY}"
}
}
}
}
```
### OAuth Options
| Option | Type | Description |
| -------------- | --------------- | -------------------------------------------------------------------------------- |
| `oauth` | Object \| false | OAuth config object, or `false` to disable OAuth auto-detection. |
| `clientId` | String | OAuth client ID. If not provided, dynamic client registration will be attempted. |
| `clientSecret` | String | OAuth client secret, if required by the authorization server. |
| `scope` | String | OAuth scopes to request during authorization. |
### Debugging OAuth
If a remote MCP server is failing to authenticate, you can diagnose issues with:
```bash theme={null}
# View auth status for all OAuth-capable servers
opencode mcp auth list
# Debug connection and OAuth flow for a specific server
opencode mcp debug my-oauth-server
```
The `mcp debug` command shows the current auth status, tests HTTP connectivity, and attempts the OAuth discovery flow.
## Managing MCP Tools
Your MCPs are available as tools in OpenCode, alongside built-in tools. So you can manage them through the OpenCode config like any other tool.
### Global Tool Management
You can enable or disable MCP tools globally:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-mcp-foo": {
"type": "local",
"command": ["bun", "x", "my-mcp-command-foo"]
},
"my-mcp-bar": {
"type": "local",
"command": ["bun", "x", "my-mcp-command-bar"]
}
},
"tools": {
"my-mcp-foo": false
}
}
```
You can also use a glob pattern to disable all matching MCPs:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-mcp-foo": {
"type": "local",
"command": ["bun", "x", "my-mcp-command-foo"]
},
"my-mcp-bar": {
"type": "local",
"command": ["bun", "x", "my-mcp-command-bar"]
}
},
"tools": {
"my-mcp*": false
}
}
```
Here we are using the glob pattern `my-mcp*` to disable all MCPs.
### Per-Agent Tool Management
If you have a large number of MCP servers you may want to only enable them per agent and disable them globally. To do this:
Disable it as a tool globally in the `tools` section.
In your [agent config](/agents#tools), enable the MCP server as a tool.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"my-mcp": {
"type": "local",
"command": ["bun", "x", "my-mcp-command"],
"enabled": true
}
},
"tools": {
"my-mcp*": false
},
"agent": {
"my-agent": {
"tools": {
"my-mcp*": true
}
}
}
}
```
### Glob Patterns
The glob pattern uses simple regex globbing patterns:
* `*` matches zero or more of any character (e.g., `"my-mcp*"` matches `my-mcp_search`, `my-mcp_list`, etc.)
* `?` matches exactly one character
* All other characters match literally
MCP server tools are registered with server name as prefix, so to disable all tools for a server simply use:
```json theme={null}
"mymcpservername_*": false
```
## Examples
Below are examples of some common MCP servers. You can submit a PR if you want to document other servers.
### Sentry
Add the [Sentry MCP server](https://mcp.sentry.dev) to interact with your Sentry projects and issues.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"sentry": {
"type": "remote",
"url": "https://mcp.sentry.dev/mcp",
"oauth": {}
}
}
}
```
After adding the configuration, authenticate with Sentry:
```bash theme={null}
opencode mcp auth sentry
```
This will open a browser window to complete the OAuth flow and connect OpenCode to your Sentry account.
Once authenticated, you can use Sentry tools in your prompts to query issues, projects, and error data.
```txt theme={null}
Show me the latest unresolved issues in my project. use sentry
```
### Context7
Add the [Context7 MCP server](https://github.com/upstash/context7) to search through docs.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp"
}
}
}
```
If you have signed up for a free account, you can use your API key and get higher rate-limits:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
}
}
}
}
```
Here we are assuming that you have the `CONTEXT7_API_KEY` environment variable set.
Add `use context7` to your prompts to use Context7 MCP server.
```txt theme={null}
Configure a Cloudflare Worker script to cache JSON API responses for five minutes. use context7
```
Alternatively, you can add something like this to your [AGENTS.md](/rules):
```md title="AGENTS.md" theme={null}
When you need to search docs, use `context7` tools.
```
### Grep by Vercel
Add the [Grep by Vercel](https://grep.app) MCP server to search through code snippets on GitHub.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"gh_grep": {
"type": "remote",
"url": "https://mcp.grep.app"
}
}
}
```
Since we named our MCP server `gh_grep`, you can add `use the gh_grep tool` to your prompts to get the agent to use it.
```txt theme={null}
What's the right way to set a custom domain in an SST Astro component? use the gh_grep tool
```
Alternatively, you can add something like this to your [AGENTS.md](/rules):
```md title="AGENTS.md" theme={null}
If you are unsure how to do something, use `gh_grep` to search code examples from GitHub.
```
## FAQ
There's no hard limit, but keep in mind that each MCP server adds to your context window. We recommend being selective about which servers you enable to avoid exceeding context limits.
Yes! You can create custom MCP servers that implement the Model Context Protocol. Check the [MCP specification](https://spec.modelcontextprotocol.io/) for details on building your own server.
By default, MCP servers have a 5-second timeout. If a server times out, OpenCode will mark it as failed. You can increase the timeout in your config using the `timeout` property.
You can list all available MCP tools and their status using `opencode mcp list`. This shows connected servers and their available tools.
# Models
Source: https://anomalyco-opencode.mintlify.app/models
Configuring an LLM provider and model
OpenCode uses the [AI SDK](https://ai-sdk.dev/) and [Models.dev](https://models.dev) to support **75+ LLM providers** and it supports running local models.
## Providers
Most popular providers are preloaded by default. If you've added the credentials for a provider through the `/connect` command, they'll be available when you start OpenCode.
## Select a Model
Once you've configured your provider you can select the model you want by typing in:
```bash theme={null}
/models
```
## Recommended Models
There are a lot of models out there, with new models coming out every week.
Consider using one of the models we recommend.
However, there are only a few of them that are good at both generating code and tool calling.
Here are several models that work well with OpenCode, in no particular order. (This is not an exhaustive list nor is it necessarily up to date):
* GPT 5.2
* GPT 5.1 Codex
* Claude Opus 4.5
* Claude Sonnet 4.5
* Minimax M2.1
* Gemini 3 Pro
## Set a Default Model
To set one of these as the default model, you can set the `model` key in your OpenCode config.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"model": "lmstudio/google/gemma-3n-e4b"
}
```
Here the full ID is `provider_id/model_id`. For example, if you're using OpenCode Zen, you would use `opencode/gpt-5.1-codex` for GPT 5.1 Codex.
If you've configured a custom provider, the `provider_id` is key from the `provider` part of your config, and the `model_id` is the key from `provider.models`.
## Configure Models
You can globally configure a model's options through the config.
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"openai": {
"models": {
"gpt-5": {
"options": {
"reasoningEffort": "high",
"textVerbosity": "low",
"reasoningSummary": "auto",
"include": ["reasoning.encrypted_content"]
}
}
}
},
"anthropic": {
"models": {
"claude-sonnet-4-5-20250929": {
"options": {
"thinking": {
"type": "enabled",
"budgetTokens": 16000
}
}
}
}
}
}
}
```
Here we're configuring global settings for two built-in models: `gpt-5` when accessed via the `openai` provider, and `claude-sonnet-4-20250514` when accessed via the `anthropic` provider. The built-in provider and model names can be found on [Models.dev](https://models.dev).
You can also configure these options for any agents that you are using. The agent config overrides any global options here.
You can also define custom variants that extend built-in ones. Variants let you configure different settings for the same model without creating duplicate entries:
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"opencode": {
"models": {
"gpt-5": {
"variants": {
"high": {
"reasoningEffort": "high",
"textVerbosity": "low",
"reasoningSummary": "auto"
},
"low": {
"reasoningEffort": "low",
"textVerbosity": "low",
"reasoningSummary": "auto"
}
}
}
}
}
}
}
```
## Variants
Many models support multiple variants with different configurations. OpenCode ships with built-in default variants for popular providers.
### Built-in Variants
OpenCode ships with default variants for many providers:
**Anthropic**:
* `high` - High thinking budget (default)
* `max` - Maximum thinking budget
**OpenAI**:
Varies by model but roughly:
* `none` - No reasoning
* `minimal` - Minimal reasoning effort
* `low` - Low reasoning effort
* `medium` - Medium reasoning effort
* `high` - High reasoning effort
* `xhigh` - Extra high reasoning effort
**Google**:
* `low` - Lower effort/token budget
* `high` - Higher effort/token budget
This list is not comprehensive. Many other providers have built-in defaults too.
### Custom Variants
You can override existing variants or add your own:
```jsonc title="opencode.jsonc" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"openai": {
"models": {
"gpt-5": {
"variants": {
"thinking": {
"reasoningEffort": "high",
"textVerbosity": "low"
},
"fast": {
"disabled": true
}
}
}
}
}
}
}
```
### Cycle Variants
Use the keybind `variant_cycle` to quickly switch between variants.
## Loading Models
When OpenCode starts up, it checks for models in the following priority order:
1. The `--model` or `-m` command line flag. The format is the same as in the config file: `provider_id/model_id`.
2. The model list in the OpenCode config.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-20250514"
}
```
The format here is `provider/model`.
3. The last used model.
4. The first model using an internal priority.
# Modes
Source: https://anomalyco-opencode.mintlify.app/modes
Different modes for different use cases (deprecated).
Modes are now configured through the `agent` option in the opencode config. The `mode` option is now deprecated. [Learn more about agents](/agents).
Modes in OpenCode allow you to customize the behavior, tools, and prompts for different use cases.
OpenCode comes with two built-in modes: **build** and **plan**. You can customize these or configure your own through the opencode config.
You can switch between modes during a session or configure them in your config file.
***
## Built-in Modes
OpenCode comes with two built-in modes.
### Build
Build is the **default** mode with all tools enabled. This is the standard mode for development work where you need full access to file operations and system commands.
### Plan
A restricted mode designed for planning and analysis. In plan mode, the following tools are disabled by default:
* `write` - Cannot create new files
* `edit` - Cannot modify existing files (except for files located at `.opencode/plans/*.md` to detail the plan itself)
* `patch` - Cannot apply patches
* `bash` - Cannot execute shell commands
This mode is useful when you want the AI to analyze code, suggest changes, or create plans without making any actual modifications to your codebase.
***
## Switching Modes
You can switch between modes during a session using the **Tab** key, or your configured `switch_mode` keybind.
***
## Configuration
You can customize the built-in modes or create your own through configuration. Modes can be configured in two ways:
### JSON Configuration
Configure modes in your `opencode.json` config file:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mode": {
"build": {
"model": "anthropic/claude-sonnet-4-20250514",
"prompt": "{file:./prompts/build.txt}",
"tools": {
"write": true,
"edit": true,
"bash": true
}
},
"plan": {
"model": "anthropic/claude-haiku-4-20250514",
"tools": {
"write": false,
"edit": false,
"bash": false
}
}
}
}
```
### Markdown Configuration
You can also define modes using markdown files. Place them in:
* **Global**: `~/.config/opencode/modes/`
* **Project**: `.opencode/modes/`
```markdown title="~/.config/opencode/modes/review.md" theme={null}
---
model: anthropic/claude-sonnet-4-20250514
temperature: 0.1
tools:
write: false
edit: false
bash: false
---
You are in code review mode. Focus on:
- Code quality and best practices
- Potential bugs and edge cases
- Performance implications
- Security considerations
Provide constructive feedback without making direct changes.
```
The markdown file name becomes the mode name (e.g., `review.md` creates a `review` mode).
***
## Configuration Options
### Model
Override the default model for this mode. Useful for using different models optimized for different tasks.
```json title="opencode.json" theme={null}
{
"mode": {
"plan": {
"model": "anthropic/claude-haiku-4-20250514"
}
}
}
```
### Temperature
Control the randomness and creativity of the AI's responses. Lower values make responses more focused and deterministic, while higher values increase creativity and variability.
```json title="opencode.json" theme={null}
{
"mode": {
"plan": {
"temperature": 0.1
},
"creative": {
"temperature": 0.8
}
}
}
```
Temperature values typically range from 0.0 to 1.0:
* **0.0-0.2**: Very focused and deterministic responses, ideal for code analysis and planning
* **0.3-0.5**: Balanced responses with some creativity, good for general development tasks
* **0.6-1.0**: More creative and varied responses, useful for brainstorming and exploration
If no temperature is specified, OpenCode uses model-specific defaults (typically 0 for most models, 0.55 for Qwen models).
### Prompt
Specify a custom system prompt file for this mode.
```json title="opencode.json" theme={null}
{
"mode": {
"review": {
"prompt": "{file:./prompts/code-review.txt}"
}
}
}
```
The path is relative to where the config file is located. This works for both the global OpenCode config and the project-specific config.
### Tools
Control which tools are available in this mode.
```json title="opencode.json" theme={null}
{
"mode": {
"readonly": {
"tools": {
"write": false,
"edit": false,
"bash": false,
"read": true,
"grep": true,
"glob": true
}
}
}
}
```
If no tools are specified, all tools are enabled by default.
***
## Available Tools
Here are all the tools that can be controlled through the mode config:
| Tool | Description |
| ----------- | ----------------------- |
| `bash` | Execute shell commands |
| `edit` | Modify existing files |
| `write` | Create new files |
| `read` | Read file contents |
| `grep` | Search file contents |
| `glob` | Find files by pattern |
| `list` | List directory contents |
| `patch` | Apply patches to files |
| `todowrite` | Manage todo lists |
| `todoread` | Read todo lists |
| `webfetch` | Fetch web content |
***
## Custom Modes
You can create your own custom modes by adding them to the configuration.
### Using JSON Configuration
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"mode": {
"docs": {
"prompt": "{file:./prompts/documentation.txt}",
"tools": {
"write": true,
"edit": true,
"bash": false,
"read": true,
"grep": true,
"glob": true
}
}
}
}
```
### Using Markdown Files
Create mode files in `.opencode/modes/` for project-specific modes or `~/.config/opencode/modes/` for global modes:
```markdown title=".opencode/modes/debug.md" theme={null}
---
temperature: 0.1
tools:
bash: true
read: true
grep: true
write: false
edit: false
---
You are in debug mode. Your primary goal is to help investigate and diagnose issues.
Focus on:
- Understanding the problem through careful analysis
- Using bash commands to inspect system state
- Reading relevant files and logs
- Searching for patterns and anomalies
- Providing clear explanations of findings
Do not make any changes to files. Only investigate and report.
```
***
## Use Cases
Here are some common use cases for different modes:
* **Build mode**: Full development work with all tools enabled
* **Plan mode**: Analysis and planning without making changes
* **Review mode**: Code review with read-only access plus documentation tools
* **Debug mode**: Focused on investigation with bash and read tools enabled
* **Docs mode**: Documentation writing with file operations but no system commands
***
## Migration to Agents
The `mode` configuration is deprecated in favor of the more flexible `agent` configuration. Please refer to the [agents documentation](/agents) for the current approach.
To migrate from modes to agents, change your configuration from:
```json title="Before (modes)" theme={null}
{
"mode": {
"build": {
"model": "anthropic/claude-sonnet-4-20250514"
}
}
}
```
To:
```json title="After (agents)" theme={null}
{
"agent": {
"build": {
"mode": "primary",
"model": "anthropic/claude-sonnet-4-20250514"
}
}
}
```
# Network Configuration
Source: https://anomalyco-opencode.mintlify.app/network
Configure proxies, custom certificates, mDNS discovery, and enterprise network settings
OpenCode supports enterprise network environments with proxy servers, custom CA certificates, and local network discovery via mDNS (Bonjour).
## Proxy Configuration
OpenCode respects standard HTTP proxy environment variables for all outbound connections to LLM providers, APIs, and external services.
### Basic Setup
```bash HTTPS Proxy (Recommended) theme={null}
export HTTPS_PROXY=https://proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1
opencode
```
```bash HTTP Proxy theme={null}
export HTTP_PROXY=http://proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1
opencode
```
You **must** set `NO_PROXY=localhost,127.0.0.1` to prevent the TUI from routing local server connections through the proxy. This would create a routing loop and break the application.
### Environment Variables
HTTPS proxy URL (recommended for encrypted proxy connections).
HTTP proxy URL (fallback if HTTPS\_PROXY not set).
Comma-separated list of hosts to bypass proxy. Must include `localhost,127.0.0.1` for OpenCode to function.
Generic proxy for all protocols (least specific, use HTTPS\_PROXY/HTTP\_PROXY when possible).
### Proxy Authentication
Include credentials directly in the proxy URL:
```bash theme={null}
export HTTPS_PROXY=http://username:password@proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1
```
**Security Best Practices:**
* Never hardcode passwords in scripts committed to version control
* Use environment variable substitution: `http://${PROXY_USER}:${PROXY_PASS}@proxy.example.com:8080`
* Consider using an LLM Gateway for advanced auth (NTLM, Kerberos)
* Rotate credentials regularly
### Advanced Authentication Methods
For proxies requiring NTLM, Kerberos, or certificate-based auth:
Use tools like [LiteLLM Proxy](https://docs.litellm.ai/docs/proxy/deploy) or [Kong](https://konghq.com/) that support advanced authentication.
```bash theme={null}
# LiteLLM example
docker run -p 4000:4000 \
-e PROXY_HOST=proxy.example.com \
-e PROXY_PORT=8080 \
ghcr.io/berriai/litellm:latest
```
Point OpenCode to your gateway instead of directly to LLM providers:
```json opencode.json theme={null}
{
"providers": {
"openai": {
"baseURL": "http://localhost:4000/openai"
}
}
}
```
OpenCode connects to the gateway on localhost, bypassing the proxy entirely.
### Proxy Debugging
Verify proxy connectivity:
```bash Test HTTPS Proxy theme={null}
curl -x $HTTPS_PROXY https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY"
```
```bash Check Environment theme={null}
env | grep -i proxy
# Should show:
# HTTPS_PROXY=https://proxy.example.com:8080
# NO_PROXY=localhost,127.0.0.1
```
```bash Verify NO_PROXY theme={null}
curl http://localhost:4096/global/health
# Should work WITHOUT going through proxy
```
**Cause**: `NO_PROXY` not set, routing localhost through proxy.
**Fix**:
```bash theme={null}
export NO_PROXY=localhost,127.0.0.1
```
**Cause**: Proxy performs SSL interception with custom CA.
**Fix**: Add custom CA certificate (see Custom Certificates section).
**Cause**: Special characters in password not URL-encoded.
**Fix**: URL-encode password:
```bash theme={null}
# Password: p@ssw0rd! → p%40ssw0rd%21
export HTTPS_PROXY=http://user:p%40ssw0rd%21@proxy.example.com:8080
```
## Custom CA Certificates
Enterprise networks often use custom Certificate Authorities (CAs) for SSL/TLS inspection. Configure OpenCode to trust these certificates.
### Single Certificate File
```bash theme={null}
export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem
opencode
```
This variable is read by Node.js/Bun's TLS stack and applies to:
* Proxy connections (if using HTTPS proxy)
* Direct HTTPS requests to LLM providers
* Any other HTTPS traffic from OpenCode
### Multiple Certificates
Concatenate multiple CA certificates into one file:
```bash theme={null}
cat ca-root.pem ca-intermediate.pem > combined-ca.pem
export NODE_EXTRA_CA_CERTS=/path/to/combined-ca.pem
```
### Certificate Locations
Common enterprise CA certificate locations:
```bash Windows theme={null}
C:\ProgramData\Company\Certificates\ca.crt
# Convert to PEM if needed:
certutil -encode ca.crt ca.pem
```
```bash macOS theme={null}
# Export from Keychain
security find-certificate -a -p \
-c "Corporate Root CA" \
/Library/Keychains/System.keychain > ca.pem
```
```bash Linux theme={null}
# System-wide CA bundle (varies by distro)
/etc/ssl/certs/ca-certificates.crt # Debian/Ubuntu
/etc/pki/tls/certs/ca-bundle.crt # RHEL/CentOS
```
### Persistent Configuration
Add to your shell profile for persistence:
```bash ~/.bashrc or ~/.zshrc theme={null}
export NODE_EXTRA_CA_CERTS="$HOME/.config/opencode/corporate-ca.pem"
export HTTPS_PROXY="https://proxy.example.com:8080"
export NO_PROXY="localhost,127.0.0.1"
```
```bash Windows (System Environment) theme={null}
setx NODE_EXTRA_CA_CERTS "C:\Users\YourName\.config\opencode\ca.pem"
setx HTTPS_PROXY "https://proxy.example.com:8080"
setx NO_PROXY "localhost,127.0.0.1"
```
### Certificate Verification Issues
**Symptoms**: `certificate has expired` errors.
**Diagnosis**:
```bash theme={null}
openssl s_client -connect api.openai.com:443 -proxy proxy.example.com:8080
# Check "Verify return code"
```
**Fix**: Update your CA certificate file or contact IT.
**Symptoms**: `unable to verify the first certificate`.
**Cause**: Missing intermediate CA certificate.
**Fix**: Ensure your CA file includes the full chain:
```bash theme={null}
cat root-ca.pem intermediate-ca.pem > full-chain.pem
export NODE_EXTRA_CA_CERTS=/path/to/full-chain.pem
```
**Symptoms**: `self signed certificate in certificate chain`.
**Fix**: Add the self-signed root CA to `NODE_EXTRA_CA_CERTS`.
## mDNS Service Discovery
mDNS (Multicast DNS), also known as Bonjour or Zeroconf, enables automatic discovery of OpenCode servers on the local network without manual IP configuration.
### How mDNS Works
When `--mdns` is enabled, the OpenCode server announces itself on the local network:
```
Service Name: opencode-4096._http._tcp.local.
Hostname: opencode.local (or custom via --mdns-domain)
Port: 4096
```
mDNS-aware clients (web browsers, mobile apps) query for `_http._tcp.local.` services and receive server details.
Clients connect to `http://opencode.local:4096` without knowing the IP address.
### Enable mDNS
```bash theme={null}
opencode serve \
--hostname 0.0.0.0 \
--port 4096 \
--mdns
```
mDNS requires:
* `--hostname` set to a non-loopback address (not `127.0.0.1` or `localhost`)
* UDP port 5353 accessible on the local network
* mDNS responder running on the host (avahi-daemon on Linux, built-in on macOS/Windows)
### Custom Domain Name
```bash theme={null}
opencode serve \
--hostname 0.0.0.0 \
--port 4096 \
--mdns \
--mdns-domain mycompany-ai.local
```
Clients can then connect to `http://mycompany-ai.local:4096`.
### mDNS Implementation
OpenCode uses the `bonjour-service` library:
```typescript theme={null}
// From packages/opencode/src/server/mdns.ts
import { Bonjour } from "bonjour-service";
const bonjour = new Bonjour();
const service = bonjour.publish({
name: `opencode-${port}`,
type: "http",
host: "opencode.local",
port: 4096,
txt: { path: "/" }
});
```
### Discovery from Clients
```javascript JavaScript (Browser/Node) theme={null}
const Bonjour = require('bonjour-service');
const bonjour = new Bonjour();
bonjour.find({ type: 'http' }, (service) => {
if (service.name.startsWith('opencode-')) {
console.log(`Found OpenCode at ${service.host}:${service.port}`);
}
});
```
```python Python theme={null}
import socket
from zeroconf import ServiceBrowser, Zeroconf
class OpenCodeListener:
def add_service(self, zeroconf, type, name):
info = zeroconf.get_service_info(type, name)
print(f"Found OpenCode at {info.server}:{info.port}")
zeroconf = Zeroconf()
browser = ServiceBrowser(zeroconf, "_http._tcp.local.", OpenCodeListener())
```
```bash CLI (Linux/macOS) theme={null}
# List all HTTP services
avahi-browse -rt _http._tcp
# macOS alternative
dns-sd -B _http._tcp
```
### mDNS Platform Support
Built-in Bonjour support. Works out of the box.
```bash theme={null}
# Test mDNS resolution
ping opencode.local
```
Requires Avahi daemon:
```bash theme={null}
# Debian/Ubuntu
sudo apt install avahi-daemon avahi-utils
sudo systemctl enable avahi-daemon
sudo systemctl start avahi-daemon
# RHEL/CentOS
sudo yum install avahi avahi-tools
sudo systemctl enable avahi-daemon
sudo systemctl start avahi-daemon
```
Bonjour included with iTunes or install standalone:
[Download Bonjour Print Services](https://support.apple.com/kb/DL999)
Or install via Chocolatey:
```powershell theme={null}
choco install bonjour
```
mDNS in containers requires host networking:
```bash theme={null}
docker run --network host \
-e OPENCODE_PORT=4096 \
opencode/opencode serve --hostname 0.0.0.0 --mdns
```
`--network host` bypasses container networking, making mDNS work but reducing isolation.
### Security Considerations
**mDNS Exposes Your Server to the Local Network**
* Any device on the same network can discover the server
* Always use `OPENCODE_SERVER_PASSWORD` when enabling mDNS
* Consider firewall rules to restrict access to trusted subnets
* mDNS should not be used on untrusted networks (coffee shops, airports)
```bash theme={null}
# Secure mDNS server
export OPENCODE_SERVER_PASSWORD="strong-password-here"
opencode serve --hostname 0.0.0.0 --mdns --port 4096
```
## Firewall Configuration
### Required Ports
Default OpenCode HTTP server port. Customize with `--port`.
mDNS/Bonjour service discovery. Only needed if `--mdns` is enabled.
### Firewall Rules
```bash Linux (iptables) theme={null}
# Allow OpenCode server
sudo iptables -A INPUT -p tcp --dport 4096 -j ACCEPT
# Allow mDNS (if enabled)
sudo iptables -A INPUT -p udp --dport 5353 -j ACCEPT
```
```bash Linux (ufw) theme={null}
sudo ufw allow 4096/tcp comment 'OpenCode server'
sudo ufw allow 5353/udp comment 'OpenCode mDNS'
```
```bash macOS theme={null}
# Add to /etc/pf.conf
pass in proto tcp to any port 4096
pass in proto udp to any port 5353
# Reload
sudo pfctl -f /etc/pf.conf
```
```powershell Windows theme={null}
# Allow inbound
New-NetFirewallRule -DisplayName "OpenCode Server" `
-Direction Inbound -Protocol TCP -LocalPort 4096 -Action Allow
New-NetFirewallRule -DisplayName "OpenCode mDNS" `
-Direction Inbound -Protocol UDP -LocalPort 5353 -Action Allow
```
## Network Architecture Patterns
### Pattern 1: Local Development (Default)
```
┌─────────────────┐
│ Developer │
│ Machine │
│ │
│ ┌───────────┐ │
│ │ TUI │ │
│ └─────┬─────┘ │
│ │ HTTP │
│ ↓ │
│ ┌───────────┐ │
│ │ Server │ │
│ │ :4096 │ │
│ └─────┬─────┘ │
│ │ HTTPS │
└────────┼────────┘
↓
LLM Provider
```
* No proxy, no mDNS
* Localhost only
* Fastest, most secure
### Pattern 2: Corporate Network with Proxy
```
┌─────────────────┐
│ Developer │
│ Machine │
│ │
│ ┌───────────┐ │ ┌──────────┐
│ │ Server │ │──────┤ Proxy │
│ │ :4096 │ │HTTPS │ :8080 │
│ └───────────┘ │ └────┬─────┘
│ │ │ HTTPS
└─────────────────┘ ↓
LLM Provider
```
* `HTTPS_PROXY` configured
* `NO_PROXY=localhost`
* Custom CA certificate if proxy intercepts SSL
### Pattern 3: Remote Server with mDNS
```
Local Network
┌──────────────────────────┐
│ │
│ ┌──────────────────┐ │
│ │ Remote Server │ │
│ │ │ │
│ │ ┌────────────┐ │ │
│ │ │ Server │ │ │
│ │ │ :4096 │ │ │
│ │ └──────┬─────┘ │ │
│ │ │ mDNS │ │
│ └─────────┼────────┘ │
│ ↓ │
│ ┌────────────────┐ │
│ │ Client Device │ │
│ │ (Laptop/Phone) │ │
│ └────────────────┘ │
│ │
└──────────────────────────┘
```
* Server on dedicated machine
* `--hostname 0.0.0.0 --mdns`
* Password authentication required
* Clients auto-discover via mDNS
### Pattern 4: Containerized Deployment
```
┌────────────────────────────┐
│ Docker Host │
│ │
│ ┌──────────────────────┐ │
│ │ OpenCode Container │ │
│ │ │ │
│ │ Server :4096 │ │
│ └──────────────────────┘ │
│ │ │
│ │ Bridge Network│
│ ↓ │
│ ┌──────────────────────┐ │
│ │ Reverse Proxy │ │
│ │ (Nginx/Caddy) │ │
│ │ :443 (HTTPS) │ │
│ └──────────┬───────────┘ │
└─────────────┼──────────────┘
↓
Internet
```
* Expose via reverse proxy with SSL
* Internal container networking
* Auth + CORS configured
## Best Practices
Include `localhost,127.0.0.1` in `NO_PROXY` when using proxies to prevent TUI connection failures.
Prefer `HTTPS_PROXY` over `HTTP_PROXY` for encrypted proxy connections.
Always set `OPENCODE_SERVER_PASSWORD` when using `--mdns` or `--hostname 0.0.0.0`.
Verify `NODE_EXTRA_CA_CERTS` with `openssl s_client` before running OpenCode.
## Next Steps
Learn about the OpenCode server API and architecture.
Diagnose network connectivity issues.
Optimal network setup for Windows users.
# Permissions
Source: https://anomalyco-opencode.mintlify.app/permissions
Control what actions agents can take with the permission system.
OpenCode's permission system gives you fine-grained control over what actions agents can perform. You can configure permissions globally, per-agent, or for specific operations like bash commands and file edits.
***
## Overview
The permission system allows you to:
* Control whether agents can edit files, run bash commands, or fetch web content
* Set different permission levels: `allow`, `ask`, or `deny`
* Configure permissions globally or override them per-agent
* Use pattern matching for specific commands or file paths
* Get prompted for approval before potentially dangerous operations
The Plan agent uses `ask` permissions by default for file edits and bash commands, making it perfect for code review and analysis without accidental modifications.
***
## Permission Levels
There are three permission levels you can configure:
**Allow all operations without approval**
The agent can perform the action freely without prompting you. This is the default for the Build agent.
```json theme={null}
{
"permission": {
"edit": "allow"
}
}
```
**Prompt for approval before running**
The agent will ask for your permission before performing the action. You'll see a prompt with details about what the agent wants to do, and you can:
* **Once**: Allow this specific operation only
* **Always**: Allow this and all similar operations in this session
* **Reject**: Deny the operation
```json theme={null}
{
"permission": {
"edit": "ask"
}
}
```
This is the default for the Plan agent.
**Disable the tool entirely**
The agent cannot perform the action at all. The tool will be completely unavailable.
```json theme={null}
{
"permission": {
"edit": "deny"
}
}
```
***
## Configurable Permissions
You can configure permissions for these tools:
### Edit Permissions
Control file modifications including `edit`, `write`, `patch`, and `multiedit` tools.
```json title="opencode.json" theme={null}
{
"permission": {
"edit": "ask"
}
}
```
**What this controls**:
* Creating new files (`write` tool)
* Modifying existing files (`edit` and `multiedit` tools)
* Applying patches (`patch` tool)
### Bash Permissions
Control shell command execution.
```json title="opencode.json" theme={null}
{
"permission": {
"bash": "ask"
}
}
```
**What this controls**:
* All bash/shell commands
* Git operations
* Package manager commands (npm, pip, etc.)
* Build and test commands
* System operations
You can also configure specific command patterns (see [Pattern Matching](#pattern-matching) below).
### WebFetch Permissions
Control web content fetching.
```json title="opencode.json" theme={null}
{
"permission": {
"webfetch": "ask"
}
}
```
**What this controls**:
* Fetching content from URLs
* Accessing external web resources
***
## Pattern Matching
You can use glob patterns to set permissions for specific commands or paths. This is especially powerful for bash commands.
### Basic Bash Command Patterns
Allow specific commands while asking for others:
```json title="opencode.json" theme={null}
{
"permission": {
"bash": {
"*": "ask",
"git status": "allow",
"git log*": "allow",
"git diff*": "allow"
}
}
}
```
In this configuration:
* Most commands require approval (`"*": "ask"`)
* `git status` is always allowed
* Any command starting with `git log` is allowed
* Any command starting with `git diff` is allowed
Rules are evaluated in order, and the **last matching rule wins**. Always put the wildcard `*` rule first, then more specific rules after.
### Deny Dangerous Commands
Prevent potentially dangerous operations:
```json title="opencode.json" theme={null}
{
"permission": {
"bash": {
"*": "allow",
"rm -rf*": "deny",
"git push --force*": "deny",
"npm publish": "ask"
}
}
}
```
### Read-Only Git Access
Allow read-only git commands but ask for modifications:
```json title="opencode.json" theme={null}
{
"permission": {
"bash": {
"*": "ask",
"git status": "allow",
"git log*": "allow",
"git diff*": "allow",
"git show*": "allow",
"git branch": "allow",
"git push*": "deny",
"git commit*": "ask"
}
}
}
```
***
## Global Configuration
Set default permissions for all agents in your `opencode.json`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "ask",
"bash": {
"*": "ask",
"git status": "allow",
"git diff*": "allow"
},
"webfetch": "allow"
}
}
```
***
## Per-Agent Configuration
Override global permissions for specific agents:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"edit": "ask",
"bash": "ask"
},
"agent": {
"build": {
"permission": {
"edit": "allow",
"bash": "allow"
}
},
"plan": {
"permission": {
"edit": "deny",
"bash": {
"*": "deny",
"git status": "allow",
"git diff*": "allow"
}
}
}
}
}
```
In this example:
* **Build agent**: Full access to edits and bash
* **Plan agent**: Cannot edit files, can only run safe git commands
***
## Markdown Agent Configuration
You can also set permissions in markdown agent definitions:
```markdown title="~/.config/opencode/agents/review.md" theme={null}
---
description: Code review without edits
mode: subagent
permission:
edit: deny
bash:
"*": ask
"git diff": allow
"git log*": allow
"grep *": allow
webfetch: deny
---
You are a code reviewer. Analyze code and suggest improvements without making changes.
```
***
## Real-World Examples
### Safe Development Agent
An agent that requires approval for potentially dangerous operations:
```json title="opencode.json" theme={null}
{
"agent": {
"safe-dev": {
"description": "Development agent with safety checks",
"mode": "primary",
"permission": {
"edit": "allow",
"bash": {
"*": "allow",
"rm *": "ask",
"git push*": "ask",
"npm publish": "ask",
"docker rm*": "ask"
}
}
}
}
}
```
### Read-Only Analysis Agent
An agent that can only read and analyze, not modify:
```json title="opencode.json" theme={null}
{
"agent": {
"analyzer": {
"description": "Analyze code without modifications",
"mode": "subagent",
"permission": {
"edit": "deny",
"bash": {
"*": "deny",
"git status": "allow",
"git log*": "allow",
"git diff*": "allow",
"grep *": "allow",
"find *": "allow"
},
"webfetch": "allow"
}
}
}
}
```
### Documentation Writer Agent
An agent that can edit docs but not run commands:
```json title="opencode.json" theme={null}
{
"agent": {
"docs-writer": {
"description": "Write and update documentation",
"mode": "subagent",
"permission": {
"edit": "allow",
"bash": "deny",
"webfetch": "allow"
},
"tools": {
"read": true,
"write": true,
"edit": true,
"grep": true,
"glob": true
}
}
}
}
```
### Testing Agent
An agent optimized for running and fixing tests:
```json title="opencode.json" theme={null}
{
"agent": {
"test-runner": {
"description": "Run tests and fix failures",
"mode": "subagent",
"permission": {
"edit": "allow",
"bash": {
"*": "ask",
"npm test*": "allow",
"npm run test*": "allow",
"pytest*": "allow",
"jest*": "allow",
"vitest*": "allow",
"git status": "allow",
"git diff": "allow"
}
}
}
}
}
```
***
## Task Permissions
Control which subagents an agent can invoke via the Task tool:
```json title="opencode.json" theme={null}
{
"agent": {
"orchestrator": {
"mode": "primary",
"permission": {
"task": {
"*": "deny",
"orchestrator-*": "allow",
"code-reviewer": "ask"
}
}
}
}
}
```
In this configuration:
* By default, no subagents can be invoked (`"*": "deny"`)
* Subagents matching `orchestrator-*` pattern can be invoked freely
* The `code-reviewer` subagent requires approval
When set to `deny`, the subagent is removed from the Task tool description entirely, so the model won't attempt to invoke it.
Users can always invoke any subagent directly via the `@` autocomplete menu, even if the agent's task permissions would deny it.
***
## Permission Prompts
When a permission is set to `ask`, you'll see a prompt with details about what the agent wants to do. You have three options:
### Once
Allow this specific operation only. The next time a similar operation is attempted, you'll be asked again.
### Always
Allow this operation and all similar operations for the rest of the session. The permission will be remembered and you won't be prompted again for matching operations.
For example, if you choose "Always" for `git status`, all future `git status` commands in that session will be allowed automatically.
### Reject
Deny the operation. The agent will receive an error and can try again with different parameters or approach the task differently.
You can optionally provide a message explaining why you rejected the operation, which helps the agent understand what to do instead.
***
## Best Practices
**Use the Plan agent for reviews**: The Plan agent has `ask` permissions by default, making it perfect for code analysis without accidental changes.
**Start restrictive, then relax**: Begin with `ask` for most operations, then use "Always" during the session for operations you trust.
**Use patterns for git safety**: Allow read-only git commands but require approval for push, rebase, and other modifying operations.
**Create specialized agents**: Use per-agent permissions to create focused agents (e.g., docs-only, test-only, read-only) for specific workflows.
**Wildcard rules first**: When using pattern matching, put the wildcard `*` rule first, then more specific rules after. The last matching rule wins.
***
## Error Handling
When a permission is denied, the agent receives different errors depending on how it was denied:
### Rejected by User
When you click "Reject" on a permission prompt, the agent receives:
* An error message stating the user rejected the operation
* Optional feedback message if you provided one
* The ability to try again with different parameters
### Denied by Configuration
When an operation is denied by a `"deny"` rule in your config, the agent receives:
* An error indicating the configuration prevents this operation
* Information about relevant permission rules
* The inability to retry (the tool is fully disabled)
### Corrected by User
When you reject an operation but provide guidance on what to do instead, the agent receives:
* Your feedback message
* The opportunity to continue working with your guidance
* Context to approach the task differently
***
## Advanced Configuration
### Path-Based Permissions
You can use path patterns in bash permissions:
```json title="opencode.json" theme={null}
{
"permission": {
"bash": {
"*": "ask",
"rm ~/Downloads/*": "allow",
"rm ~/.config/*": "deny"
}
}
}
```
### Home Directory Expansion
Permissions support home directory expansion with `~` and `$HOME`:
```json title="opencode.json" theme={null}
{
"permission": {
"bash": {
"rm ~/safe-to-delete/*": "allow",
"rm $HOME/important/*": "deny"
}
}
}
```
### Complex Permission Sets
Combine multiple permission types for sophisticated control:
```json title="opencode.json" theme={null}
{
"agent": {
"deploy-agent": {
"description": "Handles deployment tasks",
"permission": {
"edit": {
"*": "allow",
".env": "deny",
"*.production.*": "ask"
},
"bash": {
"*": "ask",
"git status": "allow",
"git push origin main": "deny",
"npm run build": "allow",
"npm run deploy": "ask"
},
"webfetch": "allow",
"task": {
"*": "allow",
"production-*": "ask"
}
}
}
}
}
```
***
## Summary
The permission system gives you powerful control over agent behavior:
* **Three levels**: `allow`, `ask`, `deny`
* **Three tools**: `edit`, `bash`, `webfetch`
* **Pattern matching**: Use globs for fine-grained control
* **Global and per-agent**: Configure defaults and override as needed
* **Task permissions**: Control subagent invocation
Use permissions to create safe, focused agents that match your workflow and risk tolerance.
# Plugins
Source: https://anomalyco-opencode.mintlify.app/plugins
Write your own plugins to extend OpenCode.
Plugins allow you to extend OpenCode by hooking into various events and customizing behavior. You can create plugins to add new features, integrate with external services, or modify OpenCode's default behavior.
For examples, check out the [plugins](https://opencode.ai/ecosystem#plugins) created by the community.
***
## Use a plugin
There are two ways to load plugins.
### From local files
Place JavaScript or TypeScript files in the plugin directory.
* `.opencode/plugins/` - Project-level plugins
* `~/.config/opencode/plugins/` - Global plugins
Files in these directories are automatically loaded at startup.
### From npm
Specify npm packages in your config file.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-helicone-session", "opencode-wakatime", "@my-org/custom-plugin"]
}
```
Both regular and scoped npm packages are supported.
Browse available plugins in the [ecosystem](https://opencode.ai/ecosystem#plugins).
### How plugins are installed
**npm plugins** are installed automatically using Bun at startup. Packages and their dependencies are cached in `~/.cache/opencode/node_modules/`.
**Local plugins** are loaded directly from the plugin directory. To use external packages, you must create a `package.json` within your config directory (see [Dependencies](#dependencies)), or publish the plugin to npm and [add it to your config](/config#plugins).
### Load order
Plugins are loaded from all sources and all hooks run in sequence. The load order is:
1. Global config (`~/.config/opencode/opencode.json`)
2. Project config (`opencode.json`)
3. Global plugin directory (`~/.config/opencode/plugins/`)
4. Project plugin directory (`.opencode/plugins/`)
Duplicate npm packages with the same name and version are loaded once. However, a local plugin and an npm plugin with similar names are both loaded separately.
***
## Create a plugin
A plugin is a **JavaScript/TypeScript module** that exports one or more plugin functions. Each function receives a context object and returns a hooks object.
### Dependencies
Local plugins and custom tools can use external npm packages. Add a `package.json` to your config directory with the dependencies you need.
```json title=".opencode/package.json" theme={null}
{
"dependencies": {
"shescape": "^2.1.0"
}
}
```
OpenCode runs `bun install` at startup to install these. Your plugins and tools can then import them.
```ts title=".opencode/plugins/my-plugin.ts" theme={null}
import { escape } from "shescape"
export const MyPlugin = async (ctx) => {
return {
"tool.execute.before": async (input, output) => {
if (input.tool === "bash") {
output.args.command = escape(output.args.command)
}
},
}
}
```
### Basic structure
```js title=".opencode/plugins/example.js" theme={null}
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
console.log("Plugin initialized!")
return {
// Hook implementations go here
}
}
```
The plugin function receives:
* `project`: The current project information.
* `directory`: The current working directory.
* `worktree`: The git worktree path.
* `client`: An opencode SDK client for interacting with the AI.
* `$`: Bun's [shell API](https://bun.com/docs/runtime/shell) for executing commands.
### TypeScript support
For TypeScript plugins, you can import types from the plugin package:
```ts title="my-plugin.ts" {1} theme={null}
import type { Plugin } from "@opencode-ai/plugin"
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
// Type-safe hook implementations
}
}
```
***
## Plugin API
### Context
The plugin function receives a `PluginInput` context object:
```ts theme={null}
type PluginInput = {
client: ReturnType
project: Project
directory: string
worktree: string
serverUrl: URL
$: BunShell
}
```
* **`client`**: SDK client for interacting with OpenCode's API
* **`project`**: Current project metadata
* **`directory`**: Current working directory for the session
* **`worktree`**: Git worktree root path
* **`serverUrl`**: OpenCode server URL
* **`$`**: Bun's shell interface for executing commands
### Hooks
Plugins return a `Hooks` object with event handlers. All hooks are optional.
#### Chat Hooks
**`chat.message`**: Modify messages before they're sent to the LLM
```ts theme={null}
"chat.message": async (input, output) => {
// input: { sessionID, agent?, model?, messageID?, variant? }
// output: { message: UserMessage, parts: Part[] }
output.parts.push({ type: "text", text: "Additional context" })
}
```
**`chat.params`**: Modify LLM parameters (temperature, topP, topK)
```ts theme={null}
"chat.params": async (input, output) => {
// input: { sessionID, agent, model, provider, message }
// output: { temperature, topP, topK, options }
output.temperature = 0.7
}
```
**`chat.headers`**: Add custom headers to LLM requests
```ts theme={null}
"chat.headers": async (input, output) => {
// input: { sessionID, agent, model, provider, message }
// output: { headers }
output.headers["X-Custom-Header"] = "value"
}
```
#### Tool Hooks
**`tool.execute.before`**: Intercept tool calls before execution
```ts theme={null}
"tool.execute.before": async (input, output) => {
// input: { tool, sessionID, callID }
// output: { args }
if (input.tool === "read" && output.args.filePath.includes(".env")) {
throw new Error("Do not read .env files")
}
}
```
**`tool.execute.after`**: Modify tool results after execution
```ts theme={null}
"tool.execute.after": async (input, output) => {
// input: { tool, sessionID, callID, args }
// output: { title, output, metadata }
if (input.tool === "bash") {
output.metadata = { exitCode: 0 }
}
}
```
**`tool.definition`**: Modify tool definitions sent to the LLM
```ts theme={null}
"tool.definition": async (input, output) => {
// input: { toolID }
// output: { description, parameters }
if (input.toolID === "bash") {
output.description = "Custom bash description"
}
}
```
**`tool`**: Register custom tools
```ts theme={null}
import { tool } from "@opencode-ai/plugin"
return {
tool: {
mytool: tool({
description: "This is a custom tool",
args: {
foo: tool.schema.string(),
},
async execute(args, context) {
return `Hello ${args.foo}!`
},
}),
},
}
```
#### Command Hooks
**`command.execute.before`**: Modify commands before execution
```ts theme={null}
"command.execute.before": async (input, output) => {
// input: { command, sessionID, arguments }
// output: { parts }
output.parts.push({ type: "text", text: "Extra instructions" })
}
```
#### Permission Hooks
**`permission.ask`**: Override permission decisions
```ts theme={null}
"permission.ask": async (input, output) => {
// input: Permission
// output: { status: "ask" | "deny" | "allow" }
if (input.type === "file" && input.path.includes(".env")) {
output.status = "deny"
}
}
```
#### Shell Hooks
**`shell.env`**: Inject environment variables into shell execution
```ts theme={null}
"shell.env": async (input, output) => {
// input: { cwd, sessionID?, callID? }
// output: { env }
output.env.MY_API_KEY = "secret"
output.env.PROJECT_ROOT = input.cwd
}
```
#### Session Hooks
**`experimental.session.compacting`**: Customize session compaction
```ts theme={null}
"experimental.session.compacting": async (input, output) => {
// input: { sessionID }
// output: { context: string[], prompt?: string }
output.context.push("Custom context for compaction")
}
```
#### Event Hook
**`event`**: Listen to all OpenCode events
```ts theme={null}
event: async ({ event }) => {
if (event.type === "session.idle") {
console.log("Session completed!")
}
}
```
***
## Events
Plugins can subscribe to events using the `event` hook. Here is a list of the different events available.
### Command Events
* `command.executed`
### File Events
* `file.edited`
* `file.watcher.updated`
### Installation Events
* `installation.updated`
### LSP Events
* `lsp.client.diagnostics`
* `lsp.updated`
### Message Events
* `message.part.removed`
* `message.part.updated`
* `message.removed`
* `message.updated`
### Permission Events
* `permission.asked`
* `permission.replied`
### Server Events
* `server.connected`
### Session Events
* `session.created`
* `session.compacted`
* `session.deleted`
* `session.diff`
* `session.error`
* `session.idle`
* `session.status`
* `session.updated`
### Todo Events
* `todo.updated`
### Shell Events
* `shell.env`
### Tool Events
* `tool.execute.after`
* `tool.execute.before`
### TUI Events
* `tui.prompt.append`
* `tui.command.execute`
* `tui.toast.show`
***
## Examples
Here are some examples of plugins you can use to extend opencode.
### Send notifications
Send notifications when certain events occur:
```js title=".opencode/plugins/notification.js" theme={null}
export const NotificationPlugin = async ({ project, client, $, directory, worktree }) => {
return {
event: async ({ event }) => {
// Send notification on session completion
if (event.type === "session.idle") {
await $`osascript -e 'display notification "Session completed!" with title "opencode"'`
}
},
}
}
```
We are using `osascript` to run AppleScript on macOS. Here we are using it to send notifications.
If you're using the OpenCode desktop app, it can send system notifications automatically when a response is ready or when a session errors.
### .env protection
Prevent opencode from reading `.env` files:
```javascript title=".opencode/plugins/env-protection.js" theme={null}
export const EnvProtection = async ({ project, client, $, directory, worktree }) => {
return {
"tool.execute.before": async (input, output) => {
if (input.tool === "read" && output.args.filePath.includes(".env")) {
throw new Error("Do not read .env files")
}
},
}
}
```
### Inject environment variables
Inject environment variables into all shell execution (AI tools and user terminals):
```javascript title=".opencode/plugins/inject-env.js" theme={null}
export const InjectEnvPlugin = async () => {
return {
"shell.env": async (input, output) => {
output.env.MY_API_KEY = "secret"
output.env.PROJECT_ROOT = input.cwd
},
}
}
```
### Custom tools
Plugins can also add custom tools to opencode:
```ts title=".opencode/plugins/custom-tools.ts" theme={null}
import { type Plugin, tool } from "@opencode-ai/plugin"
export const CustomToolsPlugin: Plugin = async (ctx) => {
return {
tool: {
mytool: tool({
description: "This is a custom tool",
args: {
foo: tool.schema.string(),
},
async execute(args, context) {
const { directory, worktree } = context
return `Hello ${args.foo} from ${directory} (worktree: ${worktree})`
},
}),
},
}
}
```
The `tool` helper creates a custom tool that opencode can call. It takes a Zod schema function and returns a tool definition with:
* `description`: What the tool does
* `args`: Zod schema for the tool's arguments
* `execute`: Function that runs when the tool is called
Your custom tools will be available to opencode alongside built-in tools.
### Logging
Use `client.app.log()` instead of `console.log` for structured logging:
```ts title=".opencode/plugins/my-plugin.ts" theme={null}
export const MyPlugin = async ({ client }) => {
await client.app.log({
body: {
service: "my-plugin",
level: "info",
message: "Plugin initialized",
extra: { foo: "bar" },
},
})
}
```
Levels: `debug`, `info`, `warn`, `error`. See [SDK documentation](/sdk/overview) for details.
### Compaction hooks
Customize the context included when a session is compacted:
```ts title=".opencode/plugins/compaction.ts" theme={null}
import type { Plugin } from "@opencode-ai/plugin"
export const CompactionPlugin: Plugin = async (ctx) => {
return {
"experimental.session.compacting": async (input, output) => {
// Inject additional context into the compaction prompt
output.context.push(`
## Custom Context
Include any state that should persist across compaction:
- Current task status
- Important decisions made
- Files being actively worked on
`)
},
}
}
```
The `experimental.session.compacting` hook fires before the LLM generates a continuation summary. Use it to inject domain-specific context that the default compaction prompt would miss.
You can also replace the compaction prompt entirely by setting `output.prompt`:
```ts title=".opencode/plugins/custom-compaction.ts" theme={null}
import type { Plugin } from "@opencode-ai/plugin"
export const CustomCompactionPlugin: Plugin = async (ctx) => {
return {
"experimental.session.compacting": async (input, output) => {
// Replace the entire compaction prompt
output.prompt = `
You are generating a continuation prompt for a multi-agent swarm session.
Summarize:
1. The current task and its status
2. Which files are being modified and by whom
3. Any blockers or dependencies between agents
4. The next steps to complete the work
Format as a structured prompt that a new agent can use to resume work.
`
},
}
}
```
When `output.prompt` is set, it completely replaces the default compaction prompt. The `output.context` array is ignored in this case.
# Providers
Source: https://anomalyco-opencode.mintlify.app/providers
Using any LLM provider in OpenCode
OpenCode uses the [AI SDK](https://ai-sdk.dev/) and [Models.dev](https://models.dev) to support **75+ LLM providers** and it supports running local models.
To add a provider you need to:
1. Add the API keys for the provider using the `/connect` command.
2. Configure the provider in your OpenCode config.
## Credentials
When you add a provider's API keys with the `/connect` command, they are stored in `~/.local/share/opencode/auth.json`.
## Config
You can customize the providers through the `provider` section in your OpenCode config.
### Base URL
You can customize the base URL for any provider by setting the `baseURL` option. This is useful when using proxy services or custom endpoints.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"anthropic": {
"options": {
"baseURL": "https://api.anthropic.com/v1"
}
}
}
}
```
## OpenCode Zen
OpenCode Zen is a list of models provided by the OpenCode team that have been tested and verified to work well with OpenCode.
If you are new, we recommend starting with OpenCode Zen.
Run the `/connect` command in the TUI, select opencode, and head to [opencode.ai/auth](https://opencode.ai/auth).
```txt theme={null}
/connect
```
Sign in, add your billing details, and copy your API key.
```txt theme={null}
┌ API key
│
│
└ enter
```
Run `/models` in the TUI to see the list of models we recommend.
```txt theme={null}
/models
```
It works like any other provider in OpenCode and is completely optional to use.
## Provider Directory
Let's look at some of the providers in detail. If you'd like to add a provider to the list, feel free to open a PR.
Don't see a provider here? Submit a PR.
### Anthropic
Once you've signed up, run the `/connect` command and select Anthropic.
```txt theme={null}
/connect
```
Here you can select the **Claude Pro/Max** option and it'll open your browser and ask you to authenticate.
```txt theme={null}
┌ Select auth method
│
│ Claude Pro/Max
│ Create an API Key
│ Manually enter API Key
└
```
Now all the Anthropic models should be available when you use the `/models` command.
```txt theme={null}
/models
```
Using your Claude Pro/Max subscription in OpenCode is not officially supported by Anthropic.
#### Using API keys
You can also select **Create an API Key** if you don't have a Pro/Max subscription. It'll also open your browser and ask you to login to Anthropic and give you a code you can paste in your terminal.
Or if you already have an API key, you can select **Manually enter API Key** and paste it in your terminal.
### Amazon Bedrock
To use Amazon Bedrock with OpenCode:
Head over to the **Model catalog** in the Amazon Bedrock console and request access to the models you want.
You need to have access to the model you want in Amazon Bedrock.
Choose one of the following methods:
#### Environment Variables (Quick Start)
Set one of these environment variables while running opencode:
```bash theme={null}
# Option 1: Using AWS access keys
AWS_ACCESS_KEY_ID=XXX AWS_SECRET_ACCESS_KEY=YYY opencode
# Option 2: Using named AWS profile
AWS_PROFILE=my-profile opencode
# Option 3: Using Bedrock bearer token
AWS_BEARER_TOKEN_BEDROCK=XXX opencode
```
Or add them to your bash profile:
```bash title="~/.bash_profile" theme={null}
export AWS_PROFILE=my-dev-profile
export AWS_REGION=us-east-1
```
#### Configuration File (Recommended)
For project-specific or persistent configuration, use `opencode.json`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"amazon-bedrock": {
"options": {
"region": "us-east-1",
"profile": "my-aws-profile"
}
}
}
}
```
**Available options:**
* `region` - AWS region (e.g., `us-east-1`, `eu-west-1`)
* `profile` - AWS named profile from `~/.aws/credentials`
* `endpoint` - Custom endpoint URL for VPC endpoints (alias for generic `baseURL` option)
Configuration file options take precedence over environment variables.
#### Authentication Methods
* **`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`**: Create an IAM user and generate access keys in the AWS Console
* **`AWS_PROFILE`**: Use named profiles from `~/.aws/credentials`. First configure with `aws configure --profile my-profile` or `aws sso login`
* **`AWS_BEARER_TOKEN_BEDROCK`**: Generate long-term API keys from the Amazon Bedrock console
* **`AWS_WEB_IDENTITY_TOKEN_FILE` / `AWS_ROLE_ARN`**: For EKS IRSA (IAM Roles for Service Accounts) or other Kubernetes environments with OIDC federation
#### Authentication Precedence
Amazon Bedrock uses the following authentication priority:
1. **Bearer Token** - `AWS_BEARER_TOKEN_BEDROCK` environment variable or token from `/connect` command
2. **AWS Credential Chain** - Profile, access keys, shared credentials, IAM roles, Web Identity Tokens (EKS IRSA), instance metadata
When a bearer token is set (via `/connect` or `AWS_BEARER_TOKEN_BEDROCK`), it takes precedence over all AWS credential methods including configured profiles.
Run the `/models` command to select the model you want.
```txt theme={null}
/models
```
For custom inference profiles, use the model and provider name in the key and set the `id` property to the arn. This ensures correct caching:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"amazon-bedrock": {
"models": {
"anthropic-claude-sonnet-4.5": {
"id": "arn:aws:bedrock:us-east-1:xxx:application-inference-profile/yyy"
}
}
}
}
}
```
### OpenAI
We recommend signing up for [ChatGPT Plus or Pro](https://chatgpt.com/pricing).
Once you've signed up, run the `/connect` command and select OpenAI.
```txt theme={null}
/connect
```
Here you can select the **ChatGPT Plus/Pro** option and it'll open your browser and ask you to authenticate.
```txt theme={null}
┌ Select auth method
│
│ ChatGPT Plus/Pro
│ Manually enter API Key
└
```
Now all the OpenAI models should be available when you use the `/models` command.
```txt theme={null}
/models
```
#### Using API keys
If you already have an API key, you can select **Manually enter API Key** and paste it in your terminal.
### GitHub Copilot
To use your GitHub Copilot subscription with opencode:
Some models might need a Pro+ subscription to use.
Run the `/connect` command and search for GitHub Copilot.
```txt theme={null}
/connect
```
Navigate to [github.com/login/device](https://github.com/login/device) and enter the code.
```txt theme={null}
┌ Login with GitHub Copilot
│
│ https://github.com/login/device
│
│ Enter code: 8F43-6FCF
│
└ Waiting for authorization...
```
Now run the `/models` command to select the model you want.
```txt theme={null}
/models
```
### Google Vertex AI
To use Google Vertex AI with OpenCode:
Head over to the **Model Garden** in the Google Cloud Console and check the models available in your region.
You need to have a Google Cloud project with Vertex AI API enabled.
Set the required environment variables:
* `GOOGLE_CLOUD_PROJECT`: Your Google Cloud project ID
* `VERTEX_LOCATION` (optional): The region for Vertex AI (defaults to `global`)
* Authentication (choose one):
* `GOOGLE_APPLICATION_CREDENTIALS`: Path to your service account JSON key file
* Authenticate using gcloud CLI: `gcloud auth application-default login`
Set them while running opencode:
```bash theme={null}
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json GOOGLE_CLOUD_PROJECT=your-project-id opencode
```
Or add them to your bash profile:
```bash title="~/.bash_profile" theme={null}
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
export GOOGLE_CLOUD_PROJECT=your-project-id
export VERTEX_LOCATION=global
```
The `global` region improves availability and reduces errors at no extra cost. Use regional endpoints (e.g., `us-central1`) for data residency requirements.
Run the `/models` command to select the model you want.
```txt theme={null}
/models
```
### DeepSeek
Head over to the [DeepSeek console](https://platform.deepseek.com/), create an account, and click **Create new API key**.
Run the `/connect` command and search for **DeepSeek**.
```txt theme={null}
/connect
```
Enter your DeepSeek API key.
```txt theme={null}
┌ API key
│
│
└ enter
```
Run the `/models` command to select a DeepSeek model like *DeepSeek Reasoner*.
```txt theme={null}
/models
```
### Local Models
#### Ollama
You can configure opencode to use local models through Ollama.
Ollama can automatically configure itself for OpenCode. See the [Ollama integration docs](https://docs.ollama.com/integrations/opencode) for details.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"name": "Ollama (local)",
"options": {
"baseURL": "http://localhost:11434/v1"
},
"models": {
"llama2": {
"name": "Llama 2"
}
}
}
}
}
```
In this example:
* `ollama` is the custom provider ID. This can be any string you want.
* `npm` specifies the package to use for this provider. Here, `@ai-sdk/openai-compatible` is used for any OpenAI-compatible API.
* `name` is the display name for the provider in the UI.
* `options.baseURL` is the endpoint for the local server.
* `models` is a map of model IDs to their configurations. The model name will be displayed in the model selection list.
If tool calls aren't working, try increasing `num_ctx` in Ollama. Start around 16k - 32k.
#### LM Studio
You can configure opencode to use local models through LM Studio.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"lmstudio": {
"npm": "@ai-sdk/openai-compatible",
"name": "LM Studio (local)",
"options": {
"baseURL": "http://127.0.0.1:1234/v1"
},
"models": {
"google/gemma-3n-e4b": {
"name": "Gemma 3n-e4b (local)"
}
}
}
}
}
```
#### llama.cpp
You can configure opencode to use local models through [llama.cpp's](https://github.com/ggml-org/llama.cpp) llama-server utility.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"llama.cpp": {
"npm": "@ai-sdk/openai-compatible",
"name": "llama-server (local)",
"options": {
"baseURL": "http://127.0.0.1:8080/v1"
},
"models": {
"qwen3-coder:a3b": {
"name": "Qwen3-Coder: a3b-30b (local)",
"limit": {
"context": 128000,
"output": 65536
}
}
}
}
}
}
```
## Custom Provider
To add any **OpenAI-compatible** provider that's not listed in the `/connect` command:
You can use any OpenAI-compatible provider with opencode. Most modern AI providers offer OpenAI-compatible APIs.
Run the `/connect` command and scroll down to **Other**.
```bash theme={null}
$ /connect
┌ Add credential
│
◆ Select provider
│ ...
│ ● Other
└
```
Enter a unique ID for the provider.
```bash theme={null}
$ /connect
┌ Add credential
│
◇ Enter provider id
│ myprovider
└
```
Choose a memorable ID, you'll use this in your config file.
Enter your API key for the provider.
```bash theme={null}
$ /connect
┌ Add credential
│
▲ This only stores a credential for myprovider - you will need to configure it in opencode.json, check the docs for examples.
│
◇ Enter your API key
│ sk-...
└
```
Create or update your `opencode.json` file in your project directory:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My AI ProviderDisplay Name",
"options": {
"baseURL": "https://api.myprovider.com/v1"
},
"models": {
"my-model-name": {
"name": "My Model Display Name"
}
}
}
}
}
```
Here are the configuration options:
* **npm**: AI SDK package to use, `@ai-sdk/openai-compatible` for OpenAI-compatible providers
* **name**: Display name in UI
* **models**: Available models
* **options.baseURL**: API endpoint URL
* **options.apiKey**: Optionally set the API key, if not using auth
* **options.headers**: Optionally set custom headers
Run the `/models` command and your custom provider and models will appear in the selection list.
### Example with Advanced Options
Here's an example setting the `apiKey`, `headers`, and model `limit` options:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My AI ProviderDisplay Name",
"options": {
"baseURL": "https://api.myprovider.com/v1",
"apiKey": "{env:ANTHROPIC_API_KEY}",
"headers": {
"Authorization": "Bearer custom-token"
}
},
"models": {
"my-model-name": {
"name": "My Model Display Name",
"limit": {
"context": 200000,
"output": 65536
}
}
}
}
}
}
```
Configuration details:
* **apiKey**: Set using `env` variable syntax
* **headers**: Custom headers sent with each request
* **limit.context**: Maximum input tokens the model accepts
* **limit.output**: Maximum tokens the model can generate
The `limit` fields allow OpenCode to understand how much context you have left. Standard providers pull these from models.dev automatically.
## Troubleshooting
If you are having trouble with configuring a provider, check the following:
Run `opencode auth list` to see if the credentials for the provider are added to your config.
This doesn't apply to providers like Amazon Bedrock, that rely on environment variables for their auth.
Check the opencode config and:
* Make sure the provider ID used in the `/connect` command matches the ID in your opencode config
* The right npm package is used for the provider. For example, use `@ai-sdk/cerebras` for Cerebras. And for all other OpenAI-compatible providers, use `@ai-sdk/openai-compatible`
* Check correct API endpoint is used in the `options.baseURL` field
# Quickstart
Source: https://anomalyco-opencode.mintlify.app/quickstart
Get up and running with OpenCode in 5 minutes
Get OpenCode running on your first project in under 5 minutes.
## Install OpenCode
The fastest way to install OpenCode:
```bash theme={null}
curl -fsSL https://opencode.ai/install | bash
```
Or use your preferred package manager:
```bash npm theme={null}
npm install -g opencode-ai
```
```bash brew theme={null}
brew install anomalyco/tap/opencode
```
```bash chocolatey theme={null}
choco install opencode
```
Check that OpenCode is installed:
```bash theme={null}
opencode --version
```
For Windows users, we recommend using [WSL](/windows-wsl) for the best experience.
***
## Configure a Provider
OpenCode works with any LLM provider. We recommend starting with OpenCode Zen for a curated experience.
Navigate to any directory and launch OpenCode:
```bash theme={null}
cd ~/my-project
opencode
```
In the OpenCode interface, run:
```
/connect
```
Select **opencode** (Zen) from the provider list.
Visit [opencode.ai/auth](https://opencode.ai/auth), sign in, and copy your API key.
Return to OpenCode and paste your API key when prompted.
```
┌ API key
│ [paste your key]
└ enter
```
You can also configure other providers like Anthropic, OpenAI, or local models. See [Providers](/providers) for more options.
***
## Initialize Your Project
Help OpenCode understand your codebase by initializing it:
```
/init
```
This command:
* Analyzes your project structure
* Identifies coding patterns
* Creates an `AGENTS.md` file with project context
Commit the generated `AGENTS.md` file to version control so your team benefits from the same context.
***
## Start Coding
You're ready! Here are some things you can try:
### Ask Questions
Ask OpenCode about your codebase:
```
How does authentication work in this project?
```
Use `@` to reference specific files:
```
Explain the logic in @src/auth/login.ts
```
### Make Changes
Request code modifications:
```
Add error handling to the login function
```
Switch to **Plan mode** (press `Tab`) to review changes before applying them.
### Run Commands
Execute bash commands with `!`:
```
!npm test
```
Or use slash commands:
```
/undo # Revert the last change
/redo # Reapply a reverted change
/share # Share the current conversation
```
***
## Keyboard Shortcuts
| Shortcut | Action |
| -------- | ------------------------------------ |
| `Tab` | Switch between Build and Plan agents |
| `@` | Reference a file |
| `!` | Run a bash command |
| `/` | Execute a slash command |
| `Ctrl+C` | Cancel current operation |
See [Keybinds](/keybinds) for the complete list.
***
## Next Steps
Learn about agents, modes, and tools
Customize OpenCode for your workflow
Explore all CLI commands
Connect LSP servers, MCP, GitHub, and more
***
## Getting Help
Check the installation with `opencode --version`. If it's not found, ensure the installation directory is in your PATH.
See [Troubleshooting](/troubleshooting) for common issues.
Verify your API key is valid and you have credits available. Run `/connect` again to reconfigure.
Check [Providers](/providers) for provider-specific setup.
Use `/undo` to revert changes. Switch to Plan mode (`Tab`) to review suggestions before applying them.
Learn more about [Modes](/modes) and [Permissions](/permissions).
Need more help? Join our [Discord community](https://opencode.ai/discord) or check the [FAQ](/community/faq).
# Rules
Source: https://anomalyco-opencode.mintlify.app/rules
Set custom instructions for opencode.
You can provide custom instructions to opencode by creating an `AGENTS.md` file. This is similar to Cursor's rules. It contains instructions that will be included in the LLM's context to customize its behavior for your specific project.
***
## Initialize
To create a new `AGENTS.md` file, you can run the `/init` command in opencode.
You should commit your project's `AGENTS.md` file to Git.
This will scan your project and all its contents to understand what the project is about and generate an `AGENTS.md` file with it. This helps opencode to navigate the project better.
If you have an existing `AGENTS.md` file, this will try to add to it.
***
## Example
You can also just create this file manually. Here's an example of some things you can put into an `AGENTS.md` file.
```markdown title="AGENTS.md" theme={null}
# SST v3 Monorepo Project
This is an SST v3 monorepo with TypeScript. The project uses bun workspaces for package management.
## Project Structure
- `packages/` - Contains all workspace packages (functions, core, web, etc.)
- `infra/` - Infrastructure definitions split by service (storage.ts, api.ts, web.ts)
- `sst.config.ts` - Main SST configuration with dynamic imports
## Code Standards
- Use TypeScript with strict mode enabled
- Shared code goes in `packages/core/` with proper exports configuration
- Functions go in `packages/functions/`
- Infrastructure should be split into logical files in `infra/`
## Monorepo Conventions
- Import shared modules using workspace names: `@my-app/core/example`
```
We are adding project-specific instructions here and this will be shared across your team.
***
## Types
opencode also supports reading the `AGENTS.md` file from multiple locations. And this serves different purposes.
### Project
Place an `AGENTS.md` in your project root for project-specific rules. These only apply when you are working in this directory or its sub-directories.
### Global
You can also have global rules in a `~/.config/opencode/AGENTS.md` file. This gets applied across all opencode sessions.
Since this isn't committed to Git or shared with your team, we recommend using this to specify any personal rules that the LLM should follow.
### Claude Code Compatibility
For users migrating from Claude Code, OpenCode supports Claude Code's file conventions as fallbacks:
* **Project rules**: `CLAUDE.md` in your project directory (used if no `AGENTS.md` exists)
* **Global rules**: `~/.claude/CLAUDE.md` (used if no `~/.config/opencode/AGENTS.md` exists)
* **Skills**: `~/.claude/skills/` — see [Agent Skills](./skills.mdx) for details
To disable Claude Code compatibility, set one of these environment variables:
```bash theme={null}
export OPENCODE_DISABLE_CLAUDE_CODE=1 # Disable all .claude support
export OPENCODE_DISABLE_CLAUDE_CODE_PROMPT=1 # Disable only ~/.claude/CLAUDE.md
export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills
```
***
## Precedence
When opencode starts, it looks for rule files in this order:
1. **Local files** by traversing up from the current directory (`AGENTS.md`, `CLAUDE.md`)
2. **Global file** at `~/.config/opencode/AGENTS.md`
3. **Claude Code file** at `~/.claude/CLAUDE.md` (unless disabled)
The first matching file wins in each category. For example, if you have both `AGENTS.md` and `CLAUDE.md`, only `AGENTS.md` is used. Similarly, `~/.config/opencode/AGENTS.md` takes precedence over `~/.claude/CLAUDE.md`.
***
## Custom Instructions
You can specify custom instruction files in your `opencode.json` or the global `~/.config/opencode/opencode.json`. This allows you and your team to reuse existing rules rather than having to duplicate them to AGENTS.md.
Example:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"instructions": ["CONTRIBUTING.md", "docs/guidelines.md", ".cursor/rules/*.md"]
}
```
You can also use remote URLs to load instructions from the web.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"instructions": ["https://raw.githubusercontent.com/my-org/shared-rules/main/style.md"]
}
```
Remote instructions are fetched with a 5 second timeout.
All instruction files are combined with your `AGENTS.md` files.
### Glob patterns
The `instructions` field supports glob patterns:
```json title="opencode.json" theme={null}
{
"instructions": [
"docs/**/*.md",
".cursor/rules/*.md",
"packages/*/AGENTS.md"
]
}
```
This is especially useful for monorepos where different packages have their own rules.
***
## What to include
Your `AGENTS.md` file should contain information that helps the LLM work effectively with your codebase.
### Project overview
```markdown theme={null}
# Project Name
Brief description of what the project does.
## Tech Stack
- **Framework**: Next.js 14 with App Router
- **Language**: TypeScript
- **Database**: PostgreSQL with Drizzle ORM
- **Styling**: Tailwind CSS
- **Testing**: Vitest + Playwright
```
### Directory structure
```markdown theme={null}
## Project Structure
- `app/` - Next.js app directory (routes, layouts)
- `components/` - Reusable React components
- `lib/` - Utility functions and shared logic
- `db/` - Database schema and migrations
- `tests/` - Test files
```
### Code conventions
```markdown theme={null}
## Code Standards
### TypeScript
- Use strict mode
- Avoid `any` type
- Prefer `interface` over `type` for object shapes
- Use `const` over `let`, never use `var`
### React
- Use functional components with hooks
- Prefer named exports over default exports
- Co-locate component files: `Button.tsx`, `Button.test.tsx`, `Button.stories.tsx`
### Imports
- Use absolute imports with `@/` prefix
- Group imports: external, internal, types
- Sort imports alphabetically
```
### Architecture patterns
````markdown theme={null}
## Architecture
### API Routes
- All API routes in `app/api/`
- Use route handlers with proper HTTP methods
- Return standardized JSON responses:
```typescript
{ success: true, data: any } | { success: false, error: string }
````
### Database
* Schema definitions in `db/schema.ts`
* Migrations in `db/migrations/`
* Use snake\_case for database columns
* Use camelCase in TypeScript
### State Management
* Use React Context for global state
* Use URL state for filters/pagination
* Use local state for component-specific data
````
### Testing guidelines
```markdown
## Testing
- Write unit tests for utility functions
- Write integration tests for API routes
- Write E2E tests for critical user flows
- Aim for >80% coverage on core logic
- Mock external API calls
- Use test databases, never production
````
### Deployment info
```markdown theme={null}
## Deployment
- Platform: Vercel
- Branch: `main` (production), `dev` (staging)
- Environment variables in `.env.local` (gitignored)
- Database: Neon PostgreSQL
- CI/CD: GitHub Actions
```
### Common operations
````markdown theme={null}
## Common Operations
### Running locally
```bash
bun install
bun dev # Start dev server
bun db:push # Push schema changes
bun test # Run tests
````
### Database migrations
```bash theme={null}
bun db:generate # Generate migration
bun db:migrate # Apply migrations
bun db:studio # Open Drizzle Studio
```
### Deployment
```bash theme={null}
git push origin main # Auto-deploys to production
```
````
### Known issues and workarounds
```markdown
## Known Issues
### TypeScript strict mode and Drizzle
- Drizzle types can be strict, use `Simplify` helper
- See `db/types.ts` for type utilities
### Vercel Edge Functions
- Some Node APIs unavailable in Edge runtime
- Check `next.config.js` for runtime configuration
````
### External resources
```markdown theme={null}
## Resources
- [API Documentation](https://docs.example.com)
- [Design System](https://design.example.com)
- [Confluence Wiki](https://wiki.example.com)
```
***
## Referencing External Files
While opencode doesn't automatically parse file references in `AGENTS.md`, you can achieve similar functionality in two ways:
### Using opencode.json
The recommended approach is to use the `instructions` field in `opencode.json`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"instructions": ["docs/development-standards.md", "test/testing-guidelines.md", "packages/*/AGENTS.md"]
}
```
### Manual Instructions in AGENTS.md
You can teach opencode to read external files by providing explicit instructions in your `AGENTS.md`. Here's a practical example:
```markdown title="AGENTS.md" theme={null}
# TypeScript Project Rules
## External File Loading
CRITICAL: When you encounter a file reference (e.g., @rules/general.md), use your Read tool to load it on a need-to-know basis. They're relevant to the SPECIFIC task at hand.
Instructions:
- Do NOT preemptively load all references - use lazy loading based on actual need
- When loaded, treat content as mandatory instructions that override defaults
- Follow references recursively when needed
## Development Guidelines
For TypeScript code style and best practices: @docs/typescript-guidelines.md
For React component architecture and hooks patterns: @docs/react-patterns.md
For REST API design and error handling: @docs/api-standards.md
For testing strategies and coverage requirements: @test/testing-guidelines.md
## General Guidelines
Read the following file immediately as it's relevant to all workflows: @rules/general-guidelines.md.
```
This approach allows you to:
* Create modular, reusable rule files
* Share rules across projects via symlinks or git submodules
* Keep AGENTS.md concise while referencing detailed guidelines
* Ensure opencode loads files only when needed for the specific task
For monorepos or projects with shared standards, using `opencode.json` with glob patterns (like `packages/*/AGENTS.md`) is more maintainable than manual instructions.
***
## Best Practices
### Be specific
Provide concrete examples instead of vague guidelines:
````markdown theme={null}
# Good
Use named exports:
```typescript
export function parseUser(data: string) { }
````
# Bad
Use good practices
````
### Keep it up to date
Update `AGENTS.md` when:
- Project structure changes
- New conventions are adopted
- Dependencies are upgraded
- Architecture evolves
### Use sections
Organize with clear headers:
```markdown
# Project Name
## Tech Stack
## Project Structure
## Code Standards
## Testing
## Deployment
## Resources
````
### Include rationale
Explain *why* rules exist:
```markdown theme={null}
## Naming Conventions
- Use PascalCase for components: `UserProfile.tsx`
- Use kebab-case for routes: `user-profile/page.tsx`
Rationale: Next.js App Router uses file-based routing where
filenames map to URLs, so kebab-case creates clean URLs.
```
### Document exceptions
```markdown theme={null}
## Code Standards
- Avoid default exports
- Exception: Next.js page/layout files require default exports
```
### Link to examples
```markdown theme={null}
## API Route Pattern
See `app/api/users/route.ts` for a complete example of:
- Request validation
- Error handling
- Response formatting
- Authentication
```
### Monorepo structure
For monorepos, combine global and package-specific rules:
**Root `AGENTS.md`:**
```markdown theme={null}
# Monorepo
General conventions for the entire monorepo.
## Packages
Each package has its own `AGENTS.md` with package-specific rules.
```
**`opencode.json`:**
```json theme={null}
{
"instructions": ["packages/*/AGENTS.md"]
}
```
**Package-specific `packages/api/AGENTS.md`:**
```markdown theme={null}
# API Package
Express.js API server.
## Routes
- All routes in `src/routes/`
- Use router middleware
- OpenAPI docs in `docs/openapi.yaml`
```
### Version control
Commit `AGENTS.md` to share with your team:
```bash theme={null}
git add AGENTS.md
git commit -m "Add project rules for AI agents"
```
Include it in PR reviews when project conventions change.
***
## Global Rules
Use `~/.config/opencode/AGENTS.md` for personal preferences:
```markdown title="~/.config/opencode/AGENTS.md" theme={null}
# Personal Preferences
## Communication Style
- Be concise, avoid unnecessary explanations
- Show code first, explain only if asked
- Don't ask for permission, just do it
## Code Preferences
- Prefer functional programming style
- Use arrow functions
- Avoid classes unless necessary
## Workflow
- Run tests after making changes
- Use conventional commits
- Create small, focused commits
```
Global rules apply to all projects but are overridden by project-specific rules when there's a conflict.
***
## Migration from Cursor
If you're migrating from Cursor, OpenCode can read your existing `.cursorrules` file:
1. **Rename to `AGENTS.md`** (recommended):
```bash theme={null}
mv .cursorrules AGENTS.md
```
2. **Or reference it** in `opencode.json`:
```json theme={null}
{
"instructions": [".cursorrules"]
}
```
3. **Update formatting** (optional):
* Cursor rules are plain text
* AGENTS.md supports markdown formatting
* Add headers, code blocks, lists for clarity
***
## Examples from Real Projects
### Next.js SaaS
```markdown title="AGENTS.md" theme={null}
# SaaS Platform
Next.js 14 SaaS with Stripe payments and Clerk auth.
## Tech Stack
- Next.js 14 (App Router)
- TypeScript
- Drizzle ORM + PostgreSQL
- Clerk (auth)
- Stripe (payments)
- Tailwind + shadcn/ui
## Structure
- `app/` - Routes and pages
- `components/` - UI components
- `lib/` - Business logic
- `db/` - Database schema
- `hooks/` - Custom React hooks
## Conventions
- Server Components by default
- Client Components only when needed ('use client')
- Server Actions for mutations
- Use tRPC-style API routes for complex queries
## Database
- Schema: snake_case columns, camelCase in TypeScript
- Relations in `db/schema.ts`
- Use prepared statements for queries
## Auth
- Clerk provides `currentUser()` for Server Components
- Use `useUser()` hook for Client Components
- Protect API routes with `auth()` middleware
## Payments
- Stripe webhook handler: `app/api/webhooks/stripe/route.ts`
- Product IDs in `lib/stripe/products.ts`
- Subscription check: `lib/subscription.ts`
```
### Express API
````markdown title="AGENTS.md" theme={null}
# Express API
RESTful API with PostgreSQL.
## Stack
- Express.js
- TypeScript
- Drizzle ORM
- PostgreSQL
- JWT auth
- Zod validation
## Structure
- `src/routes/` - Route handlers
- `src/middleware/` - Express middleware
- `src/db/` - Database schema and queries
- `src/lib/` - Utilities
## Patterns
- Route handlers return JSON: `{ success: true, data }` or `{ success: false, error }`
- Use middleware for auth, validation, error handling
- Validate with Zod schemas
- Use async/await, no callbacks
## Error Handling
```typescript
class AppError extends Error {
constructor(public statusCode: number, message: string) {
super(message)
}
}
// Throw errors, catch in error middleware
throw new AppError(400, "Invalid request")
````
## Database
* Queries in `src/db/queries/`
* Use transactions for multiple operations
* Always use parameterized queries (Drizzle handles this)
## Testing
* Unit tests for utilities
* Integration tests for routes
* Use test database
* Mock external services
````
### CLI Tool
```markdown title="AGENTS.md"
# CLI Tool
Command-line tool built with TypeScript.
## Stack
- Bun runtime
- TypeScript
- Commander.js (CLI framework)
- Chalk (colors)
- Inquirer (prompts)
## Structure
- `src/commands/` - CLI commands
- `src/lib/` - Core logic
- `src/utils/` - Helpers
- `bin/` - Executable entry point
## Commands
Each command in `src/commands/` exports:
```typescript
export default {
name: 'command-name',
description: '...',
options: [...],
action: async (args, options) => { }
}
````
## Output
* Use `console.log` for normal output
* Use `console.error` for errors
* Use chalk for colors: `chalk.green('Success')`
* Use spinners for long operations
## Error Handling
* Catch errors and show user-friendly messages
* Exit with code 1 on error
* Use --verbose flag for debug output
## Config
* Read from `~/.config/tool-name/config.json`
* Support .env files
* Allow config overrides via flags
```
```
# Client API
Source: https://anomalyco-opencode.mintlify.app/sdk/client
Create and configure OpenCode clients for programmatic access
## createOpencode()
Create a complete OpenCode instance with both server and client.
```typescript theme={null}
import { createOpencode } from '@opencode-ai/sdk'
const { client, server } = await createOpencode(options)
```
### Parameters
Server configuration options
Server hostname to bind to
Server port number
Abort signal for cancellation
Timeout in milliseconds for server startup
Configuration object (see [Configuration](/sdk/configuration))
### Returns
Type-safe client instance for API calls
Server instance with control methods
Full URL of the running server (e.g., `http://127.0.0.1:4096`)
Method to shutdown the server: `server.close()`
### Example
```typescript theme={null}
import { createOpencode } from '@opencode-ai/sdk'
const { client, server } = await createOpencode({
hostname: '127.0.0.1',
port: 4096,
timeout: 10000,
config: {
model: 'anthropic/claude-3-5-sonnet-20241022',
logLevel: 'INFO',
},
})
console.log(`Server running at ${server.url}`)
// Use the client
const health = await client.global.health()
console.log(`Version: ${health.data.version}`)
// Shutdown when done
server.close()
```
### With Abort Signal
```typescript theme={null}
const controller = new AbortController()
const { client, server } = await createOpencode({
signal: controller.signal,
config: { model: 'anthropic/claude-3-5-sonnet-20241022' },
})
// Abort after 30 seconds
setTimeout(() => controller.abort(), 30000)
```
***
## createOpencodeClient()
Create a client to connect to an existing OpenCode server.
```typescript theme={null}
import { createOpencodeClient } from '@opencode-ai/sdk'
const client = createOpencodeClient(options)
```
### Parameters
Client configuration options
URL of the OpenCode server
Override the working directory for this client. Sets the `x-opencode-directory` header.
Custom fetch implementation. Defaults to global `fetch`.
Custom headers to include with every request
Response parsing method: `auto`, `json`, `text`, `blob`, `arrayBuffer`, `stream`
Return style: `data` (returns only data) or `fields` (returns object with data, error, etc.)
Throw errors instead of returning them in response object
### Returns
Type-safe client instance for making API calls to the server
### Example: Basic Client
```typescript theme={null}
import { createOpencodeClient } from '@opencode-ai/sdk'
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
})
const sessions = await client.session.list()
console.log(`Active sessions: ${sessions.data.length}`)
```
### Example: Custom Headers
```typescript theme={null}
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
headers: {
'X-Custom-Header': 'value',
},
})
```
### Example: Override Directory
```typescript theme={null}
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
directory: '/path/to/project',
})
// All operations will use this directory
const project = await client.project.current()
```
### Example: Error Handling
```typescript theme={null}
// Return errors in response object
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
throwOnError: false,
})
const result = await client.session.get({ path: { id: 'invalid' } })
if (result.error) {
console.error('Error:', result.error)
}
// Or throw errors
const throwingClient = createOpencodeClient({
baseUrl: 'http://localhost:4096',
throwOnError: true,
})
try {
await throwingClient.session.get({ path: { id: 'invalid' } })
} catch (error) {
console.error('Error:', error)
}
```
***
## createOpencodeTui()
Launch the OpenCode Terminal UI programmatically.
```typescript theme={null}
import { createOpencodeTui } from '@opencode-ai/sdk'
const tui = createOpencodeTui(options)
```
### Parameters
TUI configuration options
Project directory to open
Model to use (format: `provider/model`)
Session ID to open
Agent to use (e.g., `build`, `plan`, `general`)
Abort signal for cancellation
Configuration object
### Returns
TUI instance with control methods
Method to close the TUI: `tui.close()`
### Example
```typescript theme={null}
import { createOpencodeTui } from '@opencode-ai/sdk'
const tui = createOpencodeTui({
project: '/path/to/project',
model: 'anthropic/claude-3-5-sonnet-20241022',
agent: 'build',
})
// TUI runs in the current terminal
// Use tui.close() to exit programmatically
```
***
## Client Namespaces
The client is organized into logical namespaces:
### Global
```typescript theme={null}
client.global.health() // Server health check
client.global.event() // Global event stream
```
### Session
```typescript theme={null}
client.session.list() // List sessions
client.session.create() // Create session
client.session.get() // Get session
client.session.update() // Update session
client.session.delete() // Delete session
client.session.prompt() // Send prompt
client.session.command() // Execute command
client.session.messages() // Get messages
client.session.share() // Share session
// ... and more
```
### Project
```typescript theme={null}
client.project.list() // List projects
client.project.current() // Get current project
```
### Config
```typescript theme={null}
client.config.get() // Get configuration
client.config.update() // Update configuration
client.config.providers() // List providers
```
### File Operations
```typescript theme={null}
client.find.text() // Search text in files
client.find.files() // Find files by name
client.find.symbols() // Find workspace symbols
client.file.read() // Read file contents
client.file.status() // Get file status
```
### App
```typescript theme={null}
client.app.log() // Write log entry
client.app.agents() // List available agents
```
### TUI Control
```typescript theme={null}
client.tui.appendPrompt() // Append text to prompt
client.tui.submitPrompt() // Submit prompt
client.tui.clearPrompt() // Clear prompt
client.tui.showToast() // Show notification
client.tui.openSessions() // Open session selector
client.tui.openModels() // Open model selector
```
### Auth
```typescript theme={null}
client.auth.set() // Set authentication credentials
```
### Events
```typescript theme={null}
client.event.subscribe() // Subscribe to server-sent events
```
## Response Format
By default, API calls return an object with the following structure:
```typescript theme={null}
interface Response {
data: T // Response data
error?: Error // Error if request failed
response: Response // Raw fetch Response object
}
```
### Accessing Data
```typescript theme={null}
const result = await client.session.list()
// Access response data
console.log(result.data) // Session[]
// Check for errors
if (result.error) {
console.error('Failed:', result.error)
}
// Access raw response
console.log(result.response.status) // 200
```
## TypeScript Support
The client is fully typed with TypeScript:
```typescript theme={null}
import type { Session, Message, Config } from '@opencode-ai/sdk'
// Types are inferred automatically
const sessions = await client.session.list()
// sessions.data is typed as Session[]
const session = await client.session.get({ path: { id: 'abc' } })
// session.data is typed as Session
const config = await client.config.get()
// config.data is typed as Config
```
See the [Types reference](/sdk/types) for all available types.
# Configuration
Source: https://anomalyco-opencode.mintlify.app/sdk/configuration
Configure OpenCode programmatically via the SDK
## Overview
The SDK allows you to configure OpenCode programmatically. You can pass configuration when creating a server instance, or update it at runtime via the API.
## Initial Configuration
Pass configuration when creating an OpenCode instance:
```typescript theme={null}
import { createOpencode } from '@opencode-ai/sdk'
const { client, server } = await createOpencode({
config: {
model: 'anthropic/claude-3-5-sonnet-20241022',
logLevel: 'INFO',
theme: 'dark',
agent: {
build: {
model: 'anthropic/claude-3-5-sonnet-20241022',
temperature: 0.7,
},
},
},
})
```
The configuration is passed via the `OPENCODE_CONFIG_CONTENT` environment variable and merged with any existing `opencode.json` file.
## Runtime Configuration
Update configuration at runtime:
```typescript theme={null}
// Get current configuration
const config = await client.config.get()
console.log(config.data.model)
// Update configuration
await client.config.update({
body: {
model: 'anthropic/claude-3-5-sonnet-20241022',
logLevel: 'DEBUG',
},
})
```
## Configuration Options
### Model Selection
Default model to use (format: `provider/model`)
```typescript theme={null}
config: {
model: 'anthropic/claude-3-5-sonnet-20241022'
}
```
Small model for tasks like title generation
```typescript theme={null}
config: {
small_model: 'anthropic/claude-3-haiku-20240307'
}
```
### Logging
Log level for server output
```typescript theme={null}
config: {
logLevel: 'DEBUG'
}
```
### Agent Configuration
Configure individual agents
```typescript theme={null}
config: {
agent: {
build: {
model: 'anthropic/claude-3-5-sonnet-20241022',
temperature: 0.7,
maxSteps: 20,
permission: {
edit: 'allow',
bash: 'ask',
},
},
plan: {
model: 'anthropic/claude-3-opus-20240229',
temperature: 0.5,
},
},
}
```
#### AgentConfig Properties
Model for this agent (overrides global model)
Temperature for model sampling (0-1)
Top-p sampling parameter
Maximum agentic iterations before forcing text-only response
Description of when to use this agent
When this agent is available
Hex color code for the agent (e.g., `#FF5733`)
Custom system prompt for this agent
Enable/disable specific tools
```typescript theme={null}
tools: {
bash: true,
webfetch: false,
}
```
Permission settings
```typescript theme={null}
permission: {
edit: 'allow', // File editing
bash: 'ask', // Shell commands
webfetch: 'allow', // Web fetching
doom_loop: 'deny', // Prevent infinite loops
}
```
Values: `'ask'` | `'allow'` | `'deny'`
### Provider Configuration
Configure custom providers or override defaults
```typescript theme={null}
config: {
provider: {
anthropic: {
options: {
apiKey: 'your-api-key',
baseURL: 'https://api.anthropic.com',
},
},
custom: {
api: 'https://api.example.com',
name: 'Custom Provider',
models: {
'my-model': {
id: 'my-model-id',
name: 'My Model',
cost: { input: 0.01, output: 0.03 },
limit: { context: 128000, output: 4096 },
},
},
},
},
}
```
### MCP Servers
Configure Model Context Protocol servers
```typescript theme={null}
config: {
mcp: {
filesystem: {
type: 'local',
command: ['npx', '-y', '@modelcontextprotocol/server-filesystem', '/path'],
enabled: true,
},
github: {
type: 'remote',
url: 'https://mcp.example.com',
enabled: true,
},
},
}
```
### Commands
Define custom commands
```typescript theme={null}
config: {
command: {
review: {
template: 'Review the code in {{0}} for issues',
description: 'Code review',
agent: 'build',
},
test: {
template: 'Write tests for {{0}}',
description: 'Generate tests',
subtask: true,
},
},
}
```
### Plugins
Load custom plugins
```typescript theme={null}
config: {
plugin: [
'./plugins/custom-tool.ts',
'@company/opencode-plugin',
],
}
```
### TUI Settings
Terminal UI settings
```typescript theme={null}
config: {
tui: {
scroll_speed: 3,
scroll_acceleration: { enabled: true },
diff_style: 'auto',
},
}
```
Theme name
```typescript theme={null}
config: {
theme: 'dark'
}
```
### Sharing
Session sharing behavior
```typescript theme={null}
config: {
share: 'auto' // Auto-share all sessions
}
```
### Other Options
Custom username for display
Enable/disable snapshots
Auto-update behavior
Additional instruction files to include
```typescript theme={null}
config: {
instructions: ['STYLE_GUIDE.md', '.cursorrules']
}
```
Global tool enable/disable
```typescript theme={null}
config: {
tools: {
bash: true,
webfetch: true,
},
}
```
Global permission settings (can be overridden per agent)
## Examples
### Development Configuration
```typescript theme={null}
const { client, server } = await createOpencode({
config: {
logLevel: 'DEBUG',
model: 'anthropic/claude-3-5-sonnet-20241022',
agent: {
build: {
permission: {
edit: 'allow',
bash: 'allow',
},
},
},
},
})
```
### Production Configuration
```typescript theme={null}
const { client, server } = await createOpencode({
config: {
logLevel: 'ERROR',
model: 'anthropic/claude-3-5-sonnet-20241022',
share: 'disabled',
permission: {
edit: 'ask',
bash: 'ask',
webfetch: 'ask',
},
},
})
```
### Custom Provider
```typescript theme={null}
const { client, server } = await createOpencode({
config: {
provider: {
'my-provider': {
api: 'https://api.example.com/v1',
name: 'My Provider',
options: {
apiKey: process.env.API_KEY,
},
models: {
'my-model': {
id: 'my-model-v1',
name: 'My Custom Model',
cost: { input: 0.01, output: 0.03 },
limit: { context: 100000, output: 4096 },
},
},
},
},
model: 'my-provider/my-model',
},
})
```
### Multi-Agent Setup
```typescript theme={null}
const { client, server } = await createOpencode({
config: {
agent: {
architect: {
model: 'anthropic/claude-3-opus-20240229',
description: 'High-level architecture and design',
temperature: 0.3,
mode: 'primary',
},
builder: {
model: 'anthropic/claude-3-5-sonnet-20241022',
description: 'Implementation and coding',
temperature: 0.7,
mode: 'all',
permission: {
edit: 'allow',
bash: 'ask',
},
},
tester: {
model: 'anthropic/claude-3-5-sonnet-20241022',
description: 'Testing and validation',
temperature: 0.5,
mode: 'subagent',
},
},
},
})
```
## Getting Provider Information
Retrieve information about available providers and models:
```typescript theme={null}
// List all providers and their default models
const info = await client.config.providers()
console.log('Available providers:')
for (const provider of info.data.providers) {
console.log(`- ${provider.name} (${provider.id})`)
console.log(` Models: ${Object.keys(provider.models).length}`)
}
console.log('\nDefaults:', info.data.default)
```
## Type Safety
All configuration is fully typed:
```typescript theme={null}
import type { Config, AgentConfig } from '@opencode-ai/sdk'
const config: Config = {
model: 'anthropic/claude-3-5-sonnet-20241022',
agent: {
build: {
temperature: 0.7,
maxSteps: 20,
} satisfies AgentConfig,
},
}
const { client, server } = await createOpencode({ config })
```
# SDK Overview
Source: https://anomalyco-opencode.mintlify.app/sdk/overview
Build programmatic integrations with OpenCode using the JavaScript/TypeScript SDK
The OpenCode SDK provides a type-safe JavaScript/TypeScript client for programmatic interaction with the OpenCode server. Use it to build integrations, automate workflows, and control OpenCode from your own applications.
## Features
* **Type-safe API**: Full TypeScript support with auto-generated types from OpenAPI specification
* **Server Management**: Start and manage OpenCode server instances programmatically
* **Session Control**: Create, manage, and interact with AI coding sessions
* **Real-time Events**: Subscribe to server-sent events for live updates
* **File Operations**: Search, read, and manage project files
* **Configuration**: Programmatically configure models, agents, and providers
* **Plugin Development**: Build custom tools and extensions
## Installation
Install the SDK from npm:
```bash theme={null}
npm install @opencode-ai/sdk
```
Or with other package managers:
```bash theme={null}
yarn add @opencode-ai/sdk
pnpm add @opencode-ai/sdk
bun add @opencode-ai/sdk
```
## Quick Start
### Server + Client
Create a complete OpenCode instance with both server and client:
```typescript theme={null}
import { createOpencode } from '@opencode-ai/sdk'
const { client, server } = await createOpencode({
hostname: '127.0.0.1',
port: 4096,
config: {
model: 'anthropic/claude-3-5-sonnet-20241022',
},
})
// Use the client
const health = await client.global.health()
console.log(`Server version: ${health.data.version}`)
// Clean up when done
server.close()
```
### Client Only
Connect to an existing OpenCode server:
```typescript theme={null}
import { createOpencodeClient } from '@opencode-ai/sdk'
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
})
const sessions = await client.session.list()
console.log(`Active sessions: ${sessions.data.length}`)
```
## Architecture
The SDK is organized into several namespaces:
* **global**: Server health and global events
* **session**: Session management and AI interactions
* **project**: Project information and navigation
* **config**: Configuration management
* **file/find**: File operations and search
* **app**: Application-level operations
* **tui**: Terminal UI control
* **auth**: Authentication management
* **event**: Real-time event subscriptions
## Common Use Cases
### Build Integrations
Connect OpenCode to other tools, CI/CD pipelines, or custom workflows:
```typescript theme={null}
const session = await client.session.create({
body: { title: 'Automated Review' },
})
await client.session.prompt({
path: { id: session.data.id },
body: {
parts: [{ type: 'text', text: 'Review recent changes' }],
},
})
```
### Automate Testing
Generate tests programmatically:
```typescript theme={null}
await client.session.command({
path: { id: sessionId },
body: {
name: 'test',
arguments: 'src/utils.ts',
},
})
```
### Custom Dashboards
Build monitoring dashboards with real-time updates:
```typescript theme={null}
const events = await client.event.subscribe()
for await (const event of events.stream) {
if (event.type === 'session.updated') {
updateDashboard(event.properties.info)
}
}
```
### Extract Structured Data
Use structured output to extract information:
```typescript theme={null}
const result = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: 'text', text: 'Analyze this codebase' }],
format: {
type: 'json_schema',
schema: {
type: 'object',
properties: {
languages: { type: 'array', items: { type: 'string' } },
frameworks: { type: 'array', items: { type: 'string' } },
complexity: { type: 'string', enum: ['low', 'medium', 'high'] },
},
},
},
},
})
const analysis = result.data.info.structured_output
```
## Type Safety
All API responses and request parameters are fully typed:
```typescript theme={null}
import type { Session, Message, Part, Config } from '@opencode-ai/sdk'
// TypeScript will validate your code
const session: Session = await client.session.get({
path: { id: 'session-id' },
})
const messages: { info: Message; parts: Part[] }[] = await client.session.messages({
path: { id: session.data.id },
})
```
## Error Handling
Handle errors gracefully:
```typescript theme={null}
try {
const session = await client.session.get({
path: { id: 'invalid-id' },
})
} catch (error) {
if (error instanceof Error) {
console.error('Failed to get session:', error.message)
}
}
```
Or configure the client to return errors instead of throwing:
```typescript theme={null}
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
throwOnError: false,
})
const result = await client.session.get({
path: { id: 'invalid-id' },
})
if (result.error) {
console.error('Error:', result.error)
} else {
console.log('Session:', result.data)
}
```
## Next Steps
Learn about client creation and configuration
Configure OpenCode programmatically
Explore TypeScript types and interfaces
Build custom tools and extensions
# Plugin API
Source: https://anomalyco-opencode.mintlify.app/sdk/plugin-api
Create custom tools and extensions for OpenCode
## Overview
OpenCode's plugin system allows you to extend functionality by:
* Creating custom tools for the AI agent
* Hooking into the chat lifecycle
* Modifying prompts and parameters
* Handling authentication for custom providers
* Responding to events
Plugins are TypeScript/JavaScript modules that export a plugin function returning hooks.
## Installation
Install the plugin SDK:
```bash theme={null}
npm install @opencode-ai/plugin
```
## Basic Plugin
Create a plugin file (e.g., `my-plugin.ts`):
```typescript theme={null}
import { Plugin } from '@opencode-ai/plugin'
export const MyPlugin: Plugin = async (ctx) => {
// ctx provides access to client, project, directory, etc.
return {
// Return hooks and tools
tool: {
// Custom tools
},
event: async (input) => {
// Handle events
},
}
}
```
### Plugin Input
The plugin function receives a context object:
```typescript theme={null}
type PluginInput = {
client: OpencodeClient // SDK client instance
project: Project // Current project
directory: string // Project directory
worktree: string // Project worktree root
serverUrl: URL // Server URL
$: BunShell // Shell for running commands
}
```
### Using the Context
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
// Access the SDK client
const config = await ctx.client.config.get()
console.log('Current model:', config.data.model)
// Get project info
console.log('Project:', ctx.project.id)
console.log('Directory:', ctx.directory)
// Run shell commands
const result = await ctx.$`git status`
console.log(result.stdout.toString())
return {
// ... hooks
}
}
```
## Registering Plugins
Add your plugin to `opencode.json`:
```json theme={null}
{
"plugin": [
"./my-plugin.ts",
"@company/opencode-plugin"
]
}
```
Or programmatically:
```typescript theme={null}
import { createOpencode } from '@opencode-ai/sdk'
const { client, server } = await createOpencode({
config: {
plugin: ['./my-plugin.ts'],
},
})
```
## Creating Tools
Tools are functions that the AI agent can call. Use the `tool()` helper to define them:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
export const MyPlugin: Plugin = async (ctx) => {
return {
tool: {
search_database: tool({
description: 'Search the database for records',
args: {
query: tool.schema.string().describe('Search query'),
limit: tool.schema.number().optional().describe('Max results'),
},
async execute(args, context) {
// args.query and args.limit are typed
const results = await searchDB(args.query, args.limit)
return JSON.stringify(results)
},
}),
},
}
}
```
### Tool Definition
```typescript theme={null}
function tool(input: {
description: string
args: Args
execute(args: z.infer>, context: ToolContext): Promise
})
```
### Tool Context
The `execute` function receives a context object:
```typescript theme={null}
type ToolContext = {
sessionID: string // Current session
messageID: string // Current message
agent: string // Current agent name
directory: string // Project directory
worktree: string // Project worktree root
abort: AbortSignal // Cancellation signal
// Update tool metadata
metadata(input: {
title?: string
metadata?: Record
}): void
// Request permission
ask(input: {
permission: string
patterns: string[]
always: string[]
metadata: Record
}): Promise
}
```
### Tool Schema
Use Zod for argument validation:
```typescript theme={null}
import { tool } from '@opencode-ai/plugin'
tool({
description: 'Example tool with various argument types',
args: {
// String
name: tool.schema.string().describe('User name'),
// Number
age: tool.schema.number().min(0).max(150).describe('User age'),
// Boolean
active: tool.schema.boolean().describe('Is active'),
// Optional
email: tool.schema.string().email().optional().describe('Email address'),
// Enum
role: tool.schema.enum(['admin', 'user', 'guest']).describe('User role'),
// Array
tags: tool.schema.array(tool.schema.string()).describe('Tags'),
// Object
metadata: tool.schema.object({
key: tool.schema.string(),
value: tool.schema.string(),
}).describe('Metadata'),
},
async execute(args, context) {
// args is fully typed
return 'Result'
},
})
```
See [Plugin Tools](/sdk/plugin-tools) for detailed tool documentation.
## Hooks
Plugins can implement various hooks to customize behavior:
### Event Hook
Listen to all server events:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
event: async (input) => {
const { event } = input
if (event.type === 'session.created') {
console.log('New session:', event.properties.info.id)
}
if (event.type === 'message.updated') {
console.log('Message updated:', event.properties.info.id)
}
},
}
}
```
### Config Hook
Modify configuration:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
config: async (config) => {
console.log('Config loaded:', config.model)
// Can modify config here
},
}
}
```
### Chat Hooks
#### chat.message
Called when a new message is received:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'chat.message': async (input, output) => {
const { sessionID, agent, model } = input
const { message, parts } = output
console.log(`Message in session ${sessionID}:`)
console.log(`Agent: ${agent}`)
console.log(`Model: ${model?.providerID}/${model?.modelID}`)
console.log(`Parts: ${parts.length}`)
},
}
}
```
#### chat.params
Modify LLM parameters:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'chat.params': async (input, output) => {
const { agent, model } = input
// Adjust temperature based on agent
if (agent === 'build') {
output.temperature = 0.7
} else if (agent === 'plan') {
output.temperature = 0.3
}
// Add custom options
output.options.customParam = 'value'
},
}
}
```
#### chat.headers
Add custom headers to LLM requests:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'chat.headers': async (input, output) => {
output.headers['X-Custom-Header'] = 'value'
output.headers['X-Session-ID'] = input.sessionID
},
}
}
```
### Permission Hook
Control permission requests:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'permission.ask': async (permission, output) => {
// Auto-approve certain patterns
if (permission.type === 'bash' && permission.pattern?.includes('npm')) {
output.status = 'allow'
}
// Deny dangerous operations
if (permission.pattern?.includes('rm -rf')) {
output.status = 'deny'
}
},
}
}
```
### Command Hook
Run code before command execution:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'command.execute.before': async (input, output) => {
const { command, sessionID, arguments: args } = input
console.log(`Executing command: ${command} ${args}`)
// Add context parts
output.parts.push({
type: 'text',
text: `Additional context for ${command}`,
})
},
}
}
```
### Tool Hooks
#### tool.execute.before
Called before tool execution:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'tool.execute.before': async (input, output) => {
const { tool, sessionID, callID } = input
console.log(`Tool ${tool} called`)
// Modify arguments
output.args.modified = true
},
}
}
```
#### tool.execute.after
Called after tool execution:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'tool.execute.after': async (input, output) => {
const { tool, args } = input
// Modify output
output.output += '\n\nProcessed by plugin'
output.metadata.processed = true
},
}
}
```
#### tool.definition
Modify tool definitions sent to the LLM:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'tool.definition': async (input, output) => {
if (input.toolID === 'bash') {
// Make bash tool description more specific
output.description += ' Use this for running shell commands.'
}
},
}
}
```
### Shell Environment Hook
Customize shell environment:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'shell.env': async (input, output) => {
// Add custom environment variables
output.env.CUSTOM_VAR = 'value'
output.env.PATH = `/custom/path:${output.env.PATH}`
},
}
}
```
### Auth Hook
Handle authentication for custom providers:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
auth: {
provider: 'my-provider',
methods: [
{
type: 'api',
label: 'API Key',
prompts: [
{
type: 'text',
key: 'apiKey',
message: 'Enter your API key',
validate: (value) => {
if (!value.startsWith('sk-')) {
return 'Invalid API key format'
}
},
},
],
async authorize(inputs) {
// Validate the API key
const isValid = await validateKey(inputs.apiKey)
if (isValid) {
return { type: 'success', key: inputs.apiKey }
}
return { type: 'failed' }
},
},
{
type: 'oauth',
label: 'OAuth',
async authorize() {
const authUrl = 'https://auth.example.com'
return {
url: authUrl,
instructions: 'Open this URL to authenticate',
method: 'auto',
async callback() {
// Handle OAuth callback
const tokens = await waitForCallback()
return {
type: 'success',
refresh: tokens.refresh,
access: tokens.access,
expires: tokens.expires,
}
},
}
},
},
],
},
}
}
```
## Experimental Hooks
These hooks may change in future versions:
### experimental.chat.messages.transform
Transform messages before sending to LLM:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'experimental.chat.messages.transform': async (input, output) => {
// Modify messages array
output.messages = output.messages.filter(
(msg) => msg.info.role === 'user'
)
},
}
}
```
### experimental.chat.system.transform
Transform system prompt:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'experimental.chat.system.transform': async (input, output) => {
// Add to system prompt
output.system.push('Additional system instructions')
},
}
}
```
### experimental.session.compacting
Customize session compaction:
```typescript theme={null}
export const MyPlugin: Plugin = async (ctx) => {
return {
'experimental.session.compacting': async (input, output) => {
// Add context for compaction
output.context.push('Important context to preserve')
// Or replace the entire prompt
output.prompt = 'Custom compaction prompt'
},
}
}
```
## Complete Example
Here's a complete plugin with multiple features:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
export const DatabasePlugin: Plugin = async (ctx) => {
// Initialize database connection
const db = await connectDB(ctx.directory)
return {
// Custom tools
tool: {
query_db: tool({
description: 'Query the database',
args: {
sql: tool.schema.string().describe('SQL query'),
},
async execute(args, context) {
const results = await db.query(args.sql)
return JSON.stringify(results, null, 2)
},
}),
},
// Listen to events
event: async (input) => {
if (input.event.type === 'session.created') {
// Log new sessions to database
await db.logSession(input.event.properties.info)
}
},
// Customize chat parameters
'chat.params': async (input, output) => {
// Use lower temperature for database queries
if (input.agent === 'database') {
output.temperature = 0.1
}
},
// Add custom context
'command.execute.before': async (input, output) => {
if (input.command === 'query') {
// Add database schema context
const schema = await db.getSchema()
output.parts.push({
type: 'text',
text: `Database schema:\n${schema}`,
})
}
},
}
}
```
## Next Steps
Detailed tool creation guide
Real-world plugin examples
# Plugin Examples
Source: https://anomalyco-opencode.mintlify.app/sdk/plugin-examples
Complete examples of OpenCode plugins
## Simple Tool Plugin
A minimal plugin with a custom tool:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
export const ExamplePlugin: Plugin = async (ctx) => {
return {
tool: {
mytool: tool({
description: 'This is a custom tool',
args: {
foo: tool.schema.string().describe('foo'),
},
async execute(args) {
return `Hello ${args.foo}!`
},
}),
},
}
}
```
**File location**: `./plugins/example.ts`
**Configuration**:
```json theme={null}
{
"plugin": ["./plugins/example.ts"]
}
```
**Usage**: The AI can now call `mytool` with a string argument.
***
## GitHub Integration Plugin
Integrate with GitHub API:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
import { Octokit } from '@octokit/rest'
export const GitHubPlugin: Plugin = async (ctx) => {
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
})
return {
tool: {
github_search_repos: tool({
description: 'Search GitHub repositories by query string. Returns repo name, description, stars, and URL.',
args: {
query: tool.schema.string().describe('Search query'),
language: tool.schema.string().optional().describe('Filter by programming language'),
limit: tool.schema.number().min(1).max(100).optional().describe('Max results (default 10)'),
},
async execute(args, context) {
context.metadata({ title: `Searching: ${args.query}` })
let q = args.query
if (args.language) {
q += ` language:${args.language}`
}
const result = await octokit.search.repos({
q,
per_page: args.limit ?? 10,
sort: 'stars',
order: 'desc',
})
const repos = result.data.items.map(repo => ({
name: repo.full_name,
description: repo.description,
stars: repo.stargazers_count,
url: repo.html_url,
language: repo.language,
}))
return JSON.stringify({ count: repos.length, repos }, null, 2)
},
}),
github_create_issue: tool({
description: 'Create a GitHub issue in the current repository',
args: {
title: tool.schema.string().describe('Issue title'),
body: tool.schema.string().describe('Issue description'),
labels: tool.schema.array(tool.schema.string()).optional().describe('Issue labels'),
},
async execute(args, context) {
// Get repo from git remote
const remote = await ctx.$`git remote get-url origin`.text()
const match = remote.match(/github\.com[\/:](.+?)\/(.+?)(\.git)?$/)
if (!match) {
return 'Error: Not a GitHub repository'
}
const [, owner, repo] = match
context.metadata({ title: `Creating issue in ${owner}/${repo}` })
const issue = await octokit.issues.create({
owner,
repo,
title: args.title,
body: args.body,
labels: args.labels,
})
return JSON.stringify({
number: issue.data.number,
url: issue.data.html_url,
}, null, 2)
},
}),
},
}
}
```
***
## Database Query Plugin
Query and manage databases:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
import { Database } from 'bun:sqlite'
import { join } from 'path'
export const DatabasePlugin: Plugin = async (ctx) => {
const dbPath = join(ctx.directory, 'app.db')
const db = new Database(dbPath)
return {
tool: {
db_query: tool({
description: 'Execute a SQL SELECT query on the application database',
args: {
query: tool.schema.string().describe('SQL SELECT query'),
limit: tool.schema.number().min(1).max(1000).optional().describe('Row limit'),
},
async execute(args, context) {
const query = args.query.trim().toLowerCase()
if (!query.startsWith('select')) {
return 'Error: Only SELECT queries allowed'
}
context.metadata({ title: 'Querying database' })
try {
let sql = args.query
if (args.limit && !query.includes('limit')) {
sql += ` LIMIT ${args.limit}`
}
const results = db.query(sql).all()
return JSON.stringify({
rowCount: results.length,
data: results,
}, null, 2)
} catch (error) {
return `Error: ${error.message}`
}
},
}),
db_schema: tool({
description: 'Get the database schema (tables and columns)',
args: {},
async execute(args, context) {
const tables = db.query(
"SELECT name FROM sqlite_master WHERE type='table'"
).all() as { name: string }[]
const schema: Record = {}
for (const table of tables) {
const columns = db.query(`PRAGMA table_info(${table.name})`).all()
schema[table.name] = columns
}
return JSON.stringify(schema, null, 2)
},
}),
db_stats: tool({
description: 'Get database statistics (table sizes, row counts)',
args: {},
async execute(args, context) {
const tables = db.query(
"SELECT name FROM sqlite_master WHERE type='table'"
).all() as { name: string }[]
const stats = []
for (const table of tables) {
const count = db.query(`SELECT COUNT(*) as count FROM ${table.name}`)
.get() as { count: number }
stats.push({
table: table.name,
rows: count.count,
})
}
return JSON.stringify({ tables: stats.length, stats }, null, 2)
},
}),
},
}
}
```
***
## Testing Integration Plugin
Run tests and generate coverage:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
import { join } from 'path'
export const TestingPlugin: Plugin = async (ctx) => {
return {
tool: {
run_tests: tool({
description: 'Run test suite for the project. Supports Jest, Vitest, and Bun test.',
args: {
pattern: tool.schema.string().optional().describe('Test file pattern (e.g., "user.test.ts")'),
coverage: tool.schema.boolean().optional().describe('Generate coverage report'),
},
async execute(args, context) {
// Detect test runner
const packageJson = await Bun.file(join(ctx.directory, 'package.json')).json()
const testCommand = packageJson.scripts?.test
if (!testCommand) {
return 'Error: No test script found in package.json'
}
let cmd = 'npm test'
if (args.pattern) {
cmd += ` -- ${args.pattern}`
}
if (args.coverage) {
cmd += ' --coverage'
}
context.metadata({ title: 'Running tests' })
const result = await ctx.$`${cmd}`.quiet().nothrow()
return JSON.stringify({
exitCode: result.exitCode,
stdout: result.stdout.toString(),
stderr: result.stderr.toString(),
}, null, 2)
},
}),
analyze_coverage: tool({
description: 'Analyze test coverage and find uncovered code',
args: {},
async execute(args, context) {
context.metadata({ title: 'Analyzing coverage' })
// Run tests with coverage
await ctx.$`npm test -- --coverage`.quiet().nothrow()
// Parse coverage report
const coverageFile = join(ctx.directory, 'coverage/coverage-summary.json')
const coverage = await Bun.file(coverageFile).json()
const summary = {
statements: coverage.total.statements.pct,
branches: coverage.total.branches.pct,
functions: coverage.total.functions.pct,
lines: coverage.total.lines.pct,
}
// Find files with low coverage
const lowCoverage = Object.entries(coverage)
.filter(([path, data]: any) => {
return path !== 'total' && data.lines.pct < 80
})
.map(([path, data]: any) => ({
file: path,
coverage: data.lines.pct,
}))
.sort((a, b) => a.coverage - b.coverage)
return JSON.stringify({ summary, lowCoverage }, null, 2)
},
}),
},
}
}
```
***
## Documentation Generator Plugin
Generate and update documentation:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
import { readFile, writeFile } from 'fs/promises'
import { join } from 'path'
import { glob } from 'glob'
export const DocsPlugin: Plugin = async (ctx) => {
return {
tool: {
generate_api_docs: tool({
description: 'Generate API documentation from TypeScript source files',
args: {
pattern: tool.schema.string().describe('File pattern (e.g., "src/**/*.ts")'),
output: tool.schema.string().describe('Output file path'),
},
async execute(args, context) {
await context.ask({
permission: 'file_write',
patterns: [args.output],
always: [],
metadata: { operation: 'generate_docs' },
})
context.metadata({ title: 'Scanning source files' })
const files = await glob(args.pattern, {
cwd: ctx.directory,
absolute: true,
})
const docs: any[] = []
for (const file of files) {
context.metadata({ title: `Processing ${file}` })
const content = await readFile(file, 'utf-8')
// Simple parser for exported functions with JSDoc
const regex = /\/\*\*([^*]|\*(?!\/))*\*\/\s*export\s+(?:async\s+)?function\s+(\w+)/g
let match
while ((match = regex.exec(content)) !== null) {
const [fullMatch, jsDoc, functionName] = match
docs.push({
file: file.replace(ctx.directory, ''),
name: functionName,
docs: jsDoc.trim(),
})
}
}
// Generate markdown
let markdown = '# API Documentation\n\n'
for (const doc of docs) {
markdown += `## ${doc.name}\n\n`
markdown += `**File**: ${doc.file}\n\n`
markdown += `${doc.docs}\n\n`
}
const outputPath = join(ctx.directory, args.output)
await writeFile(outputPath, markdown, 'utf-8')
return `Generated documentation for ${docs.length} functions in ${args.output}`
},
}),
update_readme: tool({
description: 'Update README.md with project statistics',
args: {},
async execute(args, context) {
const readmePath = join(ctx.directory, 'README.md')
await context.ask({
permission: 'file_write',
patterns: ['README.md'],
always: [],
metadata: { operation: 'update_readme' },
})
// Gather stats
const files = await glob('src/**/*.ts', { cwd: ctx.directory })
const tests = await glob('**/*.test.ts', { cwd: ctx.directory })
let totalLines = 0
for (const file of files) {
const content = await readFile(join(ctx.directory, file), 'utf-8')
totalLines += content.split('\n').length
}
// Update README
let readme = await readFile(readmePath, 'utf-8')
const stats = `## Project Stats\n\n` +
`- Files: ${files.length}\n` +
`- Tests: ${tests.length}\n` +
`- Lines of code: ${totalLines}\n`
// Replace or append stats section
if (readme.includes('## Project Stats')) {
readme = readme.replace(/## Project Stats[\s\S]*?(?=##|$)/, stats)
} else {
readme += '\n\n' + stats
}
await writeFile(readmePath, readme, 'utf-8')
return 'Updated README.md with project statistics'
},
}),
},
}
}
```
***
## Performance Monitoring Plugin
Track and analyze performance:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
import { Database } from 'bun:sqlite'
import { join } from 'path'
export const PerformancePlugin: Plugin = async (ctx) => {
const dbPath = join(ctx.directory, '.opencode/metrics.db')
const db = new Database(dbPath)
// Initialize metrics table
db.run(`
CREATE TABLE IF NOT EXISTS metrics (
id INTEGER PRIMARY KEY,
timestamp INTEGER,
session_id TEXT,
message_id TEXT,
metric_name TEXT,
metric_value REAL,
metadata TEXT
)
`)
return {
tool: {
log_metric: tool({
description: 'Log a performance metric',
args: {
name: tool.schema.string().describe('Metric name'),
value: tool.schema.number().describe('Metric value'),
metadata: tool.schema.record(tool.schema.string()).optional().describe('Additional metadata'),
},
async execute(args, context) {
db.run(
'INSERT INTO metrics (timestamp, session_id, message_id, metric_name, metric_value, metadata) VALUES (?, ?, ?, ?, ?, ?)',
Date.now(),
context.sessionID,
context.messageID,
args.name,
args.value,
JSON.stringify(args.metadata ?? {})
)
return `Logged metric: ${args.name} = ${args.value}`
},
}),
analyze_metrics: tool({
description: 'Analyze performance metrics for a given metric name',
args: {
name: tool.schema.string().describe('Metric name to analyze'),
hours: tool.schema.number().min(1).optional().describe('Hours of history (default 24)'),
},
async execute(args, context) {
const hours = args.hours ?? 24
const since = Date.now() - (hours * 60 * 60 * 1000)
const metrics = db.query(
'SELECT * FROM metrics WHERE metric_name = ? AND timestamp > ? ORDER BY timestamp',
args.name,
since
).all() as any[]
if (metrics.length === 0) {
return `No metrics found for "${args.name}" in the last ${hours} hours`
}
const values = metrics.map(m => m.metric_value)
const avg = values.reduce((a, b) => a + b, 0) / values.length
const min = Math.min(...values)
const max = Math.max(...values)
// Simple trend detection
const recentAvg = values.slice(-5).reduce((a, b) => a + b, 0) / Math.min(5, values.length)
const trend = recentAvg > avg ? 'increasing' : recentAvg < avg ? 'decreasing' : 'stable'
return JSON.stringify({
metric: args.name,
count: metrics.length,
average: avg.toFixed(2),
min,
max,
trend,
recent: values.slice(-10),
}, null, 2)
},
}),
},
// Hook to log message completion times
'chat.message': async (input, output) => {
const { sessionID, messageID } = input
const { message } = output
if (message.role === 'assistant' && message.time.completed) {
const duration = message.time.completed - message.time.created
db.run(
'INSERT INTO metrics (timestamp, session_id, message_id, metric_name, metric_value, metadata) VALUES (?, ?, ?, ?, ?, ?)',
Date.now(),
sessionID,
messageID,
'message_duration',
duration,
JSON.stringify({ model: message.modelID })
)
}
},
}
}
```
***
## Multi-Tool Plugin
Combine multiple tools in one plugin:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
export const UtilityPlugin: Plugin = async (ctx) => {
return {
tool: {
// Time utilities
format_date: tool({
description: 'Format a timestamp into a human-readable date',
args: {
timestamp: tool.schema.number().describe('Unix timestamp'),
format: tool.schema.enum(['short', 'long', 'iso']).describe('Date format'),
},
async execute(args) {
const date = new Date(args.timestamp)
switch (args.format) {
case 'short':
return date.toLocaleDateString()
case 'long':
return date.toLocaleString()
case 'iso':
return date.toISOString()
}
},
}),
// Text utilities
count_words: tool({
description: 'Count words in a text string',
args: {
text: tool.schema.string().describe('Text to analyze'),
},
async execute(args) {
const words = args.text.trim().split(/\s+/).length
const chars = args.text.length
const lines = args.text.split('\n').length
return JSON.stringify({ words, chars, lines }, null, 2)
},
}),
// Math utilities
calculate: tool({
description: 'Evaluate a mathematical expression',
args: {
expression: tool.schema.string().describe('Math expression (e.g., "2 + 2 * 3")'),
},
async execute(args) {
try {
// Simple eval for demo - use a proper math parser in production
const result = eval(args.expression)
return String(result)
} catch (error) {
return `Error: Invalid expression`
}
},
}),
// JSON utilities
validate_json: tool({
description: 'Validate and format JSON',
args: {
json: tool.schema.string().describe('JSON string to validate'),
},
async execute(args) {
try {
const parsed = JSON.parse(args.json)
return JSON.stringify({ valid: true, formatted: JSON.stringify(parsed, null, 2) })
} catch (error) {
return JSON.stringify({ valid: false, error: error.message })
}
},
}),
},
}
}
```
## Publishing Plugins
Publish your plugin as an npm package:
**package.json**:
```json theme={null}
{
"name": "@yourname/opencode-plugin-example",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"peerDependencies": {
"@opencode-ai/plugin": "*"
}
}
```
Users can then install and use your plugin:
```bash theme={null}
npm install @yourname/opencode-plugin-example
```
```json theme={null}
{
"plugin": ["@yourname/opencode-plugin-example"]
}
```
## Next Steps
Complete plugin API reference
Tool creation guide
# Plugin Tools
Source: https://anomalyco-opencode.mintlify.app/sdk/plugin-tools
Create custom tools that AI agents can use
## Overview
Tools are functions that AI agents can call to perform actions. OpenCode's plugin system makes it easy to create custom tools with type-safe arguments and execution contexts.
## Basic Tool
Use the `tool()` helper to define a tool:
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
export const MyPlugin: Plugin = async (ctx) => {
return {
tool: {
my_tool: tool({
description: 'Description of what the tool does',
args: {
param: tool.schema.string().describe('Parameter description'),
},
async execute(args, context) {
// Tool implementation
return 'Result as string'
},
}),
},
}
}
```
## Tool Structure
### Description
The description tells the AI when and how to use the tool. Be clear and specific:
```typescript theme={null}
tool({
description: 'Search for user records in the database by name, email, or ID',
// ...
})
```
Good descriptions help the AI understand when to use your tool. Include:
* What the tool does
* When to use it
* What kind of input it expects
### Arguments
Define arguments using Zod schemas via `tool.schema`:
```typescript theme={null}
import { tool } from '@opencode-ai/plugin'
tool({
description: 'Example tool',
args: {
// Required string
name: tool.schema.string().describe('User name'),
// Optional number
age: tool.schema.number().optional().describe('User age'),
// String with constraints
email: tool.schema
.string()
.email()
.describe('Email address'),
// Enum
role: tool.schema
.enum(['admin', 'user', 'guest'])
.describe('User role'),
// Boolean
active: tool.schema.boolean().describe('Is user active'),
// Array
tags: tool.schema
.array(tool.schema.string())
.describe('User tags'),
// Object
settings: tool.schema.object({
theme: tool.schema.string(),
notifications: tool.schema.boolean(),
}).optional().describe('User settings'),
},
async execute(args, context) {
// args is fully typed based on your schema
console.log(args.name) // string
console.log(args.age) // number | undefined
console.log(args.role) // 'admin' | 'user' | 'guest'
return 'Success'
},
})
```
Always add `.describe()` to your arguments. These descriptions help the AI understand what values to provide.
### Execute Function
The execute function receives typed arguments and a context object:
```typescript theme={null}
tool({
description: 'Process data',
args: {
data: tool.schema.string().describe('Data to process'),
},
async execute(args, context) {
// args.data is typed as string
// Access context
const sessionID = context.sessionID
const directory = context.directory
// Check for cancellation
if (context.abort.aborted) {
return 'Cancelled'
}
// Update tool metadata
context.metadata({
title: 'Processing...',
metadata: { status: 'running' },
})
// Do work
const result = await processData(args.data)
// Must return a string
return JSON.stringify(result)
},
})
```
## Tool Context
The execution context provides information and utilities:
### Context Properties
Current session ID
Current message ID
Current agent name (e.g., `'build'`, `'plan'`)
Current project directory. Use this instead of `process.cwd()` for path resolution.
Project worktree root. Useful for generating stable relative paths.
Signal for cancellation. Check `abort.aborted` before long operations.
### Context Methods
#### metadata()
Update tool status and metadata:
```typescript theme={null}
context.metadata({
title: 'Current operation',
metadata: {
progress: 50,
status: 'running',
},
})
```
Tool status title shown in UI
Custom metadata attached to the tool execution
#### ask()
Request permission from the user:
```typescript theme={null}
await context.ask({
permission: 'file_write',
patterns: ['src/**/*.ts'],
always: [],
metadata: {
operation: 'write',
files: ['src/index.ts'],
},
})
```
Permission type
Patterns affected by this permission
Patterns to always allow (empty for none)
Additional context for the permission request
## Example Tools
### Database Query Tool
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
import { Database } from './db'
export const DatabasePlugin: Plugin = async (ctx) => {
const db = new Database(ctx.directory)
return {
tool: {
query_database: tool({
description: 'Execute a SQL query on the project database. Use for searching, counting, or analyzing data.',
args: {
query: tool.schema
.string()
.describe('SQL query to execute. Must be a SELECT statement.'),
limit: tool.schema
.number()
.min(1)
.max(1000)
.optional()
.describe('Maximum number of rows to return (default 100)'),
},
async execute(args, context) {
// Validate query is SELECT only
if (!args.query.trim().toLowerCase().startsWith('select')) {
return 'Error: Only SELECT queries are allowed'
}
context.metadata({
title: 'Querying database',
metadata: { query: args.query },
})
try {
const results = await db.query(args.query, args.limit ?? 100)
return JSON.stringify({
rows: results.length,
data: results,
}, null, 2)
} catch (error) {
return `Error: ${error.message}`
}
},
}),
get_schema: tool({
description: 'Get the database schema showing all tables and their columns',
args: {},
async execute(args, context) {
const schema = await db.getSchema()
return JSON.stringify(schema, null, 2)
},
}),
},
}
}
```
### API Request Tool
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
export const ApiPlugin: Plugin = async (ctx) => {
return {
tool: {
api_request: tool({
description: 'Make HTTP requests to external APIs. Supports GET, POST, PUT, PATCH, DELETE.',
args: {
method: tool.schema
.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
.describe('HTTP method'),
url: tool.schema
.string()
.url()
.describe('Full URL to request'),
headers: tool.schema
.record(tool.schema.string())
.optional()
.describe('HTTP headers as key-value pairs'),
body: tool.schema
.string()
.optional()
.describe('Request body (for POST/PUT/PATCH)'),
},
async execute(args, context) {
context.metadata({
title: `${args.method} ${args.url}`,
})
try {
const response = await fetch(args.url, {
method: args.method,
headers: args.headers,
body: args.body,
signal: context.abort,
})
const data = await response.text()
return JSON.stringify({
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers),
body: data,
}, null, 2)
} catch (error) {
if (error.name === 'AbortError') {
return 'Request cancelled'
}
return `Error: ${error.message}`
}
},
}),
},
}
}
```
### File Processing Tool
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
import { readFile, writeFile } from 'fs/promises'
import { join } from 'path'
export const FilePlugin: Plugin = async (ctx) => {
return {
tool: {
process_file: tool({
description: 'Process a file with custom transformation',
args: {
path: tool.schema
.string()
.describe('Relative path to the file'),
operation: tool.schema
.enum(['uppercase', 'lowercase', 'reverse'])
.describe('Transformation to apply'),
write: tool.schema
.boolean()
.optional()
.describe('Write result back to file (default false)'),
},
async execute(args, context) {
const filePath = join(context.directory, args.path)
// Request permission if writing
if (args.write) {
await context.ask({
permission: 'file_write',
patterns: [args.path],
always: [],
metadata: { operation: 'transform' },
})
}
context.metadata({
title: `Processing ${args.path}`,
metadata: { operation: args.operation },
})
// Read file
const content = await readFile(filePath, 'utf-8')
// Transform
let result: string
switch (args.operation) {
case 'uppercase':
result = content.toUpperCase()
break
case 'lowercase':
result = content.toLowerCase()
break
case 'reverse':
result = content.split('').reverse().join('')
break
}
// Write if requested
if (args.write) {
await writeFile(filePath, result, 'utf-8')
return `Transformed and wrote ${args.path}`
}
return result
},
}),
},
}
}
```
### Shell Command Tool
```typescript theme={null}
import { Plugin, tool } from '@opencode-ai/plugin'
export const ShellPlugin: Plugin = async (ctx) => {
return {
tool: {
run_command: tool({
description: 'Run a custom shell command in the project directory',
args: {
command: tool.schema
.string()
.describe('Command to execute'),
cwd: tool.schema
.string()
.optional()
.describe('Working directory (relative to project root)'),
},
async execute(args, context) {
const cwd = args.cwd
? join(context.directory, args.cwd)
: context.directory
context.metadata({
title: `Running: ${args.command}`,
})
try {
// Use the shell helper from context
const result = await ctx.$`cd ${cwd} && ${args.command}`
return JSON.stringify({
exitCode: result.exitCode,
stdout: result.stdout.toString(),
stderr: result.stderr.toString(),
}, null, 2)
} catch (error) {
return `Error: ${error.message}`
}
},
}),
},
}
}
```
## Best Practices
### 1. Clear Descriptions
Be specific about what your tool does:
```typescript theme={null}
// Good
description: 'Search GitHub repositories by name, language, or topic. Returns repository name, description, stars, and URL.'
// Bad
description: 'Search GitHub'
```
### 2. Validate Input
Use Zod constraints to validate arguments:
```typescript theme={null}
args: {
email: tool.schema.string().email(),
age: tool.schema.number().min(0).max(150),
url: tool.schema.string().url(),
}
```
### 3. Handle Errors
Return error messages as strings:
```typescript theme={null}
try {
const result = await operation()
return JSON.stringify(result)
} catch (error) {
return `Error: ${error.message}`
}
```
### 4. Check Cancellation
Respect the abort signal:
```typescript theme={null}
if (context.abort.aborted) {
return 'Operation cancelled'
}
// Or pass to async operations
await fetch(url, { signal: context.abort })
```
### 5. Update Metadata
Keep the UI informed:
```typescript theme={null}
context.metadata({ title: 'Processing...' })
for (const item of items) {
context.metadata({
title: `Processing ${item.name}`,
metadata: { current: item.name },
})
await process(item)
}
context.metadata({ title: 'Complete' })
```
### 6. Use Context Paths
Always use `context.directory` for path resolution:
```typescript theme={null}
// Good
const filePath = join(context.directory, args.path)
// Bad
const filePath = join(process.cwd(), args.path)
```
### 7. Return Structured Data
Return JSON for complex data:
```typescript theme={null}
return JSON.stringify({
success: true,
data: results,
count: results.length,
}, null, 2)
```
### 8. Request Permissions
Ask before destructive operations:
```typescript theme={null}
if (args.delete) {
await context.ask({
permission: 'file_delete',
patterns: [args.path],
always: [],
metadata: { operation: 'delete' },
})
}
```
## Debugging Tools
Add logging to debug your tools:
```typescript theme={null}
tool({
description: 'Debug tool',
args: { input: tool.schema.string() },
async execute(args, context) {
console.log('Tool called:', context.sessionID)
console.log('Arguments:', args)
console.log('Directory:', context.directory)
// Use the SDK client from plugin context
await ctx.client.app.log({
body: {
service: 'my-tool',
level: 'info',
message: `Executed with ${args.input}`,
},
})
return 'Debug info logged'
},
})
```
## Next Steps
See complete plugin examples
Learn about other plugin hooks
# TypeScript Types
Source: https://anomalyco-opencode.mintlify.app/sdk/types
Complete TypeScript type definitions for the OpenCode SDK
## Overview
The OpenCode SDK includes comprehensive TypeScript definitions generated from the OpenAPI specification. All types are exported from the main package.
```typescript theme={null}
import type {
Session,
Message,
Part,
Config,
Agent,
Model,
Provider,
// ... and many more
} from '@opencode-ai/sdk'
```
View the complete types file in the [OpenCode repository](https://github.com/anomalyco/opencode/blob/dev/packages/sdk/js/src/gen/types.gen.ts).
## Core Types
### Session
Represents an AI coding session.
```typescript theme={null}
type Session = {
id: string
projectID: string
directory: string
parentID?: string
title: string
version: string
time: {
created: number
updated: number
compacting?: number
}
summary?: {
additions: number
deletions: number
files: number
diffs?: FileDiff[]
}
share?: {
url: string
}
revert?: {
messageID: string
partID?: string
snapshot?: string
diff?: string
}
}
```
### Message
Union type for user and assistant messages.
```typescript theme={null}
type Message = UserMessage | AssistantMessage
type UserMessage = {
id: string
sessionID: string
role: 'user'
time: { created: number }
agent: string
model: {
providerID: string
modelID: string
}
system?: string
tools?: Record
summary?: {
title?: string
body?: string
diffs: FileDiff[]
}
}
type AssistantMessage = {
id: string
sessionID: string
role: 'assistant'
time: {
created: number
completed?: number
}
parentID: string
modelID: string
providerID: string
mode: string
path: {
cwd: string
root: string
}
cost: number
tokens: {
input: number
output: number
reasoning: number
cache: { read: number; write: number }
}
finish?: string
error?: MessageError
summary?: boolean
}
```
### Part
Message parts (text, file, tool use, etc.).
```typescript theme={null}
type Part =
| TextPart
| ReasoningPart
| FilePart
| ToolPart
| SubtaskPart
| StepStartPart
| StepFinishPart
| SnapshotPart
| PatchPart
| AgentPart
| RetryPart
| CompactionPart
type TextPart = {
id: string
sessionID: string
messageID: string
type: 'text'
text: string
synthetic?: boolean
ignored?: boolean
time?: { start: number; end?: number }
metadata?: Record
}
type ToolPart = {
id: string
sessionID: string
messageID: string
type: 'tool'
callID: string
tool: string
state: ToolState
metadata?: Record
}
type FilePart = {
id: string
sessionID: string
messageID: string
type: 'file'
mime: string
filename?: string
url: string
source?: FilePartSource
}
```
### ToolState
Tool execution state.
```typescript theme={null}
type ToolState =
| ToolStatePending
| ToolStateRunning
| ToolStateCompleted
| ToolStateError
type ToolStatePending = {
status: 'pending'
input: Record
raw: string
}
type ToolStateRunning = {
status: 'running'
input: Record
title?: string
metadata?: Record
time: { start: number }
}
type ToolStateCompleted = {
status: 'completed'
input: Record
output: string
title: string
metadata: Record
time: { start: number; end: number; compacted?: number }
attachments?: FilePart[]
}
type ToolStateError = {
status: 'error'
input: Record
error: string
metadata?: Record
time: { start: number; end: number }
}
```
## Configuration Types
### Config
Main configuration object.
```typescript theme={null}
type Config = {
$schema?: string
theme?: string
logLevel?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'
model?: string
small_model?: string
username?: string
agent?: Record
provider?: Record
mcp?: Record
command?: Record
plugin?: string[]
snapshot?: boolean
share?: 'manual' | 'auto' | 'disabled'
autoupdate?: boolean | 'notify'
instructions?: string[]
tools?: Record
permission?: PermissionConfig
keybinds?: KeybindsConfig
tui?: TuiConfig
// ... more options
}
```
### AgentConfig
Configuration for individual agents.
```typescript theme={null}
type AgentConfig = {
model?: string
temperature?: number
top_p?: number
prompt?: string
tools?: Record
disable?: boolean
description?: string
mode?: 'subagent' | 'primary' | 'all'
color?: string
maxSteps?: number
permission?: PermissionConfig
}
type PermissionConfig = {
edit?: 'ask' | 'allow' | 'deny'
bash?: 'ask' | 'allow' | 'deny' | Record
webfetch?: 'ask' | 'allow' | 'deny'
doom_loop?: 'ask' | 'allow' | 'deny'
external_directory?: 'ask' | 'allow' | 'deny'
}
```
### ProviderConfig
Provider configuration.
```typescript theme={null}
type ProviderConfig = {
api?: string
name?: string
env?: string[]
id?: string
npm?: string
models?: Record
whitelist?: string[]
blacklist?: string[]
options?: {
apiKey?: string
baseURL?: string
enterpriseUrl?: string
setCacheKey?: boolean
timeout?: number | false
}
}
type ModelConfig = {
id?: string
name?: string
release_date?: string
attachment?: boolean
reasoning?: boolean
temperature?: boolean
tool_call?: boolean
cost?: {
input: number
output: number
cache_read?: number
cache_write?: number
}
limit?: {
context: number
output: number
}
modalities?: {
input: ('text' | 'audio' | 'image' | 'video' | 'pdf')[]
output: ('text' | 'audio' | 'image' | 'video' | 'pdf')[]
}
experimental?: boolean
status?: 'alpha' | 'beta' | 'deprecated'
}
```
## Provider and Model Types
### Provider
Provider information.
```typescript theme={null}
type Provider = {
id: string
name: string
source: 'env' | 'config' | 'custom' | 'api'
env: string[]
key?: string
options: Record
models: Record
}
```
### Model
Model information.
```typescript theme={null}
type Model = {
id: string
providerID: string
api: {
id: string
url: string
npm: string
}
name: string
capabilities: {
temperature: boolean
reasoning: boolean
attachment: boolean
toolcall: boolean
input: {
text: boolean
audio: boolean
image: boolean
video: boolean
pdf: boolean
}
output: {
text: boolean
audio: boolean
image: boolean
video: boolean
pdf: boolean
}
}
cost: {
input: number
output: number
cache: { read: number; write: number }
experimentalOver200K?: {
input: number
output: number
cache: { read: number; write: number }
}
}
limit: {
context: number
output: number
}
status: 'alpha' | 'beta' | 'deprecated' | 'active'
options: Record
headers: Record
}
```
### Agent
Agent information.
```typescript theme={null}
type Agent = {
name: string
description?: string
mode: 'subagent' | 'primary' | 'all'
builtIn: boolean
topP?: number
temperature?: number
color?: string
permission: {
edit: 'ask' | 'allow' | 'deny'
bash: Record
webfetch?: 'ask' | 'allow' | 'deny'
doom_loop?: 'ask' | 'allow' | 'deny'
external_directory?: 'ask' | 'allow' | 'deny'
}
model?: { modelID: string; providerID: string }
prompt?: string
tools: Record
options: Record
maxSteps?: number
}
```
## Event Types
### Event
Union type for all server events.
```typescript theme={null}
type Event =
| EventSessionCreated
| EventSessionUpdated
| EventSessionDeleted
| EventSessionStatus
| EventSessionIdle
| EventMessageUpdated
| EventMessagePartUpdated
| EventMessageRemoved
| EventPermissionUpdated
| EventTodoUpdated
| EventFileEdited
// ... and many more
type EventSessionCreated = {
type: 'session.created'
properties: { info: Session }
}
type EventMessageUpdated = {
type: 'message.updated'
properties: { info: Message }
}
type EventMessagePartUpdated = {
type: 'message.part.updated'
properties: { part: Part; delta?: string }
}
```
### GlobalEvent
Event with directory context.
```typescript theme={null}
type GlobalEvent = {
directory: string
payload: Event
}
```
## File Types
### File
File status information.
```typescript theme={null}
type File = {
path: string
added: number
removed: number
status: 'added' | 'deleted' | 'modified'
}
```
### FileDiff
File diff information.
```typescript theme={null}
type FileDiff = {
file: string
before: string
after: string
additions: number
deletions: number
}
```
### FileContent
File content response.
```typescript theme={null}
type FileContent = {
type: 'text' | 'binary'
content: string
diff?: string
patch?: PatchInfo
encoding?: 'base64'
mimeType?: string
}
```
### Symbol
Workspace symbol.
```typescript theme={null}
type Symbol = {
name: string
kind: number
location: {
uri: string
range: Range
}
}
type Range = {
start: { line: number; character: number }
end: { line: number; character: number }
}
```
## Project Types
### Project
Project information.
```typescript theme={null}
type Project = {
id: string
worktree: string
vcsDir?: string
vcs?: 'git'
time: {
created: number
initialized?: number
}
}
```
### Path
Path information.
```typescript theme={null}
type Path = {
state: string
config: string
worktree: string
directory: string
}
```
## Error Types
### Message Errors
```typescript theme={null}
type MessageError =
| ProviderAuthError
| UnknownError
| MessageOutputLengthError
| MessageAbortedError
| ApiError
type ProviderAuthError = {
name: 'ProviderAuthError'
data: { providerID: string; message: string }
}
type ApiError = {
name: 'APIError'
data: {
message: string
statusCode?: number
isRetryable: boolean
responseHeaders?: Record
responseBody?: string
}
}
```
### API Errors
```typescript theme={null}
type BadRequestError = {
data: unknown
errors: Array>
success: false
}
type NotFoundError = {
name: 'NotFoundError'
data: { message: string }
}
```
## Permission Types
### Permission
Permission request.
```typescript theme={null}
type Permission = {
id: string
type: string
pattern?: string | string[]
sessionID: string
messageID: string
callID?: string
title: string
metadata: Record
time: { created: number }
}
```
## Todo Types
### Todo
Todo item.
```typescript theme={null}
type Todo = {
id: string
content: string
status: string
priority: string
}
```
## Command Types
### Command
Command definition.
```typescript theme={null}
type Command = {
name: string
description?: string
agent?: string
model?: string
template: string
subtask?: boolean
}
```
## Input Types
These types are used for creating messages:
```typescript theme={null}
type TextPartInput = {
id?: string
type: 'text'
text: string
synthetic?: boolean
ignored?: boolean
time?: { start: number; end?: number }
metadata?: Record
}
type FilePartInput = {
id?: string
type: 'file'
mime: string
filename?: string
url: string
source?: FilePartSource
}
type SubtaskPartInput = {
id?: string
type: 'subtask'
prompt: string
description: string
agent: string
}
```
## Usage Example
```typescript theme={null}
import type {
Session,
Message,
Part,
Config,
AgentConfig,
Event,
} from '@opencode-ai/sdk'
import { createOpencodeClient } from '@opencode-ai/sdk'
const client = createOpencodeClient()
// Types are inferred automatically
const sessions = await client.session.list()
const session: Session = sessions.data[0]
const messages = await client.session.messages({
path: { id: session.id },
})
for (const msg of messages.data) {
const info: Message = msg.info
const parts: Part[] = msg.parts
console.log(`${info.role}: ${parts.length} parts`)
}
// Subscribe to events
const events = await client.event.subscribe()
for await (const event of events.stream) {
const evt: Event = event.payload
if (evt.type === 'message.updated') {
console.log('Message updated:', evt.properties.info.id)
}
}
```
# Server Architecture
Source: https://anomalyco-opencode.mintlify.app/server
Deep dive into the OpenCode server: HTTP API, authentication, mDNS discovery, and programmatic control
The `opencode serve` command runs a headless HTTP server that exposes an OpenAPI-compliant REST API. This architecture enables multiple clients (TUI, web, IDE plugins, custom integrations) to interact with the same OpenCode instance programmatically.
## Architecture Overview
When you run `opencode`, it starts both:
* **Server**: HTTP API server (Hono + Bun runtime) exposing OpenAPI 3.1 endpoints
* **TUI Client**: Terminal interface that communicates with the server via HTTP
This separation allows:
* Multiple simultaneous clients connecting to one server
* Programmatic control via HTTP APIs
* Real-time event streaming via Server-Sent Events (SSE)
* Cross-platform compatibility (local, remote, containerized)
The TUI and server communicate over HTTP on `localhost`. When using proxies, you **must** bypass `localhost` and `127.0.0.1` to prevent routing loops.
## Starting the Server
### Standalone Mode
Run a dedicated server without the TUI:
```bash theme={null}
opencode serve --port 4096 --hostname 127.0.0.1
```
Port to listen on. Use `0` to auto-assign a free port.
Hostname/IP to bind to. Use `0.0.0.0` to accept connections from any interface (requires authentication).
Enable mDNS (Bonjour) service discovery for local network clients.
Custom domain name for the mDNS service announcement.
Additional CORS origins to allow. Can be specified multiple times.
### Example with CORS
```bash theme={null}
opencode serve \
--port 4096 \
--hostname 0.0.0.0 \
--cors http://localhost:5173 \
--cors https://app.example.com
```
### Connect to Existing Server
When starting the TUI, specify connection details:
```bash theme={null}
opencode --hostname 127.0.0.1 --port 4096
```
The TUI will connect to the existing server instead of starting a new one.
## Authentication
### HTTP Basic Auth
Protect your server with username/password authentication:
```bash theme={null}
export OPENCODE_SERVER_USERNAME="admin" # Optional, defaults to "opencode"
export OPENCODE_SERVER_PASSWORD="secure-password-123"
opencode serve --hostname 0.0.0.0
```
Always use authentication when binding to `0.0.0.0` or exposing the server beyond localhost.
Authentication applies to:
* `opencode serve` (standalone server)
* `opencode web` (web interface + server)
HTTP clients must include credentials:
```bash theme={null}
curl -u admin:secure-password-123 http://localhost:4096/global/health
```
### Authentication Flow
1. Server reads `OPENCODE_SERVER_PASSWORD` at startup
2. All requests (except OPTIONS preflight) require HTTP Basic Auth
3. Username defaults to `opencode` or uses `OPENCODE_SERVER_USERNAME`
4. Invalid credentials return `401 Unauthorized`
## OpenAPI Specification
The server publishes a complete OpenAPI 3.1 spec:
```bash theme={null}
curl http://localhost:4096/doc
```
This returns an interactive Swagger UI for exploring endpoints.
### Using the Spec
Use tools like `openapi-generator` or `swagger-codegen` to generate type-safe clients:
```bash theme={null}
# Fetch the spec
curl http://localhost:4096/doc -H "Accept: application/json" > opencode-api.json
# Generate TypeScript client
openapi-generator-cli generate \
-i opencode-api.json \
-g typescript-axios \
-o ./opencode-client
```
1. Open Postman/Insomnia
2. Import → OpenAPI URL
3. Enter: `http://localhost:4096/doc?format=json`
4. All endpoints will be available for testing
Use the spec for contract testing:
```typescript theme={null}
import { OpenAPIValidator } from 'express-openapi-validator';
const spec = await fetch('http://localhost:4096/doc?format=json').then(r => r.json());
// Use spec for validation in tests
```
## Core API Endpoints
### Health & Events
```bash GET /global/health theme={null}
curl http://localhost:4096/global/health
```
```json Response theme={null}
{
"healthy": true,
"version": "1.2.3"
}
```
Always `true` if server is running.
OpenCode version (e.g., `1.2.3`).
### Real-time Events (SSE)
```bash GET /global/event theme={null}
curl -N http://localhost:4096/global/event
```
```text Stream Output theme={null}
data: {"type":"server.connected","properties":{}}
data: {"type":"session.created","properties":{"sessionID":"abc123"}}
data: {"type":"server.heartbeat","properties":{}}
```
Server-Sent Events stream providing:
* `server.connected` - Initial connection event
* `server.heartbeat` - Keepalive every 10 seconds
* All bus events (session lifecycle, file changes, etc.)
SSE connections include `X-Accel-Buffering: no` header to prevent proxy buffering issues.
### Project Context
```bash GET /project/current theme={null}
curl http://localhost:4096/project/current?directory=/path/to/project
```
```json Response theme={null}
{
"id": "project-123",
"name": "my-app",
"path": "/path/to/project",
"isGit": true
}
```
Every request can include a `directory` parameter (query or header) to scope operations:
```bash theme={null}
# Via query parameter
curl "http://localhost:4096/session?directory=/workspace/app"
# Via header
curl -H "X-Opencode-Directory: /workspace/app" http://localhost:4096/session
```
### Sessions
Create and manage AI conversation sessions:
```bash POST /session theme={null}
curl -X POST http://localhost:4096/session \
-H "Content-Type: application/json" \
-d '{"title": "Add user authentication"}'
```
```json Response theme={null}
{
"id": "session-abc123",
"title": "Add user authentication",
"createdAt": 1708387200000,
"updatedAt": 1708387200000,
"parentID": null,
"shareID": null
}
```
Session title. Auto-generated if omitted.
Parent session ID for creating branched conversations.
### Send Messages
```bash POST /session/:id/message theme={null}
curl -X POST http://localhost:4096/session/abc123/message \
-H "Content-Type: application/json" \
-d '{
"parts": [
{"type": "text", "text": "Create a login form"}
]
}'
```
```json Response theme={null}
{
"info": {
"id": "msg-xyz",
"role": "assistant",
"createdAt": 1708387260000
},
"parts": [
{
"type": "text",
"text": "I'll create a login form..."
},
{
"type": "tool_use",
"name": "write",
"input": {...}
}
]
}
```
Array of message parts (text, images, files).
Override model (format: `provider/model`, e.g., `openai/gpt-4.1`).
Agent to use (e.g., `task`, `research`).
Send message without waiting for AI response.
### Async Messaging
For fire-and-forget messages, use the async endpoint:
```bash theme={null}
curl -X POST http://localhost:4096/session/abc123/prompt_async \
-H "Content-Type: application/json" \
-d '{"parts": [{"type": "text", "text": "Fix the bug"}]}'
# Returns: 204 No Content immediately
```
Monitor progress via event stream (`GET /event`).
## TUI Control API
Programmatically control the TUI (used by IDE plugins):
```bash POST /tui/append-prompt theme={null}
curl -X POST http://localhost:4096/tui/append-prompt \
-H "Content-Type: application/json" \
-d '{"text": "Add error handling"}'
```
```bash POST /tui/submit-prompt theme={null}
curl -X POST http://localhost:4096/tui/submit-prompt
```
Available TUI endpoints:
* `/tui/append-prompt` - Add text to prompt input
* `/tui/submit-prompt` - Submit current prompt
* `/tui/clear-prompt` - Clear prompt input
* `/tui/open-sessions` - Open session selector
* `/tui/open-models` - Open model selector
* `/tui/show-toast` - Display notification
* `/tui/execute-command` - Run slash command
## Advanced Features
### File Operations
```bash Find in Files theme={null}
curl "http://localhost:4096/find?pattern=async%20function"
```
```bash Find Files theme={null}
curl "http://localhost:4096/find/file?query=auth&type=file&limit=10"
```
```bash Read File theme={null}
curl "http://localhost:4096/file/content?path=/src/index.ts"
```
### LSP & Formatters
```bash theme={null}
# LSP server status
curl http://localhost:4096/lsp
# Formatter status
curl http://localhost:4096/formatter
# MCP server status
curl http://localhost:4096/mcp
```
### Session Sharing
```bash Share Session theme={null}
curl -X POST http://localhost:4096/session/abc123/share
```
```json Response theme={null}
{
"id": "abc123",
"shareID": "s_unique123",
"shareURL": "https://opncd.ai/s/s_unique123",
...
}
```
```bash Unshare Session theme={null}
curl -X DELETE http://localhost:4096/session/abc123/share
```
## Error Handling
The server returns structured errors:
```json 400 Bad Request theme={null}
{
"success": false,
"errors": [
{
"field": "parts",
"message": "Required"
}
],
"data": null
}
```
```json 404 Not Found theme={null}
{
"name": "NotFoundError",
"message": "Session not found",
"data": {
"sessionID": "invalid-id"
}
}
```
```json 500 Internal Error theme={null}
{
"name": "UnknownError",
"message": "Stack trace...",
"data": {}
}
```
All errors follow the `NamedError` pattern with:
* `name` - Error class name
* `message` - Human-readable description
* `data` - Contextual error details
## Server Implementation Details
### Technology Stack
* **Runtime**: Bun (high-performance JavaScript runtime)
* **Framework**: Hono (lightweight HTTP framework)
* **WebSockets**: Native Bun websocket support
* **SSE**: Hono streaming utilities
* **Validation**: Zod schemas with hono-openapi
### CORS Policy
Default allowed origins:
* `http://localhost:*` (any port)
* `http://127.0.0.1:*` (any port)
* `tauri://localhost` (Tauri desktop apps)
* `https://*.opencode.ai` (official web clients)
* Custom origins via `--cors` flag
### Request Lifecycle
1. **CORS preflight** - OPTIONS requests bypass auth
2. **Authentication** - HTTP Basic Auth if `OPENCODE_SERVER_PASSWORD` is set
3. **Logging** - Request method and path logged (except `/log` endpoint)
4. **Instance resolution** - Extract `directory` from query/header, load project instance
5. **Route handler** - Execute endpoint logic
6. **Error handling** - Convert exceptions to structured JSON responses
7. **Response** - JSON or SSE stream
### Performance Characteristics
* **Idle timeout**: Disabled (connections can stay open indefinitely)
* **SSE heartbeat**: 10-second intervals
* **Port fallback**: If `--port 0`, tries 4096 first, then random
* **Graceful shutdown**: Unpublishes mDNS before stopping
## Use Cases
Build custom UI clients (mobile apps, web dashboards) using the HTTP API.
Automate code review, testing, and documentation generation in pipelines.
Create editor extensions that communicate with OpenCode server.
Run OpenCode server on remote machines, connect from local clients.
## Next Steps
Configure proxies, certificates, and mDNS discovery.
Learn about sharing sessions and collaboration features.
Use the official TypeScript/JavaScript SDK for type-safe API access.
Diagnose and fix common server issues.
# Session Sharing
Source: https://anomalyco-opencode.mintlify.app/share
Share conversations, collaborate with teammates, and manage public session links
OpenCode's session sharing feature creates public links to your AI conversations, enabling collaboration, code reviews, debugging assistance, and knowledge sharing.
Shared sessions are **publicly accessible** to anyone with the link. Review content before sharing to avoid exposing sensitive information.
## How Session Sharing Works
Work on your code in a session as usual. The session contains:
* Full conversation history (your prompts + AI responses)
* File diffs and code changes
* Tool use records (file reads, writes, bash commands)
* Session metadata (title, creation date, model used)
Run `/share` command or use the API:
```bash theme={null}
# In TUI
/share
# Via API
curl -X POST http://localhost:4096/session//share
```
OpenCode creates a unique share URL:
```
https://opncd.ai/s/s_abc123xyz789
```
The URL is automatically copied to your clipboard (TUI) or returned in the API response.
Session data is uploaded to OpenCode's servers:
* All messages and responses
* File diffs (not full file contents)
* Session configuration
* Metadata (timestamps, model info)
Send the URL to teammates via:
* Slack/Teams/Discord
* Email
* GitHub PR comments
* Documentation
## Sharing Modes
OpenCode supports three sharing modes to match different workflows and security requirements.
### Manual (Default)
Sessions are **not** shared automatically. You explicitly control when to share.
```bash theme={null}
# Share current session
/share
# Unshare when done
/unshare
```
**Configuration** (optional):
```json opencode.json theme={null}
{
"$schema": "https://opncd.ai/config.json",
"share": "manual"
}
```
* Default safe mode for most users
* Working with proprietary code
* Only sharing specific debugging sessions
* Explicit control over what gets shared
### Auto-share
Every new session is **automatically** shared upon creation.
```json opencode.json theme={null}
{
"$schema": "https://opncd.ai/config.json",
"share": "auto"
}
```
* Open source projects
* Team collaboration where all sessions should be visible
* Educational/tutorial content creation
* Public coding demonstrations
Review session content regularly. Even with auto-share, you can `/unshare` individual sessions.
### Disabled
Sharing is completely **disabled**. The `/share` command will not work.
```json opencode.json theme={null}
{
"$schema": "https://opncd.ai/config.json",
"share": "disabled"
}
```
* Enterprise environments with strict data policies
* Highly sensitive projects (healthcare, finance, defense)
* Compliance requirements (HIPAA, SOC 2, ISO 27001)
* Air-gapped or offline development
For team-wide enforcement, commit `opencode.json` with `"share": "disabled"` to version control.
## Using the Share API
### Share a Session
```bash cURL theme={null}
curl -X POST http://localhost:4096/session/abc123/share
```
```json Response theme={null}
{
"id": "abc123",
"title": "Add authentication",
"shareID": "s_xyz789abc",
"shareURL": "https://opncd.ai/s/s_xyz789abc",
"createdAt": 1708387200000,
"updatedAt": 1708387200000,
"parentID": null
}
```
Unique identifier for the shared session (prefixed with `s_`).
Full public URL to the shared session.
### Unshare a Session
Remove public access and delete cloud data:
```bash cURL theme={null}
curl -X DELETE http://localhost:4096/session/abc123/share
```
```json Response theme={null}
{
"id": "abc123",
"title": "Add authentication",
"shareID": null,
"shareURL": null,
"createdAt": 1708387200000,
"updatedAt": 1708387260000,
"parentID": null
}
```
`shareID` and `shareURL` become `null` after unsharing. The link will return 404.
### Check Share Status
Query whether a session is currently shared:
```bash cURL theme={null}
curl http://localhost:4096/session/abc123
```
```json Shared Session theme={null}
{
"id": "abc123",
"shareID": "s_xyz789abc",
"shareURL": "https://opncd.ai/s/s_xyz789abc",
...
}
```
```json Not Shared theme={null}
{
"id": "abc123",
"shareID": null,
"shareURL": null,
...
}
```
## What Gets Shared
When you share a session, the following data is uploaded:
* All user prompts (your messages)
* All AI responses (assistant messages)
* Tool use records (file operations, bash commands)
* Timestamps and message order
* Model and provider information
* Diffs of changed files (hunks with context)
* File paths relative to project root
* **Not full file contents**, only changes made during the session
* Session title
* Creation and update timestamps
* Parent session ID (if forked)
* Agent used (e.g., `task`, `research`)
* Model configuration
* Task breakdown and planning
* Task completion status
* Task descriptions
### What is NOT Shared
**Data that remains local:**
* Full source code files (only diffs are shared)
* Environment variables
* API keys and secrets (never include these in prompts!)
* Local file paths outside the project
* Git history or repository metadata
* User's global OpenCode configuration
## Privacy and Security
### Data Retention
Shared sessions remain accessible **indefinitely** until explicitly unshared.
Data uploaded to `opncd.ai` servers.
Anyone with the URL can view the session.
Run `/unshare` to delete the share:
* Link becomes invalid (404)
* Data removed from servers within 24 hours
* Local session remains intact
Automatic expiration is not currently supported. Always manually unshare sessions when collaboration is complete.
### Access Control
Shared sessions use **URL-based access** (no authentication required):
* ✅ Simple sharing (no account needed)
* ✅ Works with any browser
* ❌ Anyone with the link can access
* ❌ Cannot restrict to specific users
**Best Practices:**
1. **Treat share URLs like passwords**
* Share via secure channels (encrypted chat, private repos)
* Avoid posting in public forums or social media
2. **Time-box sharing**
* Unshare immediately after collaboration ends
* Review active shares periodically
3. **Use short-lived channels**
* Send links via disappearing messages (Signal, Telegram)
* Share in private GitHub comments, not PR descriptions
4. **Monitor access** (future feature)
* Request: Star \[#issue-number] for access logs
### Compliance Considerations
For regulated industries (healthcare, finance, government):
**Do not share sessions** containing PHI (Protected Health Information). Use `"share": "disabled"` for HIPAA-regulated projects.
Shared sessions may contain personal data. Ensure compliance with:
* Data processing agreements
* User consent requirements
* Right to erasure (use `/unshare`)
Sharing may violate data residency or access control policies. Audit `"share": "auto"` configurations.
Government/defense contractors: Verify that sharing complies with ITAR, EAR, or similar regulations.
### Recommendations by Project Type
✅ **Auto-share enabled**
```json opencode.json theme={null}
{
"share": "auto"
}
```
Benefits:
* Community can learn from your process
* Easy debugging help from maintainers
* Transparent development
⚠️ **Manual sharing (default)**
```json opencode.json theme={null}
{
"share": "manual"
}
```
Guidelines:
* Only share for code reviews or pair programming
* Unshare after resolving the issue
* Avoid sharing sessions with business logic
🛑 **Sharing disabled**
```json opencode.json theme={null}
{
"share": "disabled"
}
```
Rationale:
* Prevents accidental data leaks
* Enforces data residency policies
* Complies with security audits
## Use Cases
Share a session showing the bug reproduction steps. Teammates can see the full context without screenshots.
Link to the session in your PR description. Reviewers see the reasoning behind changes.
Create "how we built X" posts by sharing sessions. Perfect for engineering blogs.
Share sessions demonstrating common workflows. New hires learn by example.
When asking for help in GitHub issues, share the session to provide full context.
Instructors can share sessions as interactive tutorials. Students see the AI's reasoning.
## Enterprise Features
For organizations needing advanced sharing controls:
Deploy your own share server:
* Data never leaves your infrastructure
* Integrate with SSO (SAML, OAuth)
* Custom retention policies
* Audit logs for compliance
[Contact sales](/enterprise) for self-hosted options.
Require authentication to view shared sessions:
* Restrict to employees with company email
* Integrate with Okta, Azure AD, Google Workspace
* Revoke access when employees leave
[Contact sales](/enterprise) for SSO integration.
Track who views shared sessions:
* View counts and timestamps
* Viewer IP addresses and locations
* Export access logs for audits
Available in enterprise plans.
## Troubleshooting
**Possible causes:**
1. Sharing is disabled in config:
```bash theme={null}
# Check config
cat ~/.config/opencode/opencode.json | grep share
# Should not be "disabled"
```
2. Network connectivity issues:
```bash theme={null}
# Test connectivity
curl https://opncd.ai/health
```
3. Session has no content yet:
* Only sessions with messages can be shared
**Reasons:**
* Session was unshared
* Share ID is incorrect (typo in URL)
* Server maintenance (rare, check status page)
**Fix:** Re-share the session to generate a new URL.
**Workarounds:**
1. Try via API:
```bash theme={null}
curl -X DELETE http://localhost:4096/session//share
```
2. Contact support if data must be removed urgently:
[support@opencode.ai](mailto:support@opencode.ai)
**Immediate steps:**
1. Unshare the session immediately:
```bash theme={null}
/unshare
```
2. Delete the session locally:
```bash theme={null}
# In TUI: Open sessions (Ctrl+P → Sessions) → Delete
```
3. If secrets were exposed:
* Rotate API keys immediately
* Revoke OAuth tokens
* Change passwords
4. Contact support for expedited deletion:
[security@opencode.ai](mailto:security@opencode.ai)
## Best Practices
Skim the session for sensitive data:
* API keys or tokens
* Internal URLs or hostnames
* Customer data or PII
* Proprietary algorithms
```bash theme={null}
# Good titles
"Fix authentication bug in /api/login"
"Refactor database migrations"
# Bad titles
"Debug"
"Session 1"
```
Recipients understand context immediately.
Share at the start of debugging/pairing sessions, not after. Collaborators see your thought process in real-time.
Add a reminder:
```bash theme={null}
# ~/.bashrc or ~/.zshrc
alias opencode='echo "Remember to /unshare when done!" && command opencode'
```
Include share links in pull request descriptions:
```markdown theme={null}
## Changes
- Added user authentication
## Context
See development session: https://opncd.ai/s/s_abc123
```
## Next Steps
Use the `/session/:id/share` API programmatically.
Configure sharing modes in `opencode.json`.
Self-hosted sharing and SSO integration.
Review how shared data is handled.
# Agent Skills
Source: https://anomalyco-opencode.mintlify.app/skills
Define reusable behavior via SKILL.md definitions
Agent skills let OpenCode discover reusable instructions from your repo or home directory.
Skills are loaded on-demand via the native `skill` tool—agents see available skills and can load the full content when needed.
***
## Place files
Create one folder per skill name and put a `SKILL.md` inside it.
OpenCode searches these locations:
* Project config: `.opencode/skills//SKILL.md`
* Global config: `~/.config/opencode/skills//SKILL.md`
* Project Claude-compatible: `.claude/skills//SKILL.md`
* Global Claude-compatible: `~/.claude/skills//SKILL.md`
* Project agent-compatible: `.agents/skills//SKILL.md`
* Global agent-compatible: `~/.agents/skills//SKILL.md`
Create a directory for your skill:
```bash theme={null}
mkdir -p .opencode/skills/git-release
```
Create a `SKILL.md` file with frontmatter and content:
```markdown title=".opencode/skills/git-release/SKILL.md" theme={null}
---
name: git-release
description: Create consistent releases and changelogs
license: MIT
compatibility: opencode
metadata:
audience: maintainers
workflow: github
---
## What I do
- Draft release notes from merged PRs
- Propose a version bump
- Provide a copy-pasteable `gh release create` command
## When to use me
Use this when you are preparing a tagged release.
Ask clarifying questions if the target versioning scheme is unclear.
```
The agent can now load this skill:
```
Load the git-release skill and help me create a release.
```
***
## Understand discovery
For project-local paths, OpenCode walks up from your current working directory until it reaches the git worktree.
It loads any matching `skills/*/SKILL.md` in `.opencode/` and any matching `.claude/skills/*/SKILL.md` or `.agents/skills/*/SKILL.md` along the way.
Global definitions are also loaded from `~/.config/opencode/skills/*/SKILL.md`, `~/.claude/skills/*/SKILL.md`, and `~/.agents/skills/*/SKILL.md`.
### Discovery order
Skills are discovered in this order:
1. **Global external skills** (`.claude/skills/`, `.agents/skills/` in home directory)
2. **Project external skills** (walking up from current directory to worktree)
3. **OpenCode skills** (`.opencode/skills/` directories)
4. **Additional paths** (from `skills.paths` config)
5. **Remote skills** (from `skills.urls` config)
Later sources can override earlier ones if they have the same skill name.
### Discovery implementation
The skill discovery system:
```ts theme={null}
// From skill/skill.ts
export const state = Instance.state(async () => {
const skills: Record = {}
const dirs = new Set()
// Scan external skill directories (.claude/skills/, .agents/skills/)
if (!Flag.OPENCODE_DISABLE_EXTERNAL_SKILLS) {
// Global skills first
for (const dir of EXTERNAL_DIRS) {
const root = path.join(Global.Path.home, dir)
await scanExternal(root, "global")
}
// Project skills (walking up directory tree)
for await (const root of Filesystem.up({
targets: EXTERNAL_DIRS,
start: Instance.directory,
stop: Instance.worktree,
})) {
await scanExternal(root, "project")
}
}
// Scan .opencode/skill/ directories
for (const dir of await Config.directories()) {
const matches = await Glob.scan(OPENCODE_SKILL_PATTERN, { cwd: dir })
for (const match of matches) await addSkill(match)
}
// Scan additional paths from config
for (const skillPath of config.skills?.paths ?? []) {
const resolved = resolveSkillPath(skillPath)
const matches = await Glob.scan(SKILL_PATTERN, { cwd: resolved })
for (const match of matches) await addSkill(match)
}
// Download and load skills from URLs
for (const url of config.skills?.urls ?? []) {
const list = await Discovery.pull(url)
for (const dir of list) {
const matches = await Glob.scan(SKILL_PATTERN, { cwd: dir })
for (const match of matches) await addSkill(match)
}
}
return { skills, dirs }
})
```
***
## Write frontmatter
Each `SKILL.md` must start with YAML frontmatter.
Only these fields are recognized:
* `name` (required)
* `description` (required)
* `license` (optional)
* `compatibility` (optional)
* `metadata` (optional, string-to-string map)
Unknown frontmatter fields are ignored.
```yaml theme={null}
---
name: git-release
description: Create consistent releases and changelogs
license: MIT
compatibility: opencode
metadata:
audience: maintainers
workflow: github
version: "1.0"
---
```
***
## Validate names
`name` must:
* Be 1–64 characters
* Be lowercase alphanumeric with single hyphen separators
* Not start or end with `-`
* Not contain consecutive `--`
* Match the directory name that contains `SKILL.md`
Equivalent regex:
```text theme={null}
^[a-z0-9]+(-[a-z0-9]+)*$
```
Valid names:
* `git-release`
* `code-review`
* `test-generator`
* `api-docs`
Invalid names:
* `Git-Release` (uppercase)
* `-git-release` (starts with hyphen)
* `git--release` (consecutive hyphens)
* `git_release` (underscore)
***
## Follow length rules
`description` must be 1-1024 characters.
Keep it specific enough for the agent to choose correctly.
```yaml theme={null}
# Good
description: Create consistent releases and changelogs from merged PRs
# Bad (too vague)
description: Help with releases
# Bad (too long - over 1024 chars)
description: This skill helps you create releases by analyzing...
```
***
## Use an example
Create `.opencode/skills/git-release/SKILL.md` like this:
```markdown theme={null}
---
name: git-release
description: Create consistent releases and changelogs
license: MIT
compatibility: opencode
metadata:
audience: maintainers
workflow: github
---
## What I do
- Draft release notes from merged PRs
- Propose a version bump
- Provide a copy-pasteable `gh release create` command
## When to use me
Use this when you are preparing a tagged release.
Ask clarifying questions if the target versioning scheme is unclear.
```
***
## Recognize tool description
OpenCode lists available skills in the `skill` tool description.
Each entry includes the skill name and description:
```xml theme={null}
git-release
Create consistent releases and changelogs
```
The agent loads a skill by calling the tool:
```
skill({ name: "git-release" })
```
***
## Configure permissions
Control which skills agents can access using pattern-based permissions in `opencode.json`:
```json theme={null}
{
"permission": {
"skill": {
"*": "allow",
"pr-review": "allow",
"internal-*": "deny",
"experimental-*": "ask"
}
}
}
```
| Permission | Behavior |
| ---------- | ----------------------------------------- |
| `allow` | Skill loads immediately |
| `deny` | Skill hidden from agent, access rejected |
| `ask` | User prompted for approval before loading |
Patterns support wildcards: `internal-*` matches `internal-docs`, `internal-tools`, etc.
***
## Override per agent
Give specific agents different permissions than the global defaults.
**For custom agents** (in agent frontmatter):
```yaml theme={null}
---
permission:
skill:
"documents-*": "allow"
---
```
**For built-in agents** (in `opencode.json`):
```json theme={null}
{
"agent": {
"plan": {
"permission": {
"skill": {
"internal-*": "allow"
}
}
}
}
}
```
***
## Disable the skill tool
Completely disable skills for agents that shouldn't use them:
**For custom agents**:
```yaml theme={null}
---
tools:
skill: false
---
```
**For built-in agents**:
```json theme={null}
{
"agent": {
"plan": {
"tools": {
"skill": false
}
}
}
}
```
When disabled, the `` section is omitted entirely.
***
## Advanced configuration
### Additional skill paths
Add custom directories to scan for skills:
```json title="opencode.json" theme={null}
{
"skills": {
"paths": [
"~/shared-skills",
"./team-skills",
"/absolute/path/to/skills"
]
}
}
```
Paths can be:
* Absolute: `/path/to/skills`
* Relative to project: `./team-skills`
* Home relative: `~/shared-skills`
### Remote skill repositories
Load skills from remote URLs:
```json title="opencode.json" theme={null}
{
"skills": {
"urls": [
"https://example.com/skills"
]
}
}
```
The remote URL must serve an `index.json` file:
```json title="https://example.com/skills/index.json" theme={null}
{
"skills": [
{
"name": "git-release",
"description": "Create consistent releases",
"files": ["SKILL.md", "template.md"]
},
{
"name": "code-review",
"description": "Review code for issues",
"files": ["SKILL.md", "checklist.md"]
}
]
}
```
Skills are downloaded to `~/.cache/opencode/skills/` and loaded automatically.
### Disable external skills
Disable Claude Code/Agent compatible skill directories:
```bash theme={null}
export OPENCODE_DISABLE_EXTERNAL_SKILLS=1
```
This disables scanning of `.claude/skills/` and `.agents/skills/` directories.
***
## Skill content structure
The content of your `SKILL.md` (after the frontmatter) contains instructions for the agent.
### Best practices
**Be specific about what the skill does:**
```markdown theme={null}
## What I do
- Analyze git history between tags
- Extract merged PRs and group by type (features, fixes, breaking)
- Generate semantic version bump recommendation
- Format output as markdown changelog
- Provide `gh release create` command
```
**Explain when to use the skill:**
```markdown theme={null}
## When to use me
Use this skill when:
- You're ready to create a new release
- You need to generate changelog entries
- You want version bump recommendations
Don't use this skill for:
- Pre-release testing
- Backporting fixes
```
**Provide context and constraints:**
```markdown theme={null}
## Assumptions
- Project follows semantic versioning
- PRs are labeled with `feature`, `fix`, `breaking-change`
- Release branch is `main`
- GitHub CLI (`gh`) is available
```
**Include examples:**
````markdown theme={null}
## Example output
```markdown
# Release v1.2.0
## Features
- Add support for remote skills (#123)
- Improve skill discovery performance (#124)
## Fixes
- Fix skill name validation (#125)
## Breaking Changes
- Remove deprecated skill API (#126)
````
Create release:
```bash theme={null}
gh release create v1.2.0 --title "Release v1.2.0" --notes-file CHANGELOG.md
```
````
**Structure complex skills:**
```markdown
## Process
### Step 1: Analyze commits
- Run `git log` to get commits since last tag
- Parse commit messages for conventional commit format
- Extract issue/PR references
### Step 2: Categorize changes
- Group by type: features, fixes, breaking changes
- Sort by significance
- Filter out internal/chore commits
### Step 3: Generate output
- Format as markdown with proper headers
- Include PR links
- Add version bump recommendation
- Generate release command
````
***
## Examples
### Code review skill
```markdown title=".opencode/skills/code-review/SKILL.md" theme={null}
---
name: code-review
description: Comprehensive code review with security and performance checks
license: MIT
---
## What I do
- Review code changes for bugs, security issues, and performance problems
- Check code style and best practices
- Identify potential edge cases
- Suggest specific improvements with code examples
## Review checklist
### Security
- SQL injection vulnerabilities
- XSS vulnerabilities
- Authentication/authorization issues
- Sensitive data exposure
- Input validation
### Performance
- Inefficient algorithms (O(n²) or worse)
- Unnecessary database queries
- Missing indexes
- Memory leaks
- Blocking operations
### Code Quality
- Error handling
- Edge cases
- Code duplication
- Naming clarity
- Test coverage
## Output format
For each issue found, provide:
1. Severity (Critical/High/Medium/Low)
2. Location (file:line)
3. Description of the issue
4. Specific code fix
5. Explanation of why the fix works
```
### Test generator skill
````markdown title=".opencode/skills/test-generator/SKILL.md" theme={null}
---
name: test-generator
description: Generate comprehensive unit tests with edge cases
---
## What I do
- Analyze function/class implementation
- Generate unit tests covering all code paths
- Include edge cases and error scenarios
- Follow project's testing patterns
- Aim for >90% code coverage
## Test generation strategy
### 1. Analyze the code
- Identify all public functions/methods
- Map input parameters and their types
- Identify return values and side effects
- Note error conditions and exceptions
### 2. Generate test cases
- Happy path tests
- Edge cases (empty input, null, undefined, etc.)
- Boundary conditions
- Error scenarios
- Async/Promise handling if applicable
### 3. Follow conventions
- Use existing test framework (Jest, Vitest, etc.)
- Match naming patterns from existing tests
- Use same mocking/stubbing approach
- Follow describe/it structure
## Example output structure
```typescript
describe('FunctionName', () => {
describe('happy path', () => {
it('should handle valid input', () => { })
})
describe('edge cases', () => {
it('should handle empty input', () => { })
it('should handle null input', () => { })
})
describe('error scenarios', () => {
it('should throw on invalid input', () => { })
})
})
````
````
### API documentation skill
```markdown title=".opencode/skills/api-docs/SKILL.md"
---
name: api-docs
description: Generate OpenAPI/Swagger documentation from code
metadata:
framework: express
format: openapi-3.0
---
## What I do
- Extract API routes from Express/Fastify/etc. code
- Generate OpenAPI 3.0 specification
- Document request/response schemas
- Include authentication requirements
- Add code examples
## Documentation structure
For each endpoint, document:
### Basic info
- HTTP method and path
- Summary and description
- Tags for grouping
### Request
- Path parameters
- Query parameters
- Request body schema
- Headers (especially auth)
### Response
- Success status codes and schemas
- Error status codes and schemas
- Response headers
### Security
- Authentication method
- Required scopes/permissions
### Examples
- Request example (curl)
- Response example (JSON)
## Output format
Generate valid OpenAPI 3.0 YAML that can be imported into:
- Swagger UI
- Postman
- API documentation generators
````
### Database migration skill
````markdown title=".opencode/skills/db-migration/SKILL.md" theme={null}
---
name: db-migration
description: Generate database migration scripts with rollback support
metadata:
orm: drizzle
database: postgresql
---
## What I do
- Generate migration scripts for schema changes
- Include both up and down migrations
- Handle data transformations safely
- Add validation and safety checks
- Follow project's migration conventions
## Migration strategy
### For schema changes
1. Analyze existing schema
2. Determine required changes
3. Generate migration with:
- DDL statements (CREATE, ALTER, DROP)
- Proper column types and constraints
- Index creation/updates
- Foreign key handling
### For data migrations
1. Add safety checks (row counts, validation)
2. Use transactions
3. Include rollback logic
4. Add logging for tracking
## Safety rules
- Never DROP columns/tables without explicit confirmation
- Always include rollback migration
- Use transactions where possible
- Add validation checks before/after
- Log all operations
- Test migrations on copy of production data
## Example output
```typescript
import { sql } from 'drizzle-orm'
export async function up(db) {
// Add new column
await db.execute(sql`
ALTER TABLE users
ADD COLUMN email_verified BOOLEAN DEFAULT FALSE
`)
// Migrate existing data
await db.execute(sql`
UPDATE users
SET email_verified = TRUE
WHERE email IS NOT NULL
`)
}
export async function down(db) {
// Rollback
await db.execute(sql`
ALTER TABLE users
DROP COLUMN email_verified
`)
}
````
````
---
## Skills vs Commands vs Tools
Understanding when to use each:
### Skills
**Use for**: Complex, reusable workflows with detailed instructions
**Characteristics**:
- Loaded on-demand by the agent
- Can contain extensive documentation
- Multiple pages of instructions
- Can include bundled resources
- Shareable across projects
**Example**: Code review methodology with checklists
### Commands
**Use for**: Quick, templated prompts with argument substitution
**Characteristics**:
- Invoked with `/command-name`
- Simple template with placeholders
- Can execute shell commands
- Can reference files
- Lightweight and fast
**Example**: `/test` to run test suite
### Tools
**Use for**: Executable functions the LLM can call
**Characteristics**:
- Execute code when called
- Return results to the LLM
- Can perform actions
- Strongly typed arguments
- Can be written in any language
**Example**: Database query tool, API client
### Decision matrix
| Need | Use |
|------|-----|
| Multi-step process with detailed guidance | **Skill** |
| Quick prompt with argument substitution | **Command** |
| Execute code and return results | **Tool** |
| Share complex workflow across projects | **Skill** |
| Simple one-liner with file/shell refs | **Command** |
| Interact with external systems | **Tool** |
| Extensive documentation and examples | **Skill** |
---
## Troubleshoot loading
If a skill does not show up:
1. **Verify `SKILL.md` is spelled in all caps**
```bash
# Correct
.opencode/skills/my-skill/SKILL.md
# Wrong
.opencode/skills/my-skill/skill.md
.opencode/skills/my-skill/Skill.md
````
2. **Check that frontmatter includes `name` and `description`**
```yaml theme={null}
---
name: my-skill # Required
description: ... # Required
---
```
3. **Ensure skill names are unique across all locations**
* Check for duplicate names in global and project skills
* Later sources override earlier ones
4. **Check permissions—skills with `deny` are hidden from agents**
```json theme={null}
{
"permission": {
"skill": {
"my-skill": "allow" // Make sure not "deny"
}
}
}
```
5. **Verify directory name matches skill name**
```bash theme={null}
# Correct
.opencode/skills/my-skill/SKILL.md # name: my-skill
# Wrong
.opencode/skills/MySkill/SKILL.md # name: my-skill
```
6. **Check skill name format**
```yaml theme={null}
# Valid
name: my-skill
name: code-review
name: api-docs
# Invalid
name: My-Skill # No uppercase
name: my_skill # No underscores
name: -my-skill # Can't start with hyphen
name: my--skill # No consecutive hyphens
```
# Themes
Source: https://anomalyco-opencode.mintlify.app/themes
Select a built-in theme or define your own
With OpenCode you can select from one of several built-in themes, use a theme that adapts to your terminal theme, or define your own custom theme.
By default, OpenCode uses our own `opencode` theme.
## Terminal Requirements
For themes to display correctly with their full color palette, your terminal must support **truecolor** (24-bit color). Most modern terminals support this by default, but you may need to enable it:
* **Check support**: Run `echo $COLORTERM` - it should output `truecolor` or `24bit`
* **Enable truecolor**: Set the environment variable `COLORTERM=truecolor` in your shell profile
* **Terminal compatibility**: Ensure your terminal emulator supports 24-bit color (most modern terminals like iTerm2, Alacritty, Kitty, Windows Terminal, and recent versions of GNOME Terminal do)
Without truecolor support, themes may appear with reduced color accuracy or fall back to the nearest 256-color approximation.
## Built-in Themes
OpenCode comes with several built-in themes.
| Name | Description |
| ---------------------- | ---------------------------------------------------------------------------- |
| `system` | Adapts to your terminal's background color |
| `tokyonight` | Based on the [Tokyonight](https://github.com/folke/tokyonight.nvim) theme |
| `everforest` | Based on the [Everforest](https://github.com/sainnhe/everforest) theme |
| `ayu` | Based on the [Ayu](https://github.com/ayu-theme) dark theme |
| `catppuccin` | Based on the [Catppuccin](https://github.com/catppuccin) theme |
| `catppuccin-macchiato` | Based on the [Catppuccin](https://github.com/catppuccin) theme |
| `gruvbox` | Based on the [Gruvbox](https://github.com/morhetz/gruvbox) theme |
| `kanagawa` | Based on the [Kanagawa](https://github.com/rebelot/kanagawa.nvim) theme |
| `nord` | Based on the [Nord](https://github.com/nordtheme/nord) theme |
| `matrix` | Hacker-style green on black theme |
| `one-dark` | Based on the [Atom One](https://github.com/Th3Whit3Wolf/one-nvim) Dark theme |
And more, we are constantly adding new themes.
## System Theme
The `system` theme is designed to automatically adapt to your terminal's color scheme. Unlike traditional themes that use fixed colors, the system theme:
* **Generates gray scale**: Creates a custom gray scale based on your terminal's background color, ensuring optimal contrast.
* **Uses ANSI colors**: Leverages standard ANSI colors (0-15) for syntax highlighting and UI elements, which respect your terminal's color palette.
* **Preserves terminal defaults**: Uses `none` for text and background colors to maintain your terminal's native appearance.
The system theme is for users who:
* Want OpenCode to match their terminal's appearance
* Use custom terminal color schemes
* Prefer a consistent look across all terminal applications
## Using a Theme
You can select a theme by bringing up the theme select with the `/theme` command. Or you can specify it in your config.
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"theme": "tokyonight"
}
```
## Custom Themes
OpenCode supports a flexible JSON-based theme system that allows users to create and customize themes easily.
### Hierarchy
Themes are loaded from multiple directories in the following order where later directories override earlier ones:
1. **Built-in themes** - These are embedded in the binary
2. **User config directory** - Defined in `~/.config/opencode/themes/*.json` or `$XDG_CONFIG_HOME/opencode/themes/*.json`
3. **Project root directory** - Defined in the `/.opencode/themes/*.json`
4. **Current working directory** - Defined in `./.opencode/themes/*.json`
If multiple directories contain a theme with the same name, the theme from the directory with higher priority will be used.
### Creating a Theme
To create a custom theme, create a JSON file in one of the theme directories.
For user-wide themes:
```bash theme={null}
mkdir -p ~/.config/opencode/themes
vim ~/.config/opencode/themes/my-theme.json
```
And for project-specific themes:
```bash theme={null}
mkdir -p .opencode/themes
vim .opencode/themes/my-theme.json
```
### JSON Format
Themes use a flexible JSON format with support for:
* **Hex colors**: `"#ffffff"`
* **ANSI colors**: `3` (0-255)
* **Color references**: `"primary"` or custom definitions
* **Dark/light variants**: `{"dark": "#000", "light": "#fff"}`
* **No color**: `"none"` - Uses the terminal's default color or transparent
### Color Definitions
The `defs` section is optional and it allows you to define reusable colors that can be referenced in the theme.
### Terminal Defaults
The special value `"none"` can be used for any color to inherit the terminal's default color. This is particularly useful for creating themes that blend seamlessly with your terminal's color scheme:
* `"text": "none"` - Uses terminal's default foreground color
* `"background": "none"` - Uses terminal's default background color
### Example Theme
Here's an example of a custom theme:
```json title="my-theme.json" theme={null}
{
"$schema": "https://opencode.ai/theme.json",
"defs": {
"nord0": "#2E3440",
"nord1": "#3B4252",
"nord2": "#434C5E",
"nord3": "#4C566A",
"nord4": "#D8DEE9",
"nord5": "#E5E9F0",
"nord6": "#ECEFF4",
"nord7": "#8FBCBB",
"nord8": "#88C0D0",
"nord9": "#81A1C1",
"nord10": "#5E81AC",
"nord11": "#BF616A",
"nord12": "#D08770",
"nord13": "#EBCB8B",
"nord14": "#A3BE8C",
"nord15": "#B48EAD"
},
"theme": {
"primary": {
"dark": "nord8",
"light": "nord10"
},
"secondary": {
"dark": "nord9",
"light": "nord9"
},
"accent": {
"dark": "nord7",
"light": "nord7"
},
"error": {
"dark": "nord11",
"light": "nord11"
},
"warning": {
"dark": "nord12",
"light": "nord12"
},
"success": {
"dark": "nord14",
"light": "nord14"
},
"info": {
"dark": "nord8",
"light": "nord10"
},
"text": {
"dark": "nord4",
"light": "nord0"
},
"textMuted": {
"dark": "nord3",
"light": "nord1"
},
"background": {
"dark": "nord0",
"light": "nord6"
},
"backgroundPanel": {
"dark": "nord1",
"light": "nord5"
},
"backgroundElement": {
"dark": "nord1",
"light": "nord4"
},
"border": {
"dark": "nord2",
"light": "nord3"
},
"borderActive": {
"dark": "nord3",
"light": "nord2"
},
"borderSubtle": {
"dark": "nord2",
"light": "nord3"
},
"diffAdded": {
"dark": "nord14",
"light": "nord14"
},
"diffRemoved": {
"dark": "nord11",
"light": "nord11"
},
"diffContext": {
"dark": "nord3",
"light": "nord3"
},
"diffHunkHeader": {
"dark": "nord3",
"light": "nord3"
},
"diffHighlightAdded": {
"dark": "nord14",
"light": "nord14"
},
"diffHighlightRemoved": {
"dark": "nord11",
"light": "nord11"
},
"diffAddedBg": {
"dark": "#3B4252",
"light": "#E5E9F0"
},
"diffRemovedBg": {
"dark": "#3B4252",
"light": "#E5E9F0"
},
"diffContextBg": {
"dark": "nord1",
"light": "nord5"
},
"diffLineNumber": {
"dark": "nord2",
"light": "nord4"
},
"diffAddedLineNumberBg": {
"dark": "#3B4252",
"light": "#E5E9F0"
},
"diffRemovedLineNumberBg": {
"dark": "#3B4252",
"light": "#E5E9F0"
},
"markdownText": {
"dark": "nord4",
"light": "nord0"
},
"markdownHeading": {
"dark": "nord8",
"light": "nord10"
},
"markdownLink": {
"dark": "nord9",
"light": "nord9"
},
"markdownLinkText": {
"dark": "nord7",
"light": "nord7"
},
"markdownCode": {
"dark": "nord14",
"light": "nord14"
},
"markdownBlockQuote": {
"dark": "nord3",
"light": "nord3"
},
"markdownEmph": {
"dark": "nord12",
"light": "nord12"
},
"markdownStrong": {
"dark": "nord13",
"light": "nord13"
},
"markdownHorizontalRule": {
"dark": "nord3",
"light": "nord3"
},
"markdownListItem": {
"dark": "nord8",
"light": "nord10"
},
"markdownListEnumeration": {
"dark": "nord7",
"light": "nord7"
},
"markdownImage": {
"dark": "nord9",
"light": "nord9"
},
"markdownImageText": {
"dark": "nord7",
"light": "nord7"
},
"markdownCodeBlock": {
"dark": "nord4",
"light": "nord0"
},
"syntaxComment": {
"dark": "nord3",
"light": "nord3"
},
"syntaxKeyword": {
"dark": "nord9",
"light": "nord9"
},
"syntaxFunction": {
"dark": "nord8",
"light": "nord8"
},
"syntaxVariable": {
"dark": "nord7",
"light": "nord7"
},
"syntaxString": {
"dark": "nord14",
"light": "nord14"
},
"syntaxNumber": {
"dark": "nord15",
"light": "nord15"
},
"syntaxType": {
"dark": "nord7",
"light": "nord7"
},
"syntaxOperator": {
"dark": "nord9",
"light": "nord9"
},
"syntaxPunctuation": {
"dark": "nord4",
"light": "nord0"
}
}
}
```
# Tools
Source: https://anomalyco-opencode.mintlify.app/tools
Available tools and how to configure them.
OpenCode provides a comprehensive set of tools that agents can use to interact with your codebase, execute commands, and access external resources. Tools can be enabled or disabled globally or per-agent to control what actions the AI can perform.
***
## File Operations
Tools for reading, writing, and modifying files in your codebase.
**Purpose**: Read files or directories from the local filesystem.
**Capabilities**:
* Read up to 2000 lines by default from any file
* Support for offset and limit parameters for reading specific sections
* Can read image files and PDFs
* Returns content with line numbers for easy reference
* Directory listing with trailing `/` for subdirectories
**Usage**:
```json theme={null}
{
"tools": {
"read": true
}
}
```
**Common use cases**:
* Reading source code files
* Examining configuration files
* Listing directory contents
* Reading documentation
**Purpose**: Create new files or overwrite existing files.
**Capabilities**:
* Create new files with specified content
* Overwrite existing files (requires reading the file first)
* Atomic write operations
**Usage**:
```json theme={null}
{
"tools": {
"write": true
}
}
```
**Common use cases**:
* Creating new source files
* Generating configuration files
* Writing documentation
* Creating test files
**Purpose**: Perform exact string replacements in files.
**Capabilities**:
* Find and replace exact text matches
* Support for `replaceAll` to rename across entire file
* Requires reading the file first
* Preserves exact indentation and formatting
**Usage**:
```json theme={null}
{
"tools": {
"edit": true
}
}
```
**Common use cases**:
* Modifying existing code
* Fixing bugs
* Refactoring variable names
* Updating function implementations
**Note**: See also `multiedit` for making multiple edits to a single file in one operation.
**Purpose**: Make multiple edits to a single file in one operation.
**Capabilities**:
* Perform multiple find-and-replace operations efficiently
* Edits applied sequentially in order
* Atomic operation (all edits succeed or none are applied)
* Built on top of the Edit tool
**Usage**:
```json theme={null}
{
"tools": {
"multiedit": true
}
}
```
**Common use cases**:
* Refactoring multiple parts of a file
* Updating multiple function calls
* Batch renaming within a file
**Purpose**: Apply structured patches to create, update, or delete files.
**Capabilities**:
* Create new files with `*** Add File:`
* Update existing files with `*** Update File:`
* Delete files with `*** Delete File:`
* Rename files with `*** Move to:`
* Unified diff-style syntax
**Usage**:
```json theme={null}
{
"tools": {
"patch": true
}
}
```
**Example patch format**:
```
*** Begin Patch
*** Add File: hello.txt
+Hello world
*** Update File: src/app.py
@@ def greet():
-print("Hi")
+print("Hello, world!")
*** Delete File: obsolete.txt
*** End Patch
```
***
## Code Search & Discovery
Tools for finding files and searching code.
**Purpose**: Fast file pattern matching using glob patterns.
**Capabilities**:
* Find files by name patterns like `**/*.js` or `src/**/*.ts`
* Works with any codebase size
* Returns matching file paths sorted by modification time
**Usage**:
```json theme={null}
{
"tools": {
"glob": true
}
}
```
**Common use cases**:
* Finding all files of a specific type
* Locating test files
* Finding configuration files
* Discovering components by pattern
**Purpose**: Fast content search using regular expressions.
**Capabilities**:
* Search file contents with full regex syntax
* Filter by file patterns with include parameter
* Returns file paths and line numbers with matches
* Sorted by modification time
**Usage**:
```json theme={null}
{
"tools": {
"grep": true
}
}
```
**Common use cases**:
* Finding function definitions
* Searching for error messages
* Locating TODO comments
* Finding API usage patterns
**Purpose**: List files and directories in a given path.
**Capabilities**:
* List directory contents
* Support for glob patterns to ignore files
* Uses absolute paths
**Usage**:
```json theme={null}
{
"tools": {
"list": true
}
}
```
**Common use cases**:
* Exploring directory structure
* Checking if directories exist
* Verifying file organization
**Purpose**: Search and get relevant context for programming tasks using Exa Code API.
**Capabilities**:
* Provides high-quality, fresh context for libraries, SDKs, and APIs
* Returns comprehensive code examples and documentation
* Adjustable token count (1000-50000)
* Optimized for finding specific programming patterns
**Usage**:
```json theme={null}
{
"tools": {
"codesearch": true
}
}
```
**Common use cases**:
* Finding library usage examples
* Learning API patterns
* Discovering best practices
* Understanding framework concepts
***
## Code Intelligence
Tools for advanced code analysis and navigation.
**Purpose**: Interact with Language Server Protocol servers for code intelligence.
**Capabilities**:
* **Go to Definition**: Find where symbols are defined
* **Find References**: Find all references to a symbol
* **Hover**: Get documentation and type information
* **Document Symbol**: Get all symbols in a document
* **Workspace Symbol**: Search for symbols across workspace
* **Go to Implementation**: Find interface implementations
* **Call Hierarchy**: Analyze function call relationships
* **Incoming/Outgoing Calls**: Map caller/callee relationships
**Usage**:
```json theme={null}
{
"tools": {
"lsp": true
}
}
```
**Common use cases**:
* Understanding code structure
* Tracing function calls
* Finding symbol usages
* Code navigation and exploration
**Note**: LSP servers must be configured for the file type.
***
## System Operations
Tools for executing commands and system operations.
**Purpose**: Execute shell commands in a persistent bash session.
**Capabilities**:
* Run terminal operations (git, npm, docker, etc.)
* Persistent shell session across commands
* Optional timeout configuration
* Support for workdir parameter to change directories
* Automatic output truncation for large outputs
**Usage**:
```json theme={null}
{
"tools": {
"bash": true
}
}
```
**Common use cases**:
* Running git commands
* Installing dependencies (npm, pip, etc.)
* Running tests and builds
* Executing scripts
* System diagnostics
**Best practices**:
* Use `workdir` parameter instead of `cd && command`
* Quote paths with spaces
* Chain dependent commands with `&&`
* Use specialized tools for file operations instead of cat/grep/sed
***
## Task Management
Tools for managing and tracking work.
**Purpose**: Create and manage structured task lists for coding sessions.
**Capabilities**:
* Create, update, and manage todo items
* Track task states (pending, in\_progress, completed, cancelled)
* Organize complex multi-step tasks
* Provide progress visibility to users
**Usage**:
```json theme={null}
{
"tools": {
"todowrite": true
}
}
```
**When to use**:
* Complex multi-step tasks (3+ steps)
* Non-trivial and complex tasks
* User provides multiple tasks
* Planning and tracking implementation
**Task states**:
* `pending`: Task not yet started
* `in_progress`: Currently working on (limit to ONE at a time)
* `completed`: Task finished successfully
* `cancelled`: Task no longer needed
**Purpose**: Read the current todo list for the session.
**Capabilities**:
* View all todo items with their status
* Check progress on tasks
* Understand what work remains
**Usage**:
```json theme={null}
{
"tools": {
"todoread": true
}
}
```
**When to use**:
* Beginning of conversations to see what's pending
* Before starting new tasks to prioritize
* When uncertain about what to do next
* After completing tasks to see remaining work
***
## Agent Orchestration
Tools for invoking specialized agents.
**Purpose**: Launch specialized subagents to handle complex, multi-step tasks autonomously.
**Capabilities**:
* Invoke General and Explore subagents
* Launch multiple agents concurrently
* Resume existing subagent sessions with task\_id
* Execute custom slash commands
**Usage**:
```json theme={null}
{
"tools": {
"task": true
}
}
```
**Available subagents**:
* **General**: Full tool access for complex tasks and research
* **Explore**: Read-only agent for fast codebase exploration
**Common use cases**:
* Running multiple units of work in parallel
* Delegating complex research tasks
* Fast code exploration without modification risk
* Executing custom slash commands
**Best practices**:
* Launch multiple agents in parallel when possible
* Provide detailed task descriptions
* Specify what information should be returned
* Tell agents whether to write code or just research
***
## Web Access
Tools for accessing external web content.
**Purpose**: Fetch content from specified URLs.
**Capabilities**:
* Fetch web content in markdown, text, or HTML format
* Automatic HTTPS upgrade for HTTP URLs
* Read-only operations
* Content summarization for large pages
**Usage**:
```json theme={null}
{
"tools": {
"webfetch": true
}
}
```
**Common use cases**:
* Reading documentation from websites
* Fetching API documentation
* Accessing online resources
* Reading blog posts or articles
**Purpose**: Search the web using Exa AI with real-time web searches.
**Capabilities**:
* Real-time web searches for current information
* Configurable result counts
* Live crawling modes (fallback or preferred)
* Search types: auto, fast, or deep
* Domain filtering and advanced search options
**Usage**:
```json theme={null}
{
"tools": {
"websearch": true
}
}
```
**Common use cases**:
* Finding current events and recent information
* Accessing information beyond knowledge cutoff
* Researching latest library versions
* Looking up recent API changes
***
## User Interaction
Tools for gathering user input during execution.
**Purpose**: Ask users questions during execution to gather preferences or clarify requirements.
**Capabilities**:
* Present multiple choice questions
* Support for single or multiple selection
* Optional "Type your own answer" option (enabled by default)
* Recommend specific options
**Usage**:
```json theme={null}
{
"tools": {
"question": true
}
}
```
**Common use cases**:
* Gathering user preferences
* Clarifying ambiguous instructions
* Getting decisions on implementation choices
* Offering choices for direction
***
## Performance Optimization
Tools for efficient batch operations.
**Purpose**: Execute multiple independent tool calls concurrently to reduce latency.
**Capabilities**:
* Run 1-25 tool calls in parallel
* All calls start simultaneously
* Partial failures don't stop other tool calls
* Significant efficiency gains (2-5x improvement)
**Usage**:
```json theme={null}
{
"tools": {
"batch": true
}
}
```
**Good use cases**:
* Reading many files
* Grep + glob + read combinations
* Multiple bash commands
* Multi-part edits on same or different files
**When NOT to use**:
* Operations depending on prior tool output
* Ordered stateful mutations where sequence matters
**Example payload**:
```json theme={null}
[
{"tool": "read", "parameters": {"filePath": "src/index.ts"}},
{"tool": "grep", "parameters": {"pattern": "Session", "include": "*.ts"}},
{"tool": "bash", "parameters": {"command": "git status", "description": "Check git status"}}
]
```
***
## Configuration
### Global Tool Configuration
Enable or disable tools globally in your `opencode.json`:
```json title="opencode.json" theme={null}
{
"$schema": "https://opencode.ai/config.json",
"tools": {
"read": true,
"write": true,
"edit": true,
"bash": true,
"grep": true,
"glob": true,
"webfetch": true,
"todowrite": true,
"todoread": true
}
}
```
### Per-Agent Tool Configuration
Override tool access for specific agents:
```json title="opencode.json" theme={null}
{
"agent": {
"plan": {
"tools": {
"write": false,
"edit": false,
"bash": false
}
},
"readonly": {
"tools": {
"write": false,
"edit": false,
"patch": false,
"multiedit": false,
"bash": false
}
}
}
}
```
### Wildcard Tool Control
Use wildcards to control multiple tools at once:
```json title="opencode.json" theme={null}
{
"agent": {
"custom": {
"tools": {
"mymcp_*": false,
"*edit*": false
}
}
}
}
```
Agent-specific tool configurations override the global configuration.
***
## Best Practices
**Use specialized tools over bash**: Prefer `read`, `grep`, and `glob` over `cat`, `grep`, and `find` commands in bash for better integration and efficiency.
**Batch independent operations**: Use the `batch` tool to run multiple independent operations in parallel for significant performance improvements.
**Read before write**: Always use the `read` tool before using `write` or `edit` on existing files to understand the current state.
**Use Task tool for exploration**: When doing open-ended code searches or exploration, use the `task` tool with the Explore subagent instead of running searches directly.
***
## Complete Tool List
Here's a complete list of all available tools:
| Tool | Category | Purpose |
| ------------ | ------------------- | -------------------------- |
| `read` | File Operations | Read files and directories |
| `write` | File Operations | Create or overwrite files |
| `edit` | File Operations | Modify existing files |
| `multiedit` | File Operations | Multiple edits in one file |
| `patch` | File Operations | Apply structured patches |
| `glob` | Code Search | Find files by pattern |
| `grep` | Code Search | Search file contents |
| `list` | Code Search | List directory contents |
| `codesearch` | Code Search | AI-powered code search |
| `lsp` | Code Intelligence | Language server operations |
| `bash` | System Operations | Execute shell commands |
| `todowrite` | Task Management | Create and manage todos |
| `todoread` | Task Management | Read todo lists |
| `task` | Agent Orchestration | Launch subagents |
| `webfetch` | Web Access | Fetch web content |
| `websearch` | Web Access | Search the web |
| `question` | User Interaction | Ask user questions |
| `batch` | Performance | Execute tools in parallel |
# Troubleshooting
Source: https://anomalyco-opencode.mintlify.app/troubleshooting
Diagnose and resolve common OpenCode issues with detailed debugging guides
This guide covers common issues, debugging techniques, and solutions for OpenCode problems.
## Diagnostic Steps
Before diving into specific issues, gather diagnostic information:
Log files are your first stop for debugging:
```bash theme={null}
ls -ltr ~/.local/share/opencode/log/
tail -f ~/.local/share/opencode/log/$(ls -t ~/.local/share/opencode/log/ | head -1)
```
```powershell theme={null}
# Press WIN+R, paste:
%USERPROFILE%\.local\share\opencode\log
# View latest log
Get-Content (Get-ChildItem $env:USERPROFILE\.local\share\opencode\log | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName -Tail 50
```
```bash theme={null}
opencode --log-level DEBUG
```
Or set persistently:
```json opencode.json theme={null}
{
"logLevel": "debug"
}
```
```bash theme={null}
opencode --version
```
Ensure you're on the latest version:
```bash theme={null}
opencode upgrade
```
```bash theme={null}
# Check for corruption
sqlite3 ~/.local/share/opencode/project/*/storage/*.db "PRAGMA integrity_check;"
```
## Common Issues
### OpenCode Won't Start
**Error:**
```
bash: opencode: command not found
```
**Cause:** Binary not in `PATH` or installation incomplete.
**Solutions:**
```bash theme={null}
which opencode
# Should print: /usr/local/bin/opencode or ~/.local/bin/opencode
```
```bash theme={null}
curl -fsSL https://opencode.ai/install | bash
```
```bash theme={null}
# For bash
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# For zsh
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
```
Close and reopen your terminal window.
**Error:** OpenCode starts then exits within seconds.
**Debugging:**
```bash theme={null}
# Run with logs to stdout
opencode --print-logs
# Check for specific error messages
tail -100 ~/.local/share/opencode/log/*.log | grep -i error
```
**Common causes:**
```bash theme={null}
# Find process using port 4096
lsof -i :4096
# Kill conflicting process
kill -9
# Or use different port
opencode --port 4097
```
```bash theme={null}
# Backup and reset config
mv ~/.config/opencode/opencode.json ~/.config/opencode/opencode.json.bak
opencode
```
```bash theme={null}
# Fix permissions
chmod -R u+rwX ~/.local/share/opencode
chmod -R u+rwX ~/.config/opencode
```
**Symptoms:** TUI starts but shows nothing or freezes.
**Quick fixes:**
1. **Force redraw:**
* Press `Ctrl+L` to redraw screen
2. **Check terminal compatibility:**
```bash theme={null}
echo $TERM
# Should be: xterm-256color, screen-256color, or similar
```
If not, set it:
```bash theme={null}
export TERM=xterm-256color
opencode
```
3. **Try different terminal:**
* macOS: iTerm2, Alacritty, or native Terminal.app
* Linux: gnome-terminal, konsole, alacritty
* Windows: Windows Terminal, not Command Prompt
### Authentication Issues
**Error:**
```
ProviderAuthError: Failed to authenticate with openai
```
**Solutions:**
```bash theme={null}
# Check key is set
cat ~/.local/share/opencode/auth.json | jq '.openai'
# Test key directly
curl https://api.openai.com/v1/models \
-H "Authorization: Bearer YOUR_KEY"
```
```bash theme={null}
# In OpenCode TUI
/connect
# Or via API
curl -X PUT http://localhost:4096/auth/openai \
-H "Content-Type: application/json" \
-d '{"apiKey": "sk-..."}'
```
```bash theme={null}
# Test provider API access
curl -I https://api.openai.com
curl -I https://api.anthropic.com
```
If these fail, check proxy settings (see [Network Configuration](/network)).
**Cause:** Key format issue or whitespace.
**Fix:**
```bash theme={null}
# Remove auth.json and re-add
rm ~/.local/share/opencode/auth.json
opencode
# Use /connect command
```
**Ensure no leading/trailing whitespace:**
```bash theme={null}
# Wrong:
"apiKey": " sk-abc123 "
# Correct:
"apiKey": "sk-abc123"
```
### Model Errors
**Error:**
```
ProviderModelNotFoundError: Model "gpt-4" not found
```
**Cause:** Incorrect model reference format.
**Solution:**
Models must be referenced as `/`:
```json Correct theme={null}
{
"model": "openai/gpt-4.1"
}
```
```json Wrong theme={null}
{
"model": "gpt-4" // Missing provider
}
```
**List available models:**
```bash theme={null}
opencode models
# Or via API
curl http://localhost:4096/provider
```
**Common model IDs:**
* `openai/gpt-4.1`
* `openai/gpt-4.1-mini`
* `anthropic/claude-4.5-sonnet`
* `openrouter/google/gemini-2.5-flash`
**Error:**
```
Model "openai/gpt-4" requires higher tier access
```
**Causes:**
1. Model requires paid subscription
2. Account doesn't have access
3. Model deprecated/renamed
**Check access:**
```bash theme={null}
# List models you have access to
curl https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY" | jq '.data[].id'
```
**Fallback to available model:**
```json opencode.json theme={null}
{
"model": "openai/gpt-4.1-mini" // Free tier alternative
}
```
### Provider Package Issues
**Symptoms:**
* API errors mentioning unknown parameters
* "Unexpected field" errors
* Sudden failures after provider API updates
**Cause:** Cached provider packages (OpenAI SDK, Anthropic SDK, etc.) are outdated.
**Solution:**
```bash theme={null}
rm -rf ~/.cache/opencode
```
```powershell theme={null}
Remove-Item -Recurse -Force $env:USERPROFILE\.cache\opencode
```
```bash theme={null}
opencode
```
OpenCode will automatically download the latest provider packages.
Check logs for successful package installation:
```bash theme={null}
tail -f ~/.local/share/opencode/log/*.log | grep -i "installed"
```
Provider packages are cached in `~/.cache/opencode` to speed up startup. Clear this if you encounter API compatibility issues.
### Configuration Issues
**Error:**
```
ProviderInitError: Failed to initialize provider "openai"
```
**Cause:** Corrupted or invalid configuration.
**Fix:**
```bash theme={null}
# Check for JSON errors
cat ~/.config/opencode/opencode.json | jq .
# Should output formatted JSON
```
```bash theme={null}
# Backup
cp ~/.config/opencode/opencode.json{,.bak}
# Reset to minimal config
cat > ~/.config/opencode/opencode.json <
```bash theme={null}
rm -rf ~/.local/share/opencode
```
This deletes all sessions and history. Backup first if needed.
```bash theme={null}
opencode
# Run /connect command
```
**Cause:** Config cached or syntax error preventing reload.
**Solutions:**
1. **Restart OpenCode:**
```bash theme={null}
# Kill existing instance
pkill -f opencode
# Start fresh
opencode
```
2. **Verify config location:**
```bash theme={null}
# Check which config is loaded
opencode --print-logs 2>&1 | grep -i config
```
Config priority:
1. `.opencode/opencode.json` (project-specific)
2. `~/.config/opencode/opencode.json` (user-specific)
3. **Validate syntax:**
```bash theme={null}
# Must be valid JSON (no comments unless .jsonc)
cat ~/.config/opencode/opencode.json | jq .
```
### Desktop App Issues
**Quick checks:**
1. **Fully quit and relaunch:**
* macOS: Cmd+Q, then reopen
* Windows: Right-click tray icon → Exit, then reopen
2. **Check for error dialog:**
* Click "Restart" button if shown
* Copy error details for debugging
3. **macOS only - Reload webview:**
* Menu: OpenCode → Reload Webview
* Helps if UI is blank/frozen
**Symptoms:** "Connection Failed" dialog on launch.
**Causes:**
1. Custom server URL is unreachable
2. Port conflict preventing local server start
3. Firewall blocking connection
**Solutions:**
From Home screen:
1. Click server name (with status dot)
2. Click "Clear" in Default server section
3. Restart app
Edit `~/.config/opencode/opencode.json`, remove:
```json theme={null}
{
"server": {
"port": 4096,
"hostname": "..."
}
}
```
```bash theme={null}
# Unset if present
unset OPENCODE_PORT
unset OPENCODE_HOSTNAME
```
**Symptoms:** App crashes on launch after installing plugin.
**Fix: Disable plugins**
Open:
* macOS/Linux: `~/.config/opencode/opencode.jsonc`
* Windows: `%USERPROFILE%\.config\opencode\opencode.jsonc`
Set:
```jsonc theme={null}
{
"plugin": [] // Empty array disables all
}
```
Rename plugin directories:
```bash theme={null}
# Global plugins
mv ~/.config/opencode/plugins ~/.config/opencode/plugins.disabled
# Project plugins
mv .opencode/plugins .opencode/plugins.disabled
```
Once app works, re-enable plugins individually to find the culprit.
**When:** App behaves strangely, plugin install stuck.
```bash theme={null}
# Quit app first
rm -rf ~/Library/Caches/ai.opencode.*
rm -rf ~/.cache/opencode
```
```bash theme={null}
# Quit app first
rm -rf ~/.cache/opencode
```
Press `WIN+R`, paste:
```
%USERPROFILE%\.cache\opencode
```
Delete the folder, then restart app.
**Requirements:**
* Notifications enabled in OS settings for OpenCode
* App window **not focused** (notifications only show when backgrounded)
**Enable notifications:**
System Settings → Notifications → OpenCode → Allow Notifications ✓
Settings → System → Notifications → OpenCode → On
Varies by desktop environment. Usually in Settings → Notifications.
### Linux-Specific Issues
**Cause:** Missing clipboard utilities.
**Solution: Install clipboard tools**
```bash theme={null}
# Debian/Ubuntu
sudo apt install -y xclip
# Or alternative
sudo apt install -y xsel
# RHEL/CentOS
sudo yum install -y xclip
```
```bash theme={null}
# Debian/Ubuntu
sudo apt install -y wl-clipboard
# Fedora
sudo dnf install -y wl-clipboard
```
```bash theme={null}
# Install virtual display
sudo apt install -y xvfb
# Start virtual display
Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
export DISPLAY=:99.0
# Now install xclip
sudo apt install -y xclip
```
**Verify fix:**
```bash theme={null}
echo "test" | xclip -selection clipboard
xclip -selection clipboard -o
# Should output: test
```
**Symptoms:** Blank window, crashes on Linux with Wayland.
**Try Wayland flag:**
```bash theme={null}
OC_ALLOW_WAYLAND=1 opencode
```
**If worse, use X11 session:**
1. Log out
2. At login screen, select "Ubuntu on Xorg" or "GNOME on Xorg"
3. Log in and launch OpenCode
### Windows-Specific Issues
**Error:** App opens to blank window (Windows only).
**Cause:** Microsoft Edge WebView2 Runtime not installed.
**Solution:**
[Download from Microsoft](https://go.microsoft.com/fwlink/p/?LinkId=2124703)
Run the installer and follow prompts.
Should now display correctly.
**Symptoms:** Slow file operations, laggy terminal, high CPU.
**Recommended: Use WSL**
See [Windows WSL Guide](/windows-wsl) for optimal Windows setup.
WSL provides:
* 10-20x faster file I/O
* Better terminal support
* Native Linux tool compatibility
## Advanced Debugging
### Enable Debug Logging
```bash theme={null}
# Maximum verbosity
export DEBUG="*"
export LOG_LEVEL="debug"
opencode --log-level DEBUG --print-logs
```
### Inspect Database
OpenCode uses SQLite for storage:
```bash theme={null}
# List databases
find ~/.local/share/opencode -name "*.db"
# Open database
sqlite3 ~/.local/share/opencode/project//storage/session.db
# Useful queries
SELECT * FROM session ORDER BY updated_at DESC LIMIT 5;
SELECT COUNT(*) FROM message;
PRAGMA table_info(session);
```
### Network Debugging
```bash theme={null}
# Trace HTTP requests
export DEBUG="http*"
opencode
# Test proxy connectivity
curl -x $HTTPS_PROXY https://api.openai.com/v1/models
# Check certificate issues
openssl s_client -connect api.openai.com:443 -showcerts
```
### Profile Performance
```bash theme={null}
# CPU profiling (Bun runtime)
bun --inspect opencode
# Memory usage
top -p $(pgrep -f opencode)
# Disk I/O
iotop -p $(pgrep -f opencode)
```
## Reset Everything (Last Resort)
**This deletes all OpenCode data including sessions, history, and configuration.**
Backup important sessions before proceeding.
```bash theme={null}
# Backup (optional)
tar -czf ~/opencode-backup-$(date +%Y%m%d).tar.gz \
~/.local/share/opencode \
~/.config/opencode \
~/.cache/opencode
# Delete all data
rm -rf ~/.local/share/opencode
rm -rf ~/.config/opencode
rm -rf ~/.cache/opencode
# Reinstall
curl -fsSL https://opencode.ai/install | bash
# Start fresh
opencode
```
```powershell theme={null}
# Backup (optional)
Compress-Archive -Path "$env:USERPROFILE\.local\share\opencode","$env:USERPROFILE\.config\opencode","$env:USERPROFILE\.cache\opencode" -DestinationPath "$env:USERPROFILE\opencode-backup-$(Get-Date -Format 'yyyyMMdd').zip"
# Delete all data
Remove-Item -Recurse -Force "$env:USERPROFILE\.local\share\opencode"
Remove-Item -Recurse -Force "$env:USERPROFILE\.config\opencode"
Remove-Item -Recurse -Force "$env:USERPROFILE\.cache\opencode"
# Reinstall
irm https://opencode.ai/install.ps1 | iex
# Start fresh
opencode
```
## Getting Help
Report bugs and request features. Search existing issues first.
Real-time help from the community and maintainers.
Comprehensive guides and API references.
Priority support for enterprise customers.
### When Reporting Issues
Include this information:
```bash theme={null}
# Copy output of:
opencode --version
uname -a # or ver on Windows
node --version
echo $SHELL
```
```bash theme={null}
# Include relevant log excerpts
tail -100 ~/.local/share/opencode/log/*.log
```
Redact sensitive info (API keys, file paths).
```bash theme={null}
# Share config (redact secrets)
cat ~/.config/opencode/opencode.json
```
1. Step-by-step instructions to reproduce
2. Expected behavior
3. Actual behavior
4. Screenshots/screencasts if applicable
## Preventive Measures
```bash theme={null}
opencode upgrade
```
Run monthly to get latest fixes.
```bash theme={null}
# Periodic backups
tar -czf ~/opencode-backup.tar.gz ~/.local/share/opencode/project
```
Check logs after updates:
```bash theme={null}
tail -f ~/.local/share/opencode/log/*.log
```
Test config in isolated session:
```bash theme={null}
opencode --config /tmp/test-config.json
```
## Next Steps
Advanced server setup and debugging.
Resolve proxy and certificate issues.
Fix Windows-specific performance issues.
# TUI (Terminal User Interface)
Source: https://anomalyco-opencode.mintlify.app/tui
Using the OpenCode terminal user interface for AI-powered coding
OpenCode provides an interactive terminal interface (TUI) for working on your projects with an LLM. The TUI offers a powerful, keyboard-driven experience for coding with AI assistance.
## Getting Started
Start the TUI by running:
```bash theme={null}
opencode
```
This starts OpenCode in the current directory. You can also specify a project path:
```bash theme={null}
opencode /path/to/project
```
### Start with a specific model
Use the `-m` or `--model` flag to start with a specific model:
```bash theme={null}
opencode --model anthropic/claude-sonnet-4
```
### Continue a previous session
Resume your last session with the `-c` or `--continue` flag:
```bash theme={null}
opencode --continue
```
Or continue a specific session by ID:
```bash theme={null}
opencode --session ses_abc123
```
### Fork a session
Create a fork of an existing session to explore alternative approaches:
```bash theme={null}
opencode --session ses_abc123 --fork
```
### Start with a prompt
Provide an initial prompt directly from the command line:
```bash theme={null}
opencode --prompt "Add unit tests for the auth module"
```
You can also pipe input to OpenCode:
```bash theme={null}
echo "Refactor this code" | opencode
```
***
## File References
You can reference files in your messages using the `@` symbol. This performs a fuzzy file search in your working directory:
```text theme={null}
How is authentication handled in @packages/functions/src/api/index.ts?
```
The file content is automatically added to the conversation context.
Use `@` to quickly add relevant files to the context without manually copying paths.
***
## Bash Commands
Start a message with `!` to run a shell command:
```bash theme={null}
!ls -la
```
The command output is added to the conversation as a tool result, allowing the AI to see and respond to the output.
***
## Slash Commands
The TUI supports slash commands for quick actions. Type `/` followed by a command name:
```bash theme={null}
/help
```
Most commands also have keyboard shortcuts using `ctrl+x` as the leader key.
### Available Commands
#### `/connect`
Add a provider to OpenCode. Select from available providers and add their API keys.
```bash theme={null}
/connect
```
#### `/compact`
Compact (summarize) the current session to reduce context usage.
```bash theme={null}
/compact
```
**Keybind:** `ctrl+x c`
**Alias:** `/summarize`
#### `/details`
Toggle tool execution details visibility in the conversation.
```bash theme={null}
/details
```
**Keybind:** `ctrl+x d`
#### `/editor`
Open an external editor for composing multi-line messages. Uses the editor specified in your `EDITOR` environment variable.
```bash theme={null}
/editor
```
**Keybind:** `ctrl+x e`
See [Editor Setup](#editor-setup) for configuration instructions.
#### `/exit`
Exit OpenCode.
```bash theme={null}
/exit
```
**Keybind:** `ctrl+x q`
**Aliases:** `/quit`, `/q`
#### `/export`
Export the current conversation to Markdown and open it in your default editor.
```bash theme={null}
/export
```
**Keybind:** `ctrl+x x`
#### `/help`
Show the help dialog with available commands and keybinds.
```bash theme={null}
/help
```
**Keybind:** `ctrl+x h`
#### `/init`
Create or update the `AGENTS.md` file for project-specific rules and instructions.
```bash theme={null}
/init
```
**Keybind:** `ctrl+x i`
#### `/models`
List available models from configured providers.
```bash theme={null}
/models
```
**Keybind:** `ctrl+x m`
#### `/new`
Start a new session.
```bash theme={null}
/new
```
**Keybind:** `ctrl+x n`
**Alias:** `/clear`
#### `/redo`
Redo a previously undone message. Only available after using `/undo`.
```bash theme={null}
/redo
```
**Keybind:** `ctrl+x r`
File changes made by the redone message will be restored. Your project must be a Git repository for this to work.
#### `/sessions`
List and switch between sessions.
```bash theme={null}
/sessions
```
**Keybind:** `ctrl+x l`
**Aliases:** `/resume`, `/continue`
#### `/share`
Share the current session. Generates a shareable link to your conversation.
```bash theme={null}
/share
```
**Keybind:** `ctrl+x s`
#### `/theme`
List and switch between available themes.
```bash theme={null}
/theme
```
**Keybind:** `ctrl+x t`
#### `/thinking`
Toggle visibility of thinking/reasoning blocks in the conversation. When enabled, you can see the model's reasoning process for models that support extended thinking.
```bash theme={null}
/thinking
```
This command only controls whether thinking blocks are displayed - it doesn't enable or disable the model's reasoning capabilities. Use `ctrl+t` to cycle through model variants that support reasoning.
#### `/undo`
Undo the last message in the conversation. Removes the most recent user message, all subsequent responses, and reverts any file changes.
```bash theme={null}
/undo
```
**Keybind:** `ctrl+x u`
File changes will be reverted using Git. Your project must be a Git repository for this to work.
#### `/unshare`
Unshare the current session, removing public access.
```bash theme={null}
/unshare
```
***
## Keyboard Shortcuts
The TUI is designed for keyboard-driven workflows. Here are the essential shortcuts:
### Leader Key Commands
Most commands use `ctrl+x` as the leader key, followed by a second key:
* `ctrl+x h` - Show help
* `ctrl+x n` - New session
* `ctrl+x l` - List sessions
* `ctrl+x c` - Compact session
* `ctrl+x d` - Toggle details
* `ctrl+x e` - Open editor
* `ctrl+x x` - Export conversation
* `ctrl+x s` - Share session
* `ctrl+x t` - Change theme
* `ctrl+x m` - List models
* `ctrl+x i` - Initialize AGENTS.md
* `ctrl+x u` - Undo
* `ctrl+x r` - Redo
* `ctrl+x q` - Quit
### Navigation
* `PageUp` / `PageDown` - Scroll by page
* `Home` / `End` - Jump to start/end of conversation
* `↑` / `↓` - Navigate through message history
### Model Variants
* `ctrl+t` - Cycle through model variants (default, extended thinking, etc.)
### Input Shortcuts
* `Enter` - Submit message
* `Meta+Enter` (macOS) or `Alt+Enter` (Linux/Windows) - Insert newline
* `Escape` - Blur input field
***
## Editor Setup
Both the `/editor` and `/export` commands use the editor specified in your `EDITOR` environment variable. If not set, they fall back to `VISUAL`.
### Linux/macOS
Add to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.):
```bash theme={null}
# Terminal editors
export EDITOR=nano
# or
export EDITOR=vim
# or
export EDITOR=nvim
# GUI editors (require --wait flag)
export EDITOR="code --wait" # VS Code
export EDITOR="cursor --wait" # Cursor
export EDITOR="windsurf --wait" # Windsurf
export EDITOR="zed --wait" # Zed
```
### Windows (CMD)
```cmd theme={null}
set EDITOR=notepad
# For GUI editors with --wait
set EDITOR=code --wait
```
To make it permanent, use **System Properties** > **Environment Variables**.
### Windows (PowerShell)
```powershell theme={null}
$env:EDITOR = "notepad"
# For GUI editors with --wait
$env:EDITOR = "code --wait"
```
To make it permanent, add to your PowerShell profile.
GUI editors like VS Code, Cursor, Windsurf, and Zed need the `--wait` flag to block until the editor is closed.
***
## Configuration
You can customize TUI behavior through your OpenCode config file (`opencode.json`):
```json theme={null}
{
"$schema": "https://opencode.ai/config.json",
"tui": {
"scroll_speed": 3,
"scroll_acceleration": {
"enabled": true
}
}
}
```
### Options
**`scroll_speed`**
Controls how fast the TUI scrolls when using scroll commands (minimum: `1`). Defaults to `3`.
This setting is ignored if `scroll_acceleration.enabled` is `true`.
**`scroll_acceleration.enabled`**
Enable macOS-style scroll acceleration for smooth, natural scrolling. When enabled, scroll speed increases with rapid scrolling gestures and stays precise for slower movements.
This setting takes precedence over `scroll_speed`.
***
## Customization
You can customize various aspects of the TUI through the command palette (`ctrl+x h` or `/help`). Settings persist across restarts.
### Username Display
Toggle whether your username appears in chat messages:
1. Open command palette: `ctrl+x h` or `/help`
2. Search for "username" or "hide username"
3. Toggle the setting
The setting is automatically saved and remembered across sessions.
***
## Running with a Server
By default, the TUI communicates directly with OpenCode's internal engine. You can optionally run a web server alongside the TUI:
```bash theme={null}
opencode --port 4096
```
This starts both the TUI and a web server, allowing you to:
* Access the web interface at `http://localhost:4096`
* Attach additional TUI instances to the same server
* Share sessions across multiple interfaces
### Network Access
Make the server accessible on your local network:
```bash theme={null}
opencode --hostname 0.0.0.0 --port 4096
```
### mDNS Discovery
Enable mDNS for automatic server discovery:
```bash theme={null}
opencode --mdns
```
This makes your server discoverable as `opencode.local` on your network.
***
## Tips and Tricks
### Auto-focus Input
The TUI automatically focuses the input field when you start typing (except when the terminal panel is open on desktop). This allows for a fluid, conversation-like experience.
### Scroll Behavior
The TUI automatically scrolls to follow new messages. If you scroll up to review previous messages, auto-scroll pauses. Scroll back to the bottom to resume auto-scrolling.
### Custom Commands
You can add custom slash commands through your OpenCode configuration. These commands can execute scripts, run build tools, or perform any custom automation.
### Agent Selection
Use the `--agent` flag to start with a specific agent:
```bash theme={null}
opencode --agent architect
```
Agents define different working modes and capabilities for OpenCode.
# Web Interface
Source: https://anomalyco-opencode.mintlify.app/web
Using OpenCode in your browser with the web interface
OpenCode can run as a web application in your browser, providing the same powerful AI coding experience without needing a terminal. The web interface offers a modern, graphical experience with file trees, diff viewers, and session management.
## Getting Started
Start the web interface by running:
```bash theme={null}
opencode web
```
This starts a local server on `127.0.0.1` with a random available port and automatically opens OpenCode in your default browser.
If `OPENCODE_SERVER_PASSWORD` is not set, the server will be unsecured. This is fine for local use but should be set for network access.
**Windows Users:** For the best experience, run `opencode web` from WSL rather than PowerShell. This ensures proper file system access and terminal integration.
***
## Configuration
You can configure the web server using command line flags or your OpenCode config file.
### Port
By default, OpenCode picks an available port. Specify a port:
```bash theme={null}
opencode web --port 4096
```
### Hostname
By default, the server binds to `127.0.0.1` (localhost only). To make OpenCode accessible on your network:
```bash theme={null}
opencode web --hostname 0.0.0.0
```
When using `0.0.0.0`, OpenCode displays both local and network addresses:
```
Local access: http://localhost:4096
Network access: http://192.168.1.100:4096
```
### mDNS Discovery
Enable mDNS to make your server discoverable on the local network:
```bash theme={null}
opencode web --mdns
```
This automatically sets the hostname to `0.0.0.0` and advertises the server as `opencode.local`.
Customize the mDNS domain name to run multiple instances:
```bash theme={null}
opencode web --mdns --mdns-domain myproject.local
```
### CORS
Allow additional domains for CORS (useful for custom frontends):
```bash theme={null}
opencode web --cors https://example.com
```
### Authentication
Protect access with a password using environment variables:
```bash theme={null}
OPENCODE_SERVER_PASSWORD=secret opencode web
```
The username defaults to `opencode` but can be changed:
```bash theme={null}
OPENCODE_SERVER_USERNAME=myuser OPENCODE_SERVER_PASSWORD=secret opencode web
```
***
## Config File
Configure server settings in your `opencode.json` config file:
```json theme={null}
{
"server": {
"port": 4096,
"hostname": "0.0.0.0",
"mdns": true,
"cors": ["https://example.com"]
}
}
```
Command line flags take precedence over config file settings.
***
## Using the Web Interface
Once started, the web interface provides a comprehensive view of your OpenCode sessions and workspace.
### Sessions
The homepage displays your sessions with key information:
* Active sessions with recent activity
* Session titles and timestamps
* Quick access to start new sessions
Click on a session to open it and continue your conversation.
### Chat Interface
The main chat interface includes:
* **Message Timeline** - View the full conversation history
* **Composer** - Write and submit messages to the AI
* **File References** - Add files to context using `@` mentions
* **Command Palette** - Access slash commands like `/compact`, `/share`, etc.
* **Model Selector** - Switch between available AI models
* **Agent Selector** - Choose different agent modes
### File Explorer
The file tree panel allows you to:
* Browse your project files
* Open files in the editor
* View file changes made during the session
* Add files to the conversation context
Toggle the file tree with the sidebar button or keyboard shortcut.
### Review Panel
The review panel shows all changes made during the session:
* **Session Changes** - All file modifications in the current session
* **Turn Changes** - Changes from the most recent AI response
* **Unified/Split Diff** - Toggle between diff viewing modes
* **Line Comments** - Add comments to specific code sections
The review panel helps you track and understand what the AI has modified.
### Terminal Panel
Access an integrated terminal for running commands:
* Run shell commands
* View command output
* Execute build scripts and tests
* Debug and explore your codebase
The terminal integrates seamlessly with the chat, allowing the AI to see command outputs.
### Context Management
Manage conversation context efficiently:
* **Context Tab** - View all files and information in the current context
* **Add/Remove Files** - Control what the AI can see
* **Context Usage** - Monitor token usage and context limits
* **Comments** - Add notes and instructions to specific code sections
### Server Status
Click "See Servers" on the homepage to view:
* Connected servers and their status
* Server URLs and connection information
* Health and availability indicators
***
## Keyboard Shortcuts
The web interface supports keyboard shortcuts for efficient navigation:
* `Cmd/Ctrl + K` - Open command palette
* `Cmd/Ctrl + Enter` - Submit message
* `Cmd/Ctrl + N` - New session
* `Cmd/Ctrl + /` - Focus chat input
* `Cmd/Ctrl + B` - Toggle file tree
* `Cmd/Ctrl + Shift + R` - Toggle review panel
***
## Mobile Support
The web interface is fully responsive and works on mobile devices:
* Touch-optimized interface
* Mobile-friendly navigation
* Swipe gestures for panels
* Tab-based layout for session and changes
Access OpenCode from your phone or tablet when away from your desk.
***
## Attaching a Terminal TUI
You can attach a terminal TUI to a running web server:
```bash theme={null}
# Start the web server
opencode web --port 4096
# In another terminal, attach the TUI
opencode attach http://localhost:4096
```
This allows you to use both the web interface and terminal simultaneously, sharing the same sessions and state.
Use the TUI for quick keyboard-driven tasks and the web interface for visual file browsing and diff review.
***
## Session Management
### Creating Sessions
Start a new session from the homepage or use the "New Session" button in the interface.
You can also create sessions with specific configurations:
* Choose a starting model
* Select an agent mode
* Set a working directory
### Sharing Sessions
Share sessions with team members:
1. Click the share button or use `/share` command
2. Copy the generated link
3. Share the link with others
Shared sessions are read-only by default. Recipients can view the conversation and fork it to continue working.
### Forking Sessions
Create a fork of an existing session to explore alternative approaches:
1. Open the session you want to fork
2. Click the fork button in the session header
3. Continue from that point with a new session ID
Forking is useful for trying different solutions without affecting the original conversation.
***
## Collaborative Features
### Multiple Windows
Open the same session in multiple browser windows or tabs. All instances stay synchronized:
* Messages appear in real-time across all windows
* File changes are reflected immediately
* Tool execution shows progress everywhere
### Cross-Device Access
With network access enabled, access your OpenCode server from any device on your network:
```bash theme={null}
opencode web --hostname 0.0.0.0 --port 4096
```
Then open `http://:4096` on another device.
***
## Performance Optimization
### Session Compaction
As conversations grow, compact them to reduce context usage:
1. Use the `/compact` command
2. Or click "Compact" in the session menu
Compaction summarizes previous turns while preserving important context.
### Context Limits
Monitor context usage in the session header:
* Token usage indicator shows current usage vs. model limits
* Warning appears when approaching the limit
* Compact or start a new session when needed
***
## Advanced Features
### MCP Servers
The web interface supports Model Context Protocol (MCP) servers configured in your OpenCode settings. MCP servers provide additional tools and capabilities to the AI.
### Custom Commands
Custom slash commands defined in your OpenCode configuration are available in the web interface command palette.
### Themes
The web interface supports multiple themes:
* Light and dark modes
* Automatic theme switching based on system preferences
* Custom theme configurations
### Language Support
The interface includes internationalization support for multiple languages. Language settings follow your browser preferences.
***
## Troubleshooting
### Server Won't Start
If the server fails to start:
1. Check if the port is already in use
2. Try a different port: `opencode web --port 4097`
3. Check firewall settings for blocked ports
### Can't Access from Network
If other devices can't connect:
1. Ensure you're using `--hostname 0.0.0.0`
2. Check firewall rules allow the port
3. Verify network connectivity between devices
### Authentication Issues
If authentication isn't working:
1. Verify `OPENCODE_SERVER_PASSWORD` is set
2. Check username if using custom `OPENCODE_SERVER_USERNAME`
3. Clear browser cookies and try again
### Performance Issues
If the interface feels slow:
1. Compact long sessions to reduce history
2. Close unused tabs and file previews
3. Check network latency if using remote access
4. Consider starting a new session for fresh performance
# Windows (WSL)
Source: https://anomalyco-opencode.mintlify.app/windows-wsl
Optimal OpenCode setup for Windows using Windows Subsystem for Linux
While OpenCode can run natively on Windows, **Windows Subsystem for Linux (WSL)** provides the best experience with superior performance, compatibility, and stability.
**Why WSL is recommended:**
* 10-20x faster file I/O operations
* Full POSIX compatibility for shell commands
* Seamless integration with Linux development tools
* Better terminal emulator support
* Native Git performance
## Quick Start
Open PowerShell as Administrator and run:
```powershell theme={null}
wsl --install
```
This installs:
* WSL 2 (latest version)
* Ubuntu Linux (default distribution)
* Required kernel components
Restart your computer when prompted.
After restart, open "Ubuntu" from the Start menu.
Create a UNIX username and password (does not need to match Windows credentials).
In the WSL terminal:
```bash theme={null}
curl -fsSL https://opencode.ai/install | bash
```
Or use npm/pnpm/brew:
```bash theme={null}
# npm
npm install -g opencode-cli
# pnpm
pnpm add -g opencode-cli
# Homebrew
brew install opencode
```
Navigate to your project and start:
```bash theme={null}
cd /mnt/c/Users/YourName/projects/my-app
opencode
```
## WSL Installation Details
### Prerequisites
* **Windows 10 version 2004+** (Build 19041+) or **Windows 11**
* **Virtualization** enabled in BIOS/UEFI
* **Administrator access** for initial setup
### Manual Installation
If `wsl --install` doesn't work:
```powershell theme={null}
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
```
```powershell theme={null}
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
```
Reboot is required for features to activate.
[WSL2 Kernel Update (x64)](https://aka.ms/wsl2kernel)
Install the downloaded MSI package.
```powershell theme={null}
wsl --set-default-version 2
```
Open Microsoft Store → Search "Ubuntu" → Install
Or via command line:
```powershell theme={null}
wsl --install -d Ubuntu
```
### Verify Installation
```powershell theme={null}
# Check WSL version
wsl --list --verbose
# Expected output:
# NAME STATE VERSION
# * Ubuntu Running 2
```
Ensure `VERSION` is `2`. If it shows `1`, upgrade:
```powershell theme={null}
wsl --set-version Ubuntu 2
```
## Accessing Windows Files
WSL mounts Windows drives under `/mnt/`:
```bash File Paths theme={null}
# Windows C:\ drive
/mnt/c/
# Windows D:\ drive
/mnt/d/
# Example: Windows Desktop
/mnt/c/Users/YourName/Desktop/
# Example: Documents folder
/mnt/c/Users/YourName/Documents/
```
```bash Navigate to Windows Project theme={null}
cd /mnt/c/Users/YourName/projects/my-app
opencode
```
### Performance Considerations
**Cross-filesystem operations are slow.**
Accessing Windows files from WSL (`/mnt/c/`) is significantly slower than native WSL filesystem (`~/`).
* Windows → WSL: \~30% slower
* WSL → Windows: \~60% slower
**For best performance, store projects in the WSL filesystem:**
```bash theme={null}
# Clone repos into WSL home directory
cd ~
mkdir -p ~/projects
cd ~/projects
git clone https://github.com/user/repo.git
cd repo
opencode
```
**Benchmark comparison (reading 1000 files):**
* Native WSL (`~/projects/`): \~0.8 seconds
* Windows mount (`/mnt/c/projects/`): \~3.2 seconds
VS Code Remote-WSL extension can edit files in `~/projects/` seamlessly.
## Architecture Patterns
### Pattern 1: TUI in WSL (Recommended)
**Best for:** Developers who prefer terminal interfaces.
```bash theme={null}
# In WSL terminal
cd ~/projects/my-app
opencode
```
✅ Pros:
* Best performance
* Full feature support
* Native terminal experience
❌ Cons:
* Terminal-only (no GUI)
### Pattern 2: Web Client + WSL Server
**Best for:** Developers who prefer browser-based UIs.
```bash theme={null}
opencode web --hostname 0.0.0.0 --port 3000
```
Navigate to `http://localhost:3000`
✅ Pros:
* Modern web UI
* Server runs in performant WSL environment
* Access from any Windows browser
❌ Cons:
* Requires browser
* Slightly higher resource usage
WSL automatically forwards `localhost` ports to Windows. No additional configuration needed.
### Pattern 3: Desktop App + WSL Server
**Best for:** Users who want native desktop app with WSL performance.
```bash theme={null}
export OPENCODE_SERVER_PASSWORD="your-secure-password"
opencode serve --hostname 0.0.0.0 --port 4096
```
Always set `OPENCODE_SERVER_PASSWORD` when using `--hostname 0.0.0.0`.
Download from [opencode.ai/download](https://opencode.ai/download)
In OpenCode Desktop:
1. Click server name in bottom-right
2. Enter URL: `http://localhost:4096`
3. Enter credentials (username: `opencode`, password: your password)
✅ Pros:
* Native desktop app experience
* Server performance benefits from WSL
* System notifications work
❌ Cons:
* Requires password authentication
* More complex setup
Get WSL's IP address:
```bash theme={null}
# In WSL
hostname -I
# Output: 172.20.10.5
```
Connect Desktop app to `http://172.20.10.5:4096` instead of `localhost:4096`.
## File Editing
### VS Code Remote-WSL (Recommended)
Edit files in WSL filesystem from Windows:
Download from [code.visualstudio.com](https://code.visualstudio.com/)
In VS Code:
1. Open Extensions (`Ctrl+Shift+X`)
2. Search "Remote - WSL"
3. Click Install
```bash theme={null}
# In WSL terminal
cd ~/projects/my-app
code .
```
VS Code opens on Windows, editing files in WSL.
In VS Code's terminal (`Ctrl+` \`):
```bash theme={null}
opencode
```
✅ Benefits:
* Edit in GUI, run in WSL
* Full IntelliSense and extensions
* Git integration
* Side-by-side with OpenCode TUI
### Windows File Explorer
Access WSL files from Windows:
```
\\wsl$\Ubuntu\home\yourusername\projects
```
1. Open File Explorer
2. Type `\\wsl$` in address bar
3. Navigate to your files
Editing large files via `\\wsl$` can be slow. Use VS Code Remote-WSL instead.
## Git Configuration
### Separate Git Configs
WSL and Windows Git are separate. Configure Git in WSL:
```bash theme={null}
# In WSL terminal
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
# Optional: Use Windows credential manager
git config --global credential.helper "/mnt/c/Program\\ Files/Git/mingw64/bin/git-credential-manager.exe"
```
### SSH Keys
Generate SSH keys in WSL:
```bash theme={null}
ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub
# Copy output and add to GitHub/GitLab
```
WSL SSH keys are separate from Windows. You'll need to add the new public key to GitHub/GitLab.
## Terminal Recommendations
### Windows Terminal (Best)
Modern terminal with tabs, GPU acceleration, and WSL integration.
**Install:**
* Microsoft Store: "Windows Terminal"
* Or via `winget`:
```powershell theme={null}
winget install Microsoft.WindowsTerminal
```
**Configure:**
1. Open Windows Terminal
2. Settings → Startup → Default profile: Ubuntu
3. Settings → Ubuntu → Appearance → Font: "Cascadia Code NF"
### Alternative: Termius, iTerm2-like
For users wanting more features:
* [Tabby](https://tabby.sh/) - Cross-platform, highly customizable
* [Fluent Terminal](https://github.com/felixse/FluentTerminal) - Modern UWP terminal
## Troubleshooting
**Error:** `The virtual machine could not be started...`
**Fix:**
1. Enable Virtualization in BIOS:
* Restart → Enter BIOS (F2/DEL/F12)
* Find "Virtualization Technology" or "VT-x/AMD-V"
* Enable → Save → Reboot
2. Ensure Windows features are enabled:
```powershell theme={null}
# Run as Administrator
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
```
**Symptoms:** OpenCode takes minutes to start, file edits lag.
**Cause:** Project is on Windows filesystem (`/mnt/c/`).
**Fix:** Move project to WSL:
```bash theme={null}
# Copy project to WSL
cp -r /mnt/c/Users/YourName/projects/my-app ~/projects/
cd ~/projects/my-app
opencode # Much faster!
```
**Symptoms:** `\\wsl$` path not found in File Explorer.
**Fix:**
1. Ensure WSL is running:
```powershell theme={null}
wsl --list --verbose
# Should show "Running"
```
2. Start WSL if stopped:
```powershell theme={null}
wsl
```
3. Retry accessing `\\wsl$\Ubuntu`
**Symptoms:** Git push asks for password every time.
**Fix:** Use Windows credential manager from WSL:
```bash theme={null}
git config --global credential.helper "/mnt/c/Program\\ Files/Git/mingw64/bin/git-credential-manager.exe"
```
Or set up SSH keys (see Git Configuration section).
**Cause:** Tool installed in Windows, not WSL.
**Fix:** Install tools in WSL:
```bash theme={null}
# Node.js via nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install --lts
# Python
sudo apt update
sudo apt install python3 python3-pip
# Docker
# Use Docker Desktop for Windows with WSL 2 backend
```
**Symptoms:** `opencode serve` fails with "port 4096 already in use".
**Cause:** Windows process using the port.
**Fix:**
```powershell theme={null}
# In PowerShell (as Administrator)
netstat -ano | findstr :4096
# Note the PID in last column
taskkill /PID /F
```
Or use a different port:
```bash theme={null}
opencode serve --port 4097
```
## Advanced: Docker in WSL
### Docker Desktop Integration
Download from [docker.com](https://www.docker.com/products/docker-desktop)
Docker Desktop → Settings → General → "Use the WSL 2 based engine" ✓
Docker Desktop → Settings → Resources → WSL Integration → Enable Ubuntu ✓
```bash theme={null}
docker --version
docker run hello-world
```
Now OpenCode can use Docker for:
* Running MCP servers in containers
* Containerized development environments
* Building and testing Docker images
## Performance Tuning
### Increase WSL Memory Limit
By default, WSL uses 50% of total RAM. For large projects:
```powershell theme={null}
# In PowerShell
notepad $env:USERPROFILE\.wslconfig
```
```ini theme={null}
[wsl2]
memory=8GB # Increase for large projects
processors=4 # Match CPU cores
swap=2GB
localhostForwarding=true
```
```powershell theme={null}
wsl --shutdown
wsl
```
### Disable Windows Defender for WSL
Windows Defender can slow down file operations:
Start → "Windows Security" → Virus & threat protection
Manage settings → Exclusions → Add or remove exclusions
Add:
* `%USERPROFILE%\AppData\Local\Packages\CanonicalGroupLimited.*`
* `\\wsl$\Ubuntu\home\\projects`
Only exclude trusted directories. Do not disable Defender entirely.
## Migration: Windows → WSL
Moving existing projects to WSL:
```powershell theme={null}
# In PowerShell
dir C:\Users\YourName\projects
```
```bash theme={null}
# In WSL
mkdir -p ~/projects
cp -r /mnt/c/Users/YourName/projects/* ~/projects/
```
```bash theme={null}
cd ~/projects/my-app
git remote -v # Verify URLs are correct
```
```bash theme={null}
# For Node projects
rm -rf node_modules package-lock.json
npm install
# For Python projects
rm -rf venv/
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
Leave originals on Windows until you've verified everything works in WSL.
## Best Practices
Use `~/projects/` instead of `/mnt/c/` for 3-4x faster performance.
Better performance, tabs, and customization than Command Prompt or PowerShell.
Edit files seamlessly while benefiting from WSL's speed.
Native container performance with Windows UI.
## Next Steps
Advanced server setup for WSL environments.
Configure proxies and certificates in WSL.
Diagnose and fix WSL-specific issues.
Connect OpenCode with VS Code in WSL.
# OpenCode Zen
Source: https://anomalyco-opencode.mintlify.app/zen
Curated list of models provided by OpenCode
OpenCode Zen is a list of tested and verified models provided by the OpenCode team.
OpenCode Zen is currently in beta.
Zen works like any other provider in OpenCode. You login to OpenCode Zen and get your API key. It's **completely optional** and you don't need to use it to use OpenCode.
## Background
There are a large number of models out there but only a few of these models work well as coding agents. Additionally, most providers are configured very differently; so you get very different performance and quality.
We tested a select group of models and providers that work well with OpenCode.
So if you are using a model through something like OpenRouter, you can never be sure if you are getting the best version of the model you want.
To fix this, we did a couple of things:
1. We tested a select group of models and talked to their teams about how to best run them.
2. We then worked with a few providers to make sure these were being served correctly.
3. Finally, we benchmarked the combination of the model/provider and came up with a list that we feel good recommending.
OpenCode Zen is an AI gateway that gives you access to these models.
## How It Works
OpenCode Zen works like any other provider in OpenCode.
Sign in to [OpenCode Zen](https://console.opencode.ai), add your billing details, and copy your API key.
Run the `/connect` command in the TUI, select OpenCode Zen, and paste your API key.
Run `/models` in the TUI to see the list of models we recommend.
You are charged per request and you can add credits to your account.
## Endpoints
You can also access our models through the following API endpoints.
| Model | Model ID | Endpoint | AI SDK Package |
| ------------------ | ------------------ | -------------------------------------------------- | --------------------------- |
| GPT 5.2 | gpt-5.2 | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5.2 Codex | gpt-5.2-codex | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5.1 | gpt-5.1 | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5.1 Codex | gpt-5.1-codex | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5.1 Codex Max | gpt-5.1-codex-max | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5.1 Codex Mini | gpt-5.1-codex-mini | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5 | gpt-5 | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5 Codex | gpt-5-codex | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| GPT 5 Nano | gpt-5-nano | `https://opencode.ai/zen/v1/responses` | `@ai-sdk/openai` |
| Claude Opus 4.6 | claude-opus-4-6 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Opus 4.5 | claude-opus-4-5 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Opus 4.1 | claude-opus-4-1 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Sonnet 4.6 | claude-sonnet-4-6 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Sonnet 4.5 | claude-sonnet-4-5 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Sonnet 4 | claude-sonnet-4 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Haiku 4.5 | claude-haiku-4-5 | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Claude Haiku 3.5 | claude-3-5-haiku | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` |
| Gemini 3.1 Pro | gemini-3.1-pro | `https://opencode.ai/zen/v1/models/gemini-3.1-pro` | `@ai-sdk/google` |
| Gemini 3 Pro | gemini-3-pro | `https://opencode.ai/zen/v1/models/gemini-3-pro` | `@ai-sdk/google` |
| Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` |
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| MiniMax M2.5 Free | minimax-m2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| MiniMax M2.1 | minimax-m2.1 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| GLM 5 | glm-5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| GLM 5 Free | glm-5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| GLM 4.7 | glm-4.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| GLM 4.6 | glm-4.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| Kimi K2.5 Free | kimi-k2.5-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| Kimi K2 Thinking | kimi-k2-thinking | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| Kimi K2 | kimi-k2 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| Qwen3 Coder 480B | qwen3-coder | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
| Big Pickle | big-pickle | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` |
The [model id](/config#models) in your OpenCode config uses the format `opencode/`. For example, for GPT 5.2 Codex, you would use `opencode/gpt-5.2-codex` in your config.
### Models
You can fetch the full list of available models and their metadata from:
```
https://opencode.ai/zen/v1/models
```
## Pricing
We support a pay-as-you-go model. Below are the prices **per 1M tokens**.
| Model | Input | Output | Cached Read | Cached Write |
| --------------------------------- | ------- | ------- | ----------- | ------------ |
| Big Pickle | Free | Free | Free | - |
| MiniMax M2.5 Free | Free | Free | Free | - |
| MiniMax M2.5 | \$0.30 | \$1.20 | \$0.06 | - |
| MiniMax M2.1 | \$0.30 | \$1.20 | \$0.10 | - |
| GLM 5 Free | Free | Free | Free | - |
| GLM 5 | \$1.00 | \$3.20 | \$0.20 | - |
| GLM 4.7 | \$0.60 | \$2.20 | \$0.10 | - |
| GLM 4.6 | \$0.60 | \$2.20 | \$0.10 | - |
| Kimi K2.5 Free | Free | Free | Free | - |
| Kimi K2.5 | \$0.60 | \$3.00 | \$0.08 | - |
| Kimi K2 Thinking | \$0.40 | \$2.50 | - | - |
| Kimi K2 | \$0.40 | \$2.50 | - | - |
| Qwen3 Coder 480B | \$0.45 | \$1.50 | - | - |
| Claude Opus 4.6 (≤ 200K tokens) | \$5.00 | \$25.00 | \$0.50 | \$6.25 |
| Claude Opus 4.6 (> 200K tokens) | \$10.00 | \$37.50 | \$1.00 | \$12.50 |
| Claude Opus 4.5 | \$5.00 | \$25.00 | \$0.50 | \$6.25 |
| Claude Opus 4.1 | \$15.00 | \$75.00 | \$1.50 | \$18.75 |
| Claude Sonnet 4.6 (≤ 200K tokens) | \$3.00 | \$15.00 | \$0.30 | \$3.75 |
| Claude Sonnet 4.6 (> 200K tokens) | \$6.00 | \$22.50 | \$0.60 | \$7.50 |
| Claude Sonnet 4.5 (≤ 200K tokens) | \$3.00 | \$15.00 | \$0.30 | \$3.75 |
| Claude Sonnet 4.5 (> 200K tokens) | \$6.00 | \$22.50 | \$0.60 | \$7.50 |
| Claude Sonnet 4 (≤ 200K tokens) | \$3.00 | \$15.00 | \$0.30 | \$3.75 |
| Claude Sonnet 4 (> 200K tokens) | \$6.00 | \$22.50 | \$0.60 | \$7.50 |
| Claude Haiku 4.5 | \$1.00 | \$5.00 | \$0.10 | \$1.25 |
| Claude Haiku 3.5 | \$0.80 | \$4.00 | \$0.08 | \$1.00 |
| Gemini 3.1 Pro (≤ 200K tokens) | \$2.00 | \$12.00 | \$0.20 | - |
| Gemini 3.1 Pro (> 200K tokens) | \$4.00 | \$18.00 | \$0.40 | - |
| Gemini 3 Pro (≤ 200K tokens) | \$2.00 | \$12.00 | \$0.20 | - |
| Gemini 3 Pro (> 200K tokens) | \$4.00 | \$18.00 | \$0.40 | - |
| Gemini 3 Flash | \$0.50 | \$3.00 | \$0.05 | - |
| GPT 5.2 | \$1.75 | \$14.00 | \$0.175 | - |
| GPT 5.2 Codex | \$1.75 | \$14.00 | \$0.175 | - |
| GPT 5.1 | \$1.07 | \$8.50 | \$0.107 | - |
| GPT 5.1 Codex | \$1.07 | \$8.50 | \$0.107 | - |
| GPT 5.1 Codex Max | \$1.25 | \$10.00 | \$0.125 | - |
| GPT 5.1 Codex Mini | \$0.25 | \$2.00 | \$0.025 | - |
| GPT 5 | \$1.07 | \$8.50 | \$0.107 | - |
| GPT 5 Codex | \$1.07 | \$8.50 | \$0.107 | - |
| GPT 5 Nano | Free | Free | Free | - |
You might notice *Claude Haiku 3.5* in your usage history. This is a [low cost model](/config#models) that's used to generate the titles of your sessions.
Credit card fees are passed along at cost (4.4% + \$0.30 per transaction); we don't charge anything beyond that.
The free models:
* GLM 5 Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
* Kimi K2.5 Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
* MiniMax M2.5 Free is available on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
* Big Pickle is a stealth model that's free on OpenCode for a limited time. The team is using this time to collect feedback and improve the model.
[Contact us](mailto:hello@opencode.ai) if you have any questions.
### Auto-reload
If your balance goes below $5, Zen will automatically reload $20.
You can change the auto-reload amount. You can also disable auto-reload entirely.
### Monthly Limits
You can also set a monthly usage limit for the entire workspace and for each member of your team.
For example, let's say you set a monthly usage limit to $20, Zen will not use more than $20 in a month. But if you have auto-reload enabled, Zen might end up charging you more than $20 if your balance goes below $5.
## Privacy
All our models are hosted in the US. Our providers follow a zero-retention policy and do not use your data for model training, with the following exceptions:
* Big Pickle: During its free period, collected data may be used to improve the model.
* GLM 5 Free: During its free period, collected data may be used to improve the model.
* Kimi K2.5 Free: During its free period, collected data may be used to improve the model.
* MiniMax M2.5 Free: During its free period, collected data may be used to improve the model.
* OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data).
* Anthropic APIs: Requests are retained for 30 days in accordance with [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage).
## For Teams
Zen also works great for teams. You can invite teammates, assign roles, curate the models your team uses, and more.
Workspaces are currently free for teams as a part of the beta.
Managing your workspace is currently free for teams as a part of the beta. We'll be sharing more details on the pricing soon.
Invite teammates and assign Admin or Member roles with customizable spending limits.
Enable or disable specific models for the workspace to control data collection and costs.
Use your own OpenAI or Anthropic API keys while still accessing other models in Zen.
Manage API keys, billing, and spending limits all in one place.
### Roles
You can invite teammates to your workspace and assign roles:
* **Admin**: Manage models, members, API keys, and billing
* **Member**: Manage only their own API keys
Admins can also set monthly spending limits for each member to keep costs under control.
### Model Access
Admins can enable or disable specific models for the workspace. Requests made to a disabled model will return an error.
This is useful for cases where you want to disable the use of a model that collects data.
### Bring Your Own Key
You can use your own OpenAI or Anthropic API keys while still accessing other models in Zen.
When you use your own keys, tokens are billed directly by the provider, not by Zen.
For example, your organization might already have a key for OpenAI or Anthropic and you want to use that instead of the one that Zen provides.
## Goals
We created OpenCode Zen to:
1. **Benchmark** the best models/providers for coding agents.
2. Have access to the **highest quality** options and not downgrade performance or route to cheaper providers.
3. Pass along any **price drops** by selling at cost; so the only markup is to cover our processing fees.
4. Have **no lock-in** by allowing you to use it with any other coding agent. And always let you use any other provider with OpenCode as well.