docs: expand user guides

This commit is contained in:
Dax Raad 2026-07-09 16:55:34 -04:00
commit f6f0a5c10f
20 changed files with 3149 additions and 52 deletions

288
packages/docs/agents.mdx Normal file
View file

@ -0,0 +1,288 @@
---
title: "Agents"
description: "Configure and use primary agents and subagents in OpenCode."
---
Agents combine a system prompt, model preference, tool permissions, and display
metadata into a reusable assistant profile. OpenCode includes agents for common
workflows, and you can override them or add your own in configuration or
Markdown files.
## Modes
An agent's `mode` controls where it can run:
| Mode | Behavior |
| --- | --- |
| `primary` | Can be selected as the main agent for a session. It cannot be launched as a subagent. |
| `subagent` | Can run in a child session through the `subagent` tool, but cannot be selected as the main agent. |
| `all` | Can be used either way. This is the default for a custom agent when `mode` is omitted. |
In the TUI, press <kbd>Tab</kbd> and <kbd>Shift</kbd>+<kbd>Tab</kbd> to cycle
through visible primary and `all` agents, or use `/agents` to choose one.
Subagents run in child sessions with fresh context. A primary agent can invoke
one with the `subagent` tool, either in the foreground or in the background.
You can also `@` mention a visible subagent to ask the current agent to delegate
work to it:
```text
@explore find where authentication errors are handled
```
The parent agent's `subagent` permission controls which agents it may launch.
The child currently uses its own configured permissions, not a restricted copy
of the parent's permissions.
## Built-in agents
| Agent | Mode | Purpose |
| --- | --- | --- |
| **Build** (`build`) | `primary` | Default coding agent. Tools are allowed by default, sensitive environment-file reads ask for approval, and access outside the workspace asks for approval. |
| **Plan** (`plan`) | `primary` | Planning agent. File edits are denied except for OpenCode plan files. Shell commands are not generally denied. |
| **General** (`general`) | `subagent` | General-purpose research and multi-step work. It has broad tool access but cannot launch more subagents. |
| **Explore** (`explore`) | `subagent` | Read-only code and web exploration using `read`, `glob`, `grep`, `webfetch`, and `websearch`. |
OpenCode also has hidden `compaction`, `title`, and `summary` system agents.
They run internal maintenance tasks and are not selectable. There is no built-in
`scout` agent in V2.
You can override a built-in agent with an entry of the same ID. Set
`disabled: true` to remove one.
## Default agent
Set the primary agent used when a session has not selected one:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"default_agent": "reviewer"
}
```
The configured agent must exist, must not have `mode: "subagent"`, and must not
be hidden. If it is unavailable, OpenCode falls back to `build`, then to the
first visible agent that can run as a primary agent. This selection does not
rewrite the agent already stored on an existing session.
## Configure agents
### JSON or JSONC
Use the plural `agents` field in any [OpenCode configuration file](/config):
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"default_agent": "reviewer",
"agents": {
"reviewer": {
"description": "Reviews changes for correctness, security, and missing tests",
"mode": "all",
"model": "anthropic/claude-sonnet-4-5#high",
"system": "Review the current changes. Report findings before any summary.",
"color": "warning",
"steps": 8,
"permissions": [
{ "action": "edit", "resource": "*", "effect": "deny" },
{ "action": "shell", "resource": "*", "effect": "deny" }
]
},
"build": {
"permissions": [
{ "action": "shell", "resource": "git push *", "effect": "ask" }
]
}
}
}
```
Agent definitions merge in configuration order. Later scalar fields replace
earlier values, request maps merge by key, and permission rules are appended.
Global `permissions` are applied to every agent before its agent-specific rules,
so a later agent rule can refine a global rule.
### Markdown files
The recommended file locations are:
```text
~/.config/opencode/agents/<name>.md
.opencode/agents/<name>.md
```
OpenCode discovers project `.opencode` directories from the current directory
up to the project root. The path below `agents/` becomes the agent ID, so
`.opencode/agents/team/reviewer.md` defines `team/reviewer`.
Frontmatter uses the same fields as an entry under `agents`. The Markdown body
becomes `system`:
```md title=".opencode/agents/reviewer.md"
---
description: Reviews changes without modifying files
mode: subagent
model: anthropic/claude-sonnet-4-5#high
color: warning
steps: 8
permissions:
- action: edit
resource: "*"
effect: deny
- action: shell
resource: "*"
effect: deny
---
Review for correctness, security, regressions, and missing tests.
List findings in severity order with file and line references.
```
For compatibility with older layouts, V2 also discovers Markdown under
`agent/`, and treats files under `mode/` or `modes/` as primary agents. Prefer
`agents/` for new files.
## Options
### `description`
Explains the agent's purpose. It is optional, but strongly recommended for
subagents because OpenCode includes it in the subagent catalog shown to the
model.
### `mode`
Accepts `primary`, `subagent`, or `all`. The default is `all`.
### `model`
Selects a model using `provider/model` with an optional `#variant`:
```jsonc
{
"agents": {
"reviewer": {
"model": "anthropic/claude-sonnet-4-5#high"
}
}
}
```
The equivalent expanded form is:
```jsonc
{
"agents": {
"reviewer": {
"model": {
"providerID": "anthropic",
"model": "claude-sonnet-4-5",
"variant": "high"
}
}
}
}
```
The TUI uses this as the preferred model when the agent is selected. A child
session uses its subagent's configured model, or inherits the parent session's
model when none is configured. In the API, the session's selected model is
stored separately; creating or switching a primary session with only an agent
ID does not itself change that session model.
### `system`
Sets the agent's system prompt. A non-empty value replaces OpenCode's
provider-specific base prompt for that agent. Project instructions, skills,
references, and other instruction sources are still added separately.
For a Markdown agent, use the document body instead of a `system` frontmatter
field.
### `permissions`
Permissions are an ordered array of rules:
```jsonc
{
"agents": {
"orchestrator": {
"permissions": [
{ "action": "subagent", "resource": "*", "effect": "deny" },
{ "action": "subagent", "resource": "explore", "effect": "allow" },
{ "action": "shell", "resource": "git *", "effect": "ask" }
]
}
}
}
```
Each rule has:
| Field | Meaning |
| --- | --- |
| `action` | Tool or permission action, with `*` wildcards supported. |
| `resource` | The path, command, agent ID, or other resource matched by the action. Wildcards are supported. |
| `effect` | `allow`, `ask`, or `deny`. |
The last matching rule wins. Important V2 action names include `shell` for
shell commands, `edit` for all edit/write/patch tools, and `subagent` for child
agents. Other tools generally use their tool name, such as `read`, `glob`,
`grep`, `webfetch`, `websearch`, and `skill`.
<Tip>
Put broad wildcard rules first and exceptions afterward. For example, deny
all subagents first, then allow `explore`.
</Tip>
`~` and `$HOME` are expanded in filesystem resources for `read`, `edit`, and
`external_directory`. Shell resources are raw command text and are not
expanded.
### `steps`
Sets a positive maximum number of model steps. On the final allowed step,
OpenCode removes tools and asks the model to summarize its work in text. New
user input resets the allowance.
### `hidden`
When `true`, removes the agent from normal selectors, `@` autocomplete, and the
subagent catalog advertised to models. It is a visibility setting, not a
security boundary.
### `color`
Sets the agent's UI color. Use a six-digit hex color such as `#ff6b6b`, or one
of `primary`, `secondary`, `accent`, `success`, `warning`, `error`, or `info`.
### `disabled`
When `true`, removes the agent definition at that point in configuration
loading. This works for built-in and custom agents.
### `request`
The V2 schema accepts per-agent request `headers` and JSON `body` overlays:
```jsonc
{
"agents": {
"reviewer": {
"request": {
"headers": { "x-agent": "reviewer" },
"body": { "temperature": 0.1 }
}
}
}
}
```
<Warning>
The current V2 session runner preserves these overlays on the agent
definition but does not yet apply them to model requests. Configure effective
request settings on the provider, model, or model variant instead. Do not use
legacy top-level agent fields such as `temperature`, `top_p`, `prompt`,
`permission`, `tools`, `disable`, or `maxSteps` in new V2 configuration.
</Warning>

View file

@ -0,0 +1,168 @@
---
title: "Attachments"
description: "Attach supported files and images to V2 prompts and configure image processing."
---
OpenCode can add local context to a prompt as text or image media. Current V2
sessions make these attachment types visible to the model:
| Input | Model receives |
| --- | --- |
| UTF-8 text file | The filename and decoded text |
| Directory | A non-recursive listing of its immediate files and directories |
| PNG, JPEG, GIF, or WebP | Image media |
SVG files are treated as text, not image media. PDF, AVIF, BMP, audio, video,
and other binary prompt attachments are not currently included in the model
request. Some clients may let you select a PDF, but V2 does not yet make that
PDF visible to the model.
<Warning>
Use a model that supports image input before attaching an image. OpenCode
passes supported image media to the selected provider, but the provider and
model still enforce their own formats, dimensions, file counts, and size
limits. A text-only model may reject the request.
</Warning>
## Add attachments
### TUI
Type `@` followed by a filename and select the result to attach a project file.
This is the preferred way to add source code and other text files:
```text
Explain the error handling in @src/server.ts
```
Paste an image from the clipboard with the configured paste key, `Ctrl+V` by
default. You can also drag a supported image into a terminal that exposes the
dropped file path to the TUI. The TUI reads PNG, JPEG, GIF, and WebP as image
attachments; a dropped SVG is inserted as text.
### Desktop and web
Use **Attach file**, paste, or drag and drop. Attach UTF-8 text or a PNG, JPEG,
GIF, or WebP image. The desktop file picker limits one selection to 20 MiB in
total; the server also applies the per-attachment limit described below.
### CLI
Pass `--file` or `-f` to `opencode2 run`. Repeat the flag for multiple files:
```bash
opencode2 run -f src/server.ts -f screenshot.png "Explain the failure"
```
The run command accepts at most 100 file flags and reads at most 10 MiB per
file. Use it for text files and the four supported image formats; other binary
files do not become model context.
### API
The V2 prompt and command payloads accept a `files` array. Each item requires a
`uri` and can include `name` and `description`:
```bash
opencode2 api post /api/session/ses_example/prompt --data '{
"text": "Review this file",
"files": [
{
"uri": "file:///home/me/project/src/server.ts",
"name": "server.ts",
"description": "Request handler"
}
]
}'
```
Use an absolute `file:` URL for a file available to the server, or an inline
data URL:
```json
{
"text": "What is wrong with this layout?",
"files": [
{
"uri": "data:image/png;base64,<base64-data>",
"name": "layout.png"
}
]
}
```
HTTP and HTTPS attachment URLs are not supported. OpenCode materializes each
attachment before admitting the prompt and rejects invalid URLs, unreadable
paths, non-files other than directories, and attachments over 20 MiB decoded.
For a text `file:` URL, optional positive `start` and `end` query parameters
select one-based lines:
```text
file:///home/me/project/src/server.ts?start=20&end=60
```
The server infers the media type from the bytes. A supplied filename or data
URL media type does not make an unsupported binary format model-visible.
## Configure image processing
Configure image normalization in `opencode.json` or `opencode.jsonc`:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"attachments": {
"image": {
"auto_resize": true,
"max_width": 2000,
"max_height": 2000,
"max_base64_bytes": 5242880
}
}
}
```
All fields are optional:
| Field | Default | Behavior |
| --- | ---: | --- |
| `auto_resize` | `true` | Resize an image that exceeds any configured limit. If `false`, reject it. |
| `max_width` | `2000` | Maximum width in pixels. Must be a positive integer. |
| `max_height` | `2000` | Maximum height in pixels. Must be a positive integer. |
| `max_base64_bytes` | `5242880` | Maximum byte length of the Base64-encoded image string. Must be a positive integer. |
<Note>
In the current V2 runtime, these settings apply to image media produced by
the built-in `read` tool. Images attached directly through the TUI, desktop,
web, CLI, or API bypass this normalization. Resize direct attachments before
adding them if the provider requires smaller media.
</Note>
The `read` tool recognizes PNG, JPEG, GIF, and WebP by their contents and will
ingest at most 20 MiB of source image bytes. It decodes the image and compares
its width, height, and encoded Base64 length with all three configured limits.
When `auto_resize` is `true`, OpenCode preserves the aspect ratio, scales the
image down to the dimension limits, and tries progressively smaller PNG and
JPEG encodings until the Base64 limit is met. The resulting media type can
therefore change to PNG or JPEG. If no encoding fits, the tool call fails.
When `auto_resize` is `false`, exceeding any limit fails the tool call without
modifying the image. An image that cannot be decoded also fails. If the image
resizer cannot be loaded, the `read` tool returns the
original image instead, so these settings are processing limits rather than an
upload or security boundary.
## Limits and provider behavior
- Direct prompt attachments are limited to 20 MiB decoded per item by the V2
server. Client-specific limits can be lower.
- `max_base64_bytes` counts the encoded Base64 characters in bytes, not the
decoded file size and not the complete `data:` URL.
- Text attachments are inserted into the prompt as text and do not require a
multimodal model. Large text read through the `read` tool has separate
paging and truncation limits.
- Image attachments use provider-native image input. Provider errors can still
occur when OpenCode's limits pass but the selected model's limits do not.
- PDFs and other unsupported binary prompt attachments should be converted to
text or supported images before attaching them.

164
packages/docs/commands.mdx Normal file
View file

@ -0,0 +1,164 @@
---
title: "Commands"
description: "Create reusable slash commands from configuration or Markdown files."
---
Custom commands turn a named prompt template into a slash command. Type the
command in the TUI, followed by any arguments:
```text
/review src/auth
```
## Configure with JSON
Add commands under the plural `commands` key in any OpenCode JSON or JSONC
[configuration file](/config). Each entry's key is the command name and
`template` is required.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"commands": {
"review": {
"description": "Review code for correctness and missing tests",
"template": "Review $ARGUMENTS. Report bugs first, then missing tests.",
"agent": "plan",
"model": "anthropic/claude-sonnet-4-5#high"
}
}
}
```
Run it with:
```text
/review src/auth
```
## Configure with Markdown
OpenCode discovers `.md` command files in both the singular `command/` and
plural `commands/` directories:
```text
~/.config/opencode/commands/ # Global
.opencode/commands/ # Project
```
The equivalent `command/` paths also work. Files may be nested; for example,
`.opencode/commands/team/review.md` defines `/team/review`. Files with other
extensions, including `.mdx`, are not discovered.
```md title=".opencode/commands/review.md"
---
description: Review code for correctness and missing tests
agent: plan
model: anthropic/claude-sonnet-4-5#high
---
Review $ARGUMENTS. Report bugs first, then missing tests.
```
The file body, with surrounding whitespace removed, is the command template.
JSON and Markdown commands share one registry. Project definitions take
precedence over global definitions, and a later definition can override a
built-in or earlier command with the same name. Changes are reloaded
automatically.
## Fields
| Field | Required | Behavior |
| --- | --- | --- |
| `template` | JSON only | Prompt template. In a Markdown command, the file body supplies it. |
| `description` | No | Text shown with the command in autocomplete. |
| `agent` | No | Agent selected before the prompt runs. |
| `model` | No | Model override in `provider/model` or `provider/model#variant` format. |
| `subtask` | No | Accepted as a boolean, but currently has no execution effect in V2. |
The four optional fields can be used in JSON or YAML frontmatter. Do not put
`template` in frontmatter because the Markdown body always supplies it.
## Arguments
Use `$ARGUMENTS` for the complete argument string:
```md title=".opencode/commands/component.md"
---
description: Create a component
---
Create a typed React component named $ARGUMENTS.
```
```text
/component Button
```
Use `$1`, `$2`, and higher numbers for parsed positional arguments. Single and
double quotes group text containing spaces and are removed during parsing.
```md title=".opencode/commands/check.md"
---
description: Check one area with a specific focus
---
Check $1. Focus on $2.
```
```text
/check src/auth "error handling and missing tests"
```
The highest-numbered positional placeholder present in the template consumes
that argument and all remaining arguments. For example, if a template contains
only `$1`, then `$1` receives the full parsed argument list. Missing positions
become empty strings.
If a template contains neither positional placeholders nor `$ARGUMENTS`,
OpenCode appends non-empty arguments to the template after a blank line.
## Shell interpolation
Wrap a shell command in `!` followed by backticks to insert its output before
the prompt is submitted:
```md title=".opencode/commands/review-diff.md"
---
description: Review the current diff
---
Review this diff:
!`git diff --stat && git diff`
```
OpenCode runs each interpolation with the configured shell in the active
project location and inserts its combined output into the template. Argument
interpolation happens first, so avoid placing untrusted arguments inside shell
interpolations.
<Warning>
Shell interpolations run when the command is evaluated, outside the agent's
tool permission flow. Only use commands from sources you trust.
</Warning>
No other template interpolation is performed. In particular, an `@path`
written into a stored template remains ordinary prompt text; V2 does not
automatically attach that file.
## Agent, model, and execution
Running a command evaluates its arguments and shell blocks, submits the result
as a durable user prompt in the current session, and schedules normal model
execution.
If `agent` is set, it overrides the agent selected when the command was
invoked and becomes the session's active agent. If `model` is set, it overrides
the model. Otherwise, a model configured on the command's agent takes
precedence over the model selected at invocation.
Although `subtask` is accepted in JSON and frontmatter, V2 currently ignores
it: commands run in the current session and do not create a child session.
Selecting an agent whose mode is `subagent` also does not turn the command into
a subtask.

View file

@ -0,0 +1,151 @@
---
title: "Context compaction"
description: "Configure and run context compaction in OpenCode V2."
---
Compaction replaces the active model context from an older part of a session
with a generated checkpoint. The checkpoint contains a structured summary and
a serialized tail of recent context, so the agent can continue with more room
in the model's context window.
Compaction is lossy, but it does not delete the earlier durable session
messages. After a successful compaction, V2 builds model requests from the
latest completed checkpoint and the messages that follow it.
## Automatic compaction
Automatic compaction is enabled by default. Before a model call, V2 estimates
the size of the final system prompt, messages, and advertised tools. It starts
compaction when:
```text
estimated tokens > context limit - max(requested output tokens, buffer)
```
The estimate is approximate: V2 JSON-serializes the request and assumes four
characters per token. When compaction succeeds, V2 rebuilds the request from
the new checkpoint and retries the step without promoting the input again.
V2 also recognizes provider errors classified as context overflow. If an
overflow occurs before the provider produces assistant output or other retry
evidence, V2 can compact and retry that step once. This recovery is attempted
even when `auto` is `false`; `auto` controls only the preflight size check. A
second overflow after recovery is returned as an error.
## Manual compaction
In the TUI, run:
```text
/compact
```
`/summarize` is an alias. The default keybind is `<leader>c`, configured as
`session_compact`.
A manual request is durably admitted and wakes the session runner. It can
compact short histories that would not trigger automatic compaction. If the
session is busy, compaction runs at the next safe drain boundary before later
steered or queued prompts are promoted. Repeated requests while one is pending
coalesce into that pending request. Whether compaction completes or fails, the
barrier is then settled so later prompts can proceed.
The CLI has no separate `compact` subcommand. Use the TUI command or the server
API. For example:
```bash
opencode2 api v2.session.compact \
--param sessionID=ses_example \
--data '{}'
```
The equivalent raw request is:
```bash
opencode2 api post /api/session/ses_example/compact --data '{}'
```
`POST /api/session/:sessionID/compact` returns the admitted compaction input;
it does not wait for summary generation. Clients can call
`client.session.compact({ sessionID })` and then wait for the session or follow
the `session.compaction.*` events. Supplying an optional message `id` makes an
exact retry idempotent, but reusing an ID owned by another record returns a
conflict.
## Configuration
Add `compaction` to any [OpenCode configuration file](/config):
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"compaction": {
"auto": true,
"prune": false,
"keep": {
"tokens": 8000
},
"buffer": 20000
}
}
```
| Field | Default | V2 behavior |
| --- | ---: | --- |
| `auto` | `true` | Runs the preflight context-size check. It does not disable manual compaction or one-shot provider-overflow recovery. |
| `prune` | None | Accepted by the V2 schema, but currently has no runtime effect. V2 does not prune old tool outputs in place. |
| `keep.tokens` | `8000` | Approximate number of tokens from the newest serialized conversation context to retain beside the summary. |
| `buffer` | `20000` | Token reserve used by the automatic threshold. The requested model output allowance wins when it is larger. |
`keep.tokens` and `buffer` accept non-negative integers. Larger `keep.tokens`
preserves more recent detail but leaves less room for future work. Larger
`buffer` triggers preflight compaction earlier.
## Checkpoint contents
V2 uses the session's selected or default model to generate the summary, with
tools disabled and at most 4096 output tokens. The summary records the
objective, important details, completed and active work, blockers, next moves,
and relevant files.
The newest serialized context up to `keep.tokens` is retained separately. This
is not a byte-for-byte transcript: tool output is limited to 2000 characters,
and file or media attachments become textual descriptors rather than embedded
data. On later compactions, V2 updates the previous summary and carries forward
its retained recent context before selecting a new tail.
The completed checkpoint is presented to the model as historical conversation
context, explicitly not as new instructions. Running and failed compactions are
not included in model context.
## Instructions and checkpoints
Conversation compaction and the durable instruction checkpoint are separate.
Before each model step, V2 compares live instruction sources with what that
session's model was last told. Ordinary changes are durable chronological
system updates and do not rewrite the established instruction baseline.
After a completed compaction, the next model step creates a fresh instruction
baseline from current sources. If a source is temporarily unavailable, V2
restates the last-applied value instead of treating it as removed. Session
movement and a committed revert reset the instruction checkpoint as well. See
[Instructions](/instructions) for source ordering and update behavior.
## Current limitations
- `prune` is reserved configuration; V1-style in-place tool-output pruning is
not implemented in V2.
- Compaction requires a resolvable model with a positive catalog context limit.
There is no separate compaction-model setting or fallback model.
- Summary generation can fail if the summary prompt itself cannot fit beside
its output allowance, the model returns no summary, or the provider fails.
- Automatic and overflow compaction need older conversation context that can be
replaced. A provider overflow can still surface when there is no compressible
head or fixed instructions and tool schemas dominate the request.
- Overflow recovery retries only once per step. Token estimation is heuristic,
so it cannot prevent every provider-specific overflow.
- Earlier durable messages remain stored even though they are no longer in the
active model context.
V1 used additional tail-turn and pruning behavior. Those V1 details are only
migration context; the settings and behavior on this page describe V2.

View file

@ -44,8 +44,12 @@ Project-specific configuration can use either form:
```
When OpenCode starts, it searches for configuration files from the current
directory upward to the project root. The files are merged, and configuration
closer to the current directory takes precedence.
directory upward to the project root. It merges direct `opencode.json(c)` files
from the project root toward the current directory, then does the same for
files inside `.opencode` directories. A `.opencode` config therefore overrides
every direct config, even when the direct config is closer to the current
directory. Avoid mixing the two forms across one project hierarchy unless this
precedence is intentional.
For example, consider a monorepo with OpenCode started from
`/home/user/projects/acme/packages/web`:
@ -67,9 +71,9 @@ OpenCode applies these files from lowest to highest precedence:
2. `/home/user/projects/acme/opencode.json`
3. `/home/user/projects/acme/packages/web/opencode.json`
Settings in the package config override matching settings from the repository
config, which override matching settings from the global config. Settings that
do not conflict are preserved from every file.
In this direct-config example, the package config overrides matching settings
from the repository config, which overrides matching settings from the global
config. Settings that do not conflict are preserved from every file.
## Schema
@ -100,16 +104,16 @@ Set the shell used by the terminal and shell tools.
### Model
Set the default model in `provider/model` format. Add `#variant` to select a
specific model variant.
Set the default model in `provider/model` format. The root default currently
does not retain a `#variant`; select variants in the TUI or on an agent or command.
```jsonc
{
"model": "anthropic/claude-sonnet-4-5#high"
"model": "anthropic/claude-sonnet-4-5"
}
```
See the [models guide](https://opencode.ai/docs/models/) for model selection
See the [models guide](/models) for model selection
and local models.
### Default agent
@ -122,13 +126,15 @@ Choose the primary agent used when a session does not select one explicitly.
}
```
See the [agents guide](https://opencode.ai/docs/agents/) for built-in and custom
See the [agents guide](/agents) for built-in and custom
agents.
### Autoupdate
Control automatic updates. Set this to `false` to disable updates or `"notify"`
to receive update notifications.
Control automatic updates from the global config. Set this to `false` to
disable updates. The current beta treats `true` and `"notify"` identically and
automatically installs compatible non-major updates; project-level values are
ignored.
```jsonc
{
@ -138,8 +144,8 @@ to receive update notifications.
### Sharing
Control whether sessions can be shared manually, shared automatically, or not
shared at all.
Set the intended session sharing policy. V2 accepts this field, but session
sharing is not implemented yet.
```jsonc
{
@ -147,11 +153,12 @@ shared at all.
}
```
See the [sharing guide](https://opencode.ai/docs/share/) for more details.
See the [sharing guide](/sharing) for more details.
### Username
Set the username displayed in conversations.
Set a username for future display behavior. V2 accepts this field but does not
currently display it in conversations.
```jsonc
{
@ -168,7 +175,7 @@ matching resource.
{
"permissions": [
{
"action": "bash",
"action": "shell",
"resource": "git push *",
"effect": "ask"
}
@ -176,8 +183,7 @@ matching resource.
}
```
See the [permissions guide](https://opencode.ai/docs/permissions/) for rule
matching and available actions.
See the [permissions guide](/permissions) for rule matching and available actions.
### Agents
@ -199,12 +205,11 @@ instructions, mode, and permissions.
}
```
See the [agents guide](https://opencode.ai/docs/agents/) for all agent options
and file-based agents.
See the [agents guide](/agents) for all agent options and file-based agents.
### Snapshots
Enable or disable the snapshots used by undo and revert behavior.
Enable or disable filesystem snapshots used by undo and revert behavior.
```jsonc
{
@ -212,6 +217,8 @@ Enable or disable the snapshots used by undo and revert behavior.
}
```
See the [snapshots guide](/snapshots) for undo and redo behavior.
### Watcher
Ignore files and directories that should not trigger filesystem updates.
@ -226,8 +233,8 @@ Ignore files and directories that should not trigger filesystem updates.
### Formatter
Enable built-in formatters, disable formatting entirely, or configure formatter
commands by name.
Define formatter settings for compatibility and future use. V2 accepts this
field, but it does not run formatters yet.
```jsonc
{
@ -240,12 +247,12 @@ commands by name.
}
```
See the [formatters guide](https://opencode.ai/docs/formatters/) for built-in
formatters and custom commands.
See the [formatters guide](/formatters) for accepted fields and current limitations.
### LSP
Enable built-in language servers, disable them, or configure servers by name.
Define language server settings for compatibility and future use. V2 accepts
this field, but it does not start language servers yet.
```jsonc
{
@ -258,12 +265,12 @@ Enable built-in language servers, disable them, or configure servers by name.
}
```
See the [LSP guide](https://opencode.ai/docs/lsp/) for language server setup.
See the [LSP guide](/lsp) for accepted fields and current limitations.
### Attachments
Control how oversized image attachments are resized or rejected before they are
sent to a model.
Control how oversized images loaded by the `read` tool are resized or rejected
before they are sent to a model.
```jsonc
{
@ -278,6 +285,8 @@ sent to a model.
}
```
See the [attachments guide](/attachments) for image processing and limits.
### Tool output
Set the maximum number of lines and bytes retained from a tool result.
@ -309,8 +318,7 @@ be overridden by an individual server.
}
```
See the [MCP guide](https://opencode.ai/docs/mcp-servers/) for remote servers,
OAuth, environment variables, and timeouts.
See the [MCP guide](/mcp) for remote servers, OAuth, environment variables, and timeouts.
### Compaction
@ -328,6 +336,8 @@ Control automatic context compaction and how much recent context it preserves.
}
```
See the [compaction guide](/compaction) for automatic context management.
### Skills
Add directories or URLs that OpenCode should search for agent skills.
@ -338,8 +348,7 @@ Add directories or URLs that OpenCode should search for agent skills.
}
```
See the [skills guide](https://opencode.ai/docs/skills/) for skill structure and
automatic discovery under `.opencode/skills/`.
See the [skills guide](/skills) for skill structure and automatic discovery under `.opencode/skills/`.
### Commands
@ -356,12 +365,12 @@ Define reusable slash commands as named prompt templates.
}
```
See the [commands guide](https://opencode.ai/docs/commands/) for arguments,
models, agents, and file-based commands.
See the [commands guide](/commands) for arguments, models, agents, and file-based commands.
### Instructions
Load additional instruction files, globs, or URLs into the agent's context.
Declare additional instruction files, globs, or URLs. V2 accepts this field,
but does not load these entries yet; use `AGENTS.md` for active instructions.
```jsonc
{
@ -369,8 +378,7 @@ Load additional instruction files, globs, or URLs into the agent's context.
}
```
See the [rules guide](https://opencode.ai/docs/rules/) for project instructions
and `AGENTS.md`.
See the [instructions guide](/instructions) for project instructions and `AGENTS.md`.
### References
@ -392,8 +400,7 @@ context.
}
```
See the [references guide](https://opencode.ai/docs/references/) for shorthand,
visibility, and path resolution.
See the [references guide](/references) for shorthand, visibility, and path resolution.
### Plugins
@ -440,5 +447,4 @@ headers, and model variants.
}
```
See the [providers guide](https://opencode.ai/docs/providers/) for credentials,
custom endpoints, provider packages, and model configuration.
See the [providers guide](/providers) for credentials, custom endpoints, provider packages, and model configuration.

View file

@ -20,7 +20,32 @@
"groups": [
{
"group": "Get started",
"pages": ["index", "config", "plugins", "troubleshooting"]
"pages": ["index", "config", "troubleshooting"]
},
{
"group": "Migrate from V1",
"pages": ["migrate-v1"]
},
{
"group": "Configure",
"pages": [
"models",
"providers",
"agents",
"permissions",
"sharing",
"snapshots",
"commands",
"skills",
"instructions",
"mcp",
"attachments",
"compaction",
"formatters",
"lsp",
"references",
"plugins"
]
}
]
},

View file

@ -0,0 +1,93 @@
---
title: "Formatters"
description: "Configure formatter settings and understand formatter support in OpenCode V2."
---
OpenCode V2 accepts formatter configuration, but it does not yet include a
formatter runtime. File writes and edits are not automatically formatted.
<Warning>
V2 currently has no built-in formatters. The built-in formatter list and
automatic post-edit formatting documented for V1 do not apply to V2.
</Warning>
## Configuration
The `formatter` field accepts a boolean or an object keyed by formatter name:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"formatter": {
"prettier": {
"disabled": false,
"command": ["prettier", "--write", "$FILE"],
"environment": {
"NODE_ENV": "development"
},
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
}
```
This example is valid V2 configuration, but V2 does not currently execute the
command.
Each named formatter entry supports these optional fields:
| Field | Type | Current V2 behavior |
| --- | --- | --- |
| `disabled` | `boolean` | Accepted, but there is no runtime formatter to enable or disable. |
| `command` | `string[]` | Accepted as an argument array, but not executed. |
| `environment` | `Record<string, string>` | Accepts string environment variable names and values, but they are not applied. |
| `extensions` | `string[]` | Accepted without extension-specific validation, but files are not matched against it. |
All entry fields are optional. The schema therefore also accepts an empty entry
such as `"prettier": {}`.
## Enable and disable
The schema accepts all of the following forms:
```jsonc
// Omit `formatter`, or use false, when formatting is not requested.
{
"formatter": false
}
```
```jsonc
// Reserved for enabling all built-ins once a V2 runtime provides them.
{
"formatter": true
}
```
```jsonc
// Configure named entries or mark one as disabled.
{
"formatter": {
"prettier": { "disabled": true },
"custom": {
"command": ["custom-fmt", "$FILE"],
"extensions": [".foo"]
}
}
}
```
At present, omitted, `false`, `true`, and object forms have the same runtime
result: V2 runs no formatter. `disabled` is retained as configuration data but
does not control an executable formatter.
## Commands and placeholders
`command` is an array of strings, not a shell command string. `$FILE` is the V1
file-path placeholder and is often retained in migrated configuration. V2 does
not currently substitute `$FILE` or define another formatter placeholder.
Likewise, V2 does not currently use `extensions` to select commands, merge
`environment` into a child process, discover formatter executables or project
configuration, or run multiple matching formatters. These behaviors will only
be available after a V2 formatter runtime is implemented.

View file

@ -3,6 +3,11 @@ title: "Intro"
description: "Get started with OpenCode."
---
<Warning>
These docs are for the beta version of OpenCode, which will become OpenCode 2.0. The beta is still changing: we may
wipe your data, things may break, and APIs, configuration, and plugin APIs may change.
</Warning>
[**OpenCode**](https://opencode.ai) is an open source AI coding agent. It's available as a terminal-based interface or
desktop app.
@ -70,9 +75,9 @@ Arch Linux installation is not available in beta.
### Windows
<Tip>
For the best experience on Windows, use [Windows Subsystem for Linux
(WSL)](https://opencode.ai/docs/windows-wsl). It provides better performance and full compatibility with OpenCode's
features.
For the best experience on Windows, install [Windows Subsystem for Linux
(WSL)](https://learn.microsoft.com/windows/wsl/install), open your Linux distribution, and use one of the beta package
manager commands above.
</Tip>
<Tabs>
@ -110,7 +115,8 @@ If you'd like easy access to all the best coding models you can try out
You can also try [OpenCode Go](https://opencode.ai/go) a $10/month subscription
plan that grants you access to the best open source models.
See the current [provider directory](https://opencode.ai/docs/providers#directory).
Use `/models` to browse the providers and models available to your project. See [Providers](/providers) for connection and
configuration details.
---
@ -148,8 +154,10 @@ Use `/undo` when a change isn't what you wanted.
/undo
```
OpenCode reverts the changes and restores your original message so you can revise it. Run `/undo` multiple times to undo
multiple changes, or use `/redo` to reapply them.
OpenCode stages a conversation revert and restores your original message so you can revise it. In a Git repository, it
also restores file changes when snapshots were captured successfully. Run `/undo` multiple times to move the conversation
boundary back, or use `/redo` to restore the staged conversation and files. See [Snapshots and undo](/snapshots) for
limitations and safety details.
```text
/redo
@ -160,5 +168,5 @@ multiple changes, or use `/redo` to reapply them.
## Customize
Make OpenCode your own by [picking a theme](https://opencode.ai/docs/themes), [customizing
keybinds](https://opencode.ai/docs/keybinds), [configuring formatters](https://opencode.ai/docs/formatters), [creating
commands](https://opencode.ai/docs/commands), or editing the [OpenCode config](https://opencode.ai/docs/config).
keybinds](https://opencode.ai/docs/keybinds), [configuring formatters](/formatters), [creating commands](/commands), or
editing the [OpenCode config](/config).

View file

@ -0,0 +1,124 @@
---
title: "Instructions"
description: "Give OpenCode global, project, and directory-specific guidance."
---
Instructions are privileged context that guide an agent throughout a session.
V2 combines built-in context, discovered `AGENTS.md` files, and dynamic sources
such as skill, reference, MCP, and session context into a durable instruction
baseline.
## AGENTS.md
Use `AGENTS.md` for persistent guidance such as build commands, architecture,
code conventions, and verification requirements. Commit project files so the
whole team receives the same instructions.
V2 loads:
1. The global file at `$XDG_CONFIG_HOME/opencode/AGENTS.md`, normally
`~/.config/opencode/AGENTS.md`.
2. Every `AGENTS.md` from the current Location up to and including the project
root.
For example, when the Location is `packages/web`, OpenCode can load all three
project files below:
```text
my-project/
├── AGENTS.md
└── packages/
├── AGENTS.md
└── web/
└── AGENTS.md
```
The files are combined rather than selecting a single winner. They are rendered
in this order: global, then project files from the Location toward the project
root. OpenCode does not resolve conflicts between their contents, so keep broad
guidance global and put scoped guidance in the relevant project directory.
If the Location is outside the project root, only the global file is loaded.
Setting `OPENCODE_DISABLE_PROJECT_CONFIG=1` also skips project `AGENTS.md`
discovery but does not disable the global file.
<Note>
Current V2 discovery only recognizes `AGENTS.md`. The `CLAUDE.md` fallback
and related precedence described by older OpenCode documentation do not apply.
</Note>
### Nested instructions
An `AGENTS.md` below the Location is not part of the initial upward scan. When
the read tool successfully reads a file or lists a directory, OpenCode discovers
`AGENTS.md` files from that target upward to, but not including, the Location.
It adds newly discovered files to the session in nearest-first order.
Each nested file is injected once per session and recorded in durable session
history. Reading the same area again does not inject it again. Consequently,
editing an already injected nested `AGENTS.md` does not replace its earlier
session entry automatically; start a new session if the updated text must apply
immediately.
## Config entries
The V2 config schema accepts an `instructions` array of strings:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"instructions": [
"CONTRIBUTING.md",
"docs/guidelines/*.md",
"https://example.com/shared-instructions.md"
]
}
```
Configuration is loaded from global through project-local files. If more than
one config defines `instructions`, the highest-precedence, closest config's
entire array is selected; arrays are not merged.
<Warning>
V2 currently parses and retains this field but does not resolve its entries
into instruction sources. Local files, glob patterns, and HTTP or HTTPS URLs
in `instructions` therefore do not reach the model yet. Use `AGENTS.md` for
active V2 instructions. URL fetching and timeout behavior documented for V1
are not supported by the current V2 implementation.
</Warning>
See [Config](/config) for config locations and general precedence.
## Ordering
The selected agent or provider system prompt is sent first. OpenCode then sends
the session's instruction baseline, composed in this order:
1. Built-in environment and date context.
2. Ambient `AGENTS.md` discovery.
3. Available skill, reference, and MCP guidance.
4. Session-specific instruction entries supplied through the API.
These sources are combined; ordering is not an override mechanism. Nested
`AGENTS.md` files discovered by reads are chronological session entries rather
than part of the baseline.
## Changes
Before each model step, V2 compares live instruction sources with what that
session's model was last told:
- A new or changed ambient `AGENTS.md` aggregate is announced as a system update
that replaces the previous ambient aggregate.
- Removing all ambient files announces that the previous ambient instructions
no longer apply.
- A temporary read or discovery failure preserves the session's last known
instructions instead of treating them as deleted. If no baseline exists yet,
the first model step waits until required sources are available.
- Completed conversation compaction creates a fresh baseline from the current
sources. Moving a session or committing a revert also resets its instruction
checkpoint so the next step establishes a new baseline.
Updates are durable session history. OpenCode does not rewrite the original
baseline on every change; it records the change so subsequent model steps see
both the established baseline and the chronological update.

105
packages/docs/lsp.mdx Normal file
View file

@ -0,0 +1,105 @@
---
title: "LSP"
description: "Configure language servers and understand LSP support in OpenCode V2."
---
Language Server Protocol (LSP) integrations can provide code diagnostics,
symbols, definitions, references, and other language-aware context.
<Warning>
OpenCode V2 does not yet have an LSP runtime or built-in language servers.
The `lsp` configuration is accepted and preserved, but it does not currently
start or download servers, expose an LSP tool, or add diagnostics to file tool
results.
</Warning>
## Built-in servers
There are no built-in LSP servers in the current V2 implementation. Setting
`lsp` to `true` declares that built-ins should be enabled, but has no runtime
effect until V2 provides a server registry and LSP runtime.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"lsp": true
}
```
## Configuration
The `lsp` field accepts a boolean or an object keyed by server name:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"custom-typescript": {
"command": ["typescript-language-server", "--stdio"],
"extensions": [".ts", ".tsx"],
"env": {
"TSS_LOG": "-level verbose"
},
"initialization": {
"preferences": {
"importModuleSpecifierPreference": "relative"
}
}
}
}
}
```
Each enabled server entry has this shape:
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `command` | `string[]` | Yes | Executable followed by any arguments. |
| `extensions` | `string[]` | No | File extensions associated with the server, including the leading dot. |
| `disabled` | `boolean` | No | Disables the entry when `true`. |
| `env` | `Record<string, string>` | No | Environment variables for the server process. The property is named `env`, not `environment`. |
| `initialization` | `Record<string, unknown>` | No | Server-specific options for the LSP `initialize` request. |
The only entry that may omit `command` is the disable-only form:
```jsonc
{
"lsp": {
"typescript": {
"disabled": true
}
}
}
```
Server names are arbitrary. The V2 schema permits `extensions` to be omitted,
including for a custom server, although a future runtime will need a way to
associate that server with files.
## Disable LSP
Omit `lsp` when no configuration is needed. Set it to `false` to explicitly
disable the whole integration, including when a lower-priority configuration
set it to `true` or supplied an object:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"lsp": false
}
```
Use `{ "disabled": true }` under a server name to disable one server while
retaining the object form. `OPENCODE_DISABLE_LSP_DOWNLOAD` is not used by V2;
V2 currently performs no automatic LSP downloads.
## Current usage
V2 loads and validates the configuration shape for compatibility and future
integration. It does not currently use LSP when reading, writing, editing, or
patching files, and those tools do not notify a language server or return LSP
diagnostics.
For reliable feedback today, have the agent run the project's lint, typecheck,
test, or compiler commands. Record those commands in an `AGENTS.md` file or a
skill so the agent knows when and where to run them.

254
packages/docs/mcp.mdx Normal file
View file

@ -0,0 +1,254 @@
---
title: "MCP servers"
description: "Connect local and remote Model Context Protocol servers to OpenCode."
---
OpenCode can connect to [Model Context Protocol](https://modelcontextprotocol.io/) servers and make their tools, prompts, and instructions available to agents. MCP tools consume model context, so enable only the servers you need.
## Configure servers
Define each server by a unique name under `mcp.servers` in your [OpenCode configuration](/config). V2 does not place server names directly under `mcp`.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"my-server": {
"type": "local",
"command": ["npx", "-y", "example-mcp-server"]
}
}
}
}
```
Servers connect automatically unless `disabled` is `true`. There is no V2 `enabled` field.
```jsonc
{
"mcp": {
"servers": {
"my-server": {
"type": "local",
"command": ["npx", "-y", "example-mcp-server"],
"disabled": true
}
}
}
}
```
As with other configuration, a server in a higher-precedence project config replaces a server with the same name from a lower-precedence config. Use different names when you need separate connections or accounts.
## Local servers
A local server is a command that OpenCode starts using the MCP stdio transport.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"everything": {
"type": "local",
"command": [
"npx",
"-y",
"@modelcontextprotocol/server-everything"
],
"cwd": ".",
"environment": {
"LOG_LEVEL": "info",
"MCP_API_KEY": "{env:MCP_API_KEY}"
}
}
}
}
}
```
| Field | Required | Description |
| --- | --- | --- |
| `type` | Yes | Must be `"local"`. |
| `command` | Yes | Executable followed by its arguments. |
| `cwd` | No | Process working directory. Relative paths resolve from the workspace directory; the workspace is the default. |
| `environment` | No | String environment variables added to the inherited OpenCode process environment. |
| `disabled` | No | Set to `true` to prevent the server from connecting. Defaults to `false`. |
| `timeout` | No | Per-server timeout overrides. |
Use `{env:NAME}` to substitute an environment variable while loading config. Shell expressions such as `$NAME` are not expanded in JSON strings.
## Remote servers
A remote server uses the MCP Streamable HTTP transport. Its `url` must be a valid absolute URL.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp",
"oauth": false,
"headers": {
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
}
}
}
}
}
```
| Field | Required | Description |
| --- | --- | --- |
| `type` | Yes | Must be `"remote"`. |
| `url` | Yes | Streamable HTTP endpoint. |
| `headers` | No | String HTTP headers sent to the MCP endpoint. |
| `oauth` | No | OAuth client settings, or `false` to disable OAuth support. |
| `disabled` | No | Set to `true` to prevent the server from connecting. Defaults to `false`. |
| `timeout` | No | Per-server timeout overrides. |
Use `oauth: false` for a server that exclusively uses an API key or another header-based credential.
## OAuth
OAuth support is enabled for remote servers unless `oauth` is `false`. OpenCode discovers the authorization server, uses PKCE, refreshes tokens, and attempts dynamic client registration when the server supports it. OAuth credentials are stored outside project configuration.
For a server that supports dynamic client registration, only the remote server is required:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"sentry": {
"type": "remote",
"url": "https://mcp.sentry.dev/mcp"
}
}
}
}
```
When the server reports `needs authentication`, start the browser authorization flow:
```bash
opencode2 mcp auth sentry
```
The command prints the authorization URL and waits for the redirect to OpenCode's loopback callback server.
If the provider issued client credentials, configure them using V2's snake_case field names:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"company-tools": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"oauth": {
"client_id": "{env:MCP_CLIENT_ID}",
"client_secret": "{env:MCP_CLIENT_SECRET}",
"scope": "tools:read tools:execute",
"callback_port": 19876,
"redirect_uri": "http://127.0.0.1:19876/callback"
}
}
}
}
}
```
| OAuth field | Description |
| --- | --- |
| `client_id` | Pre-registered OAuth client ID. If omitted, OpenCode attempts dynamic client registration. |
| `client_secret` | Client secret for a pre-registered client. |
| `scope` | Space-delimited scopes to request. |
| `callback_port` | Local callback port, from `1` through `65535`. An available ephemeral port is used by default. |
| `redirect_uri` | Pre-registered loopback redirect URI. Its path and port must reach the local callback listener. |
Remove stored credentials with:
```bash
opencode2 mcp logout sentry
```
## Timeouts
Timeouts are positive integer milliseconds. Configure defaults under `mcp.timeout`; a server's `timeout` fields override matching defaults.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"timeout": {
"startup": 45000,
"catalog": 30000,
"execution": 600000
},
"servers": {
"slow-tools": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"timeout": {
"catalog": 60000
}
}
}
}
}
```
| Timeout | Default | Applies to |
| --- | --- | --- |
| `startup` | 30 seconds | Establishing the transport and initializing the server. |
| `catalog` | 30 seconds | Listing tools, prompts, resources, and resource templates. |
| `execution` | 12 hours | Calling tools, getting prompts, and reading resources. |
## Names and permissions
OpenCode combines the server name and MCP tool name as `<server>_<tool>`. Characters other than letters, numbers, `_`, and `-` are replaced with `_`; for example, server `context 7` and tool `resolve.library/id` become `context_7_resolve_library_id`. MCP prompts appear as slash commands named `<server>:<prompt>` using the same normalization.
Choose short server names that remain unique after normalization. Under the default Code Mode, MCP tools are grouped by the normalized server name.
Use permission actions to hide or deny a server's tools without stopping its connection:
```jsonc
{
"permissions": [
{
"action": "context7_*",
"resource": "*",
"effect": "deny"
}
]
}
```
## CLI commands
V2 provides these MCP management commands:
```bash
# Add a local server to the project config
opencode2 mcp add everything --env LOG_LEVEL=info -- npx -y @modelcontextprotocol/server-everything
# Add a remote server to the project config
opencode2 mcp add context7 --url https://mcp.context7.com/mcp --header 'CONTEXT7_API_KEY={env:CONTEXT7_API_KEY}'
# Add to the global config instead
opencode2 mcp add context7 --global --url https://mcp.context7.com/mcp
# List configured servers and connection status
opencode2 mcp list
# Authenticate or remove OAuth credentials
opencode2 mcp auth context7
opencode2 mcp logout context7
```
`mcp add` accepts either `--url` for a remote server or a command after `--` for a local server, not both. Use `--header NAME=VALUE` only with remote servers and `--env NAME=VALUE` only with local servers. Edit the config directly for OAuth, timeout, working-directory, or enablement settings.

View file

@ -0,0 +1,89 @@
---
title: "Overview"
description: "Move from OpenCode V1 to the OpenCode 2.0 beta."
---
<Warning>
OpenCode 2.0 is in beta. Back up important configuration and data before migrating. Beta data may be wiped, features may
break, and configuration and plugin APIs may change.
</Warning>
During the beta, OpenCode V1 and V2 use different executable names. You can keep using `opencode` for V1 while trying V2
with `opencode2`.
## Install the beta
Install the beta from the `next` distribution tag:
```bash
npm install -g @opencode-ai/cli@next
```
Start it in your project with:
```bash
opencode2
```
## Back up your configuration
Before making changes, back up your global and project configuration files:
```text
~/.config/opencode/opencode.json(c)
<project>/opencode.json(c)
<project>/.opencode/opencode.json(c)
```
V2 reads these same locations. It detects V1-shaped configuration and translates supported fields in memory without
rewriting the source file. Keep shared files in their V1 shape while evaluating both versions; V1 does not understand the
native plural V2 fields and may silently ignore them.
## Update configuration
Automatic translation is a compatibility aid, not a guarantee that every V1 setting behaves identically. Convert your
configuration to the native V2 shape only when you no longer need V1 to read those same files.
<Note>
Do not mix V1 and V2 field names in one file. Back up the V1 file, update the complete working file to the V2 shape, and
validate it against the current schema. Restore the V1 copy before using V1 again.
</Note>
The main field changes are:
| V1 | V2 |
| --- | --- |
| `permission` | `permissions` |
| `agent` and `mode` | `agents` |
| `snapshot` | `snapshots` |
| `attachment` | `attachments` |
| `command` | `commands` |
| `reference` | `references` |
| `plugin` | `plugins` |
| `provider` | `providers` |
| Servers directly under `mcp` | Servers under `mcp.servers` |
| `skills.paths` and `skills.urls` | A single `skills` array |
Permission actions also changed: `bash` is now `shell`, `task` is now `subagent`, and `write` and `patch` are now `edit`.
See [Permissions](/permissions) for the ordered V2 rule format.
V1-only fields including `logLevel`, `server`, `layout`, `disabled_providers`, `enabled_providers`, and `small_model` are
not carried into the native V2 configuration. Remove them and use the current [Config](/config) and
[Providers](/providers) guides to configure their replacements where applicable.
## Review extensions
V2 continues to discover file-based agents, commands, and skills from OpenCode configuration directories. Their schemas
have changed, so review each extension against the current [Agents](/agents), [Commands](/commands), and
[Skills](/skills) guides.
<Warning>
V1 plugins are not guaranteed to work with V2. The plugin API is changing during beta; review and port each plugin using
the [V2 plugin guide](/plugins).
</Warning>
## Verify your setup
Start `opencode2` in a project and verify your model, provider credentials, agents, permissions, MCP servers, and plugins
before relying on the beta for regular work. Keep your V1 setup and backups until you have confirmed the V2 behavior you
need, and do not point V1 at configuration that you have converted to the native V2 shape.

223
packages/docs/models.mdx Normal file
View file

@ -0,0 +1,223 @@
---
title: "Models"
description: "Select, configure, and customize models in OpenCode 2.0."
---
OpenCode builds its model catalog from [Models.dev](https://models.dev), provider integrations, and your configuration.
Only enabled models whose provider is available for the current project appear in the model picker.
Connect a provider with `/connect` in the TUI, or configure a provider and its credentials in `opencode.json`.
## Select a model
Open the model picker with `/models` or the default `<leader>m` keybind. Use `/variants` to choose a variant for the
current model, or press `ctrl+t` to cycle through its variants.
A model reference has this form:
```text
provider/model#variant
```
The variant is optional. OpenCode splits at the first `/`, so model IDs may contain additional slashes:
```text
openai/gpt-5.2
openai/gpt-5.2#high
openrouter/anthropic/claude-sonnet-4.5#high
```
Use the catalog IDs shown by `/models`, not a provider's display name. Omit `#variant` to use the model's base settings.
### Command line
Select a model for a non-interactive run with `--model` or `-m`:
```bash
opencode2 run --model openai/gpt-5.2 "Explain this repository"
opencode2 run -m openai/gpt-5.2#high "Review the current changes"
```
<Note>
`opencode2 run` accepts `provider/model#variant`. The current `opencode2 mini --model` option accepts only
`provider/model`; choose its variant from the interactive interface.
</Note>
## Set the default
Set `model` in `opencode.json` or `opencode.jsonc`:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-5"
}
```
The explicit object form is equivalent:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": {
"providerID": "openrouter",
"model": "anthropic/claude-sonnet-4.5"
}
}
```
Root, agent, and command `model` fields accept these same selection forms. See [Config](/config) for configuration
locations and precedence.
The configured model becomes the catalog default when its provider is available and the model is enabled. Otherwise,
session execution falls back to the newest available supported model. An explicit model already selected on a session
takes precedence over the default; switching models changes that session and does not rewrite your config.
## Variants
Variants are named request overlays for one model, commonly used for reasoning effort or token budgets. Available names
are model-specific and are derived from current catalog metadata. Do not assume that names such as `low`, `high`, or
`max` exist for every model; `/variants` shows the valid choices.
Add a variant, or override a catalog variant with the same ID, under the model's `variants` array:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"openai": {
"models": {
"gpt-5.2": {
"settings": {
"reasoningEffort": "medium"
},
"variants": [
{
"id": "fast",
"settings": {
"reasoningEffort": "low"
}
},
{
"id": "deep",
"settings": {
"reasoningEffort": "high",
"reasoningSummary": "auto"
}
}
]
}
}
}
},
"commands": {
"deep-review": {
"description": "Review with high reasoning effort",
"template": "Review the current changes for correctness and missing tests.",
"model": "openai/gpt-5.2#deep"
}
}
}
```
Variant entries support `settings`, `headers`, and `body`. Selecting one deeply overlays its values on the effective
provider and model configuration. An unknown variant fails model resolution instead of silently using the base model.
<Warning>
V2 uses `providers` (plural) and an array of `{ "id": "..." }` variant entries. The V1 `provider` key and
object-shaped `variants` configuration are not the V2 format.
</Warning>
## Configure a model
Provider and model entries can supply three kinds of request configuration:
- `settings` contains provider-package options such as `baseURL`, `reasoningEffort`, or `thinkingConfig`.
- `headers` adds HTTP request headers.
- `body` adds provider-specific fields to the request body.
These values are provider-specific JSON. OpenCode applies provider values first, then model values, then the selected
variant. Nested `settings` and `body` objects are merged; later array and scalar values replace earlier values. Header
names are matched case-insensitively.
You can also map a friendly catalog ID to a different API model ID with `modelID`:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": "openai/coding-default",
"providers": {
"openai": {
"models": {
"coding-default": {
"modelID": "gpt-5.2",
"name": "Coding default",
"capabilities": {
"tools": true,
"input": ["text", "image"],
"output": ["text"]
},
"limit": {
"context": 200000,
"output": 32000
}
}
}
}
}
}
```
Here `openai/coding-default` is the selectable catalog reference, while `gpt-5.2` is sent to the provider. When adding a
model that is not already in the catalog, set accurate `capabilities` and `limit` values so OpenCode can expose tools and
enforce the correct context limits. Set `disabled: true` on a model entry to hide it from the available catalog.
## Local and compatible models
For an OpenAI-compatible server, define a provider package, endpoint, and at least one model:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": "local/coder",
"providers": {
"local": {
"name": "Local server",
"package": "aisdk:@ai-sdk/openai-compatible",
"settings": {
"baseURL": "http://127.0.0.1:1234/v1"
},
"models": {
"coder": {
"modelID": "model-name-on-server",
"capabilities": {
"tools": true,
"input": ["text"],
"output": ["text"]
},
"limit": {
"context": 32768,
"output": 8192
}
}
}
}
}
}
```
Use the server's real model name, limits, modalities, and tool support. OpenCode cannot infer these for a model you add
manually. If the endpoint requires a key, add `apiKey` to provider `settings` using an environment substitution such as
`"apiKey": "{env:LOCAL_API_KEY}"`; do not commit secrets.
## Caveats
- Provider and model IDs are case-sensitive. Provider IDs cannot contain `/` or `#`; model IDs cannot contain `#`.
- The selector object uses `model`, while a provider catalog entry uses `modelID` for the upstream API identifier.
- The root `model` currently sets the default provider and model only. Although its selection shape accepts a variant,
the V2 catalog default does not retain it; select a variant in the TUI, with `opencode2 run`, or on an agent or command.
- Model options are provider-specific. A setting accepted by one provider package may be ignored or rejected by another.
- Catalog data, credentials, and config are location-scoped. A model available in one project may be unavailable in
another.
- Configuration files are watched and normally reload automatically, but an in-flight model request keeps the settings
with which it started.

View file

@ -0,0 +1,209 @@
---
title: "Permissions"
description: "Configure ordered V2 rules for tool access, approvals, and agent overrides."
---
Permissions control whether an agent may perform an action on a resource. V2
configuration uses the plural `permissions` field and an ordered array of rules.
<Warning>
The V1 object syntax uses different field and action names. Do not use
`permission`, `bash`, or `task` in V2 configuration; use `permissions`,
`shell`, and `subagent`.
</Warning>
## Rule schema
Each rule has three required string fields:
```jsonc
{
"$schema": "https://opencode.ai/config.json",
"permissions": [
{ "action": "*", "resource": "*", "effect": "ask" },
{ "action": "read", "resource": "*", "effect": "allow" },
{ "action": "read", "resource": "*.env", "effect": "deny" },
{ "action": "shell", "resource": "git status *", "effect": "allow" },
{ "action": "shell", "resource": "git push *", "effect": "deny" },
{ "action": "edit", "resource": "packages/docs/*.mdx", "effect": "allow" }
]
}
```
- `action` matches a tool permission action.
- `resource` matches the value the tool is trying to use, such as a path,
command, URL, query, or agent ID.
- `effect` is `"allow"`, `"deny"`, or `"ask"`.
`allow` proceeds without prompting, `deny` blocks the operation, and `ask`
waits for a user decision. If no rule matches, the result is `ask`.
## Matching and order
Both `action` and `resource` support simple wildcards:
- `*` matches zero or more characters, including `/`.
- `?` matches exactly one character.
- All other characters are literal.
Matches cover the entire value. Slashes are normalized, and matching is
case-insensitive on Windows. For shell convenience, a pattern ending in
`" *"` also matches the command without arguments: `"git status *"` matches
both `git status` and `git status --short`.
The **last matching rule wins**. Put broad rules first and exceptions later.
Rules from lower-priority configuration files are loaded first. OpenCode then
appends all global rules before agent-specific rules, so a matching agent rule
overrides a global rule.
Some operations check several resources at once, such as a patch touching
multiple files. OpenCode denies the operation if any resource resolves to
`deny`; otherwise it asks if any resolves to `ask`; otherwise it allows it.
## Actions and resources
V2 action names are strings, so plugins may introduce additional actions. The
current built-in actions use these resources:
| Action | Resource matched |
| --- | --- |
| `read` | Location-relative path for an internal file or directory; canonical absolute path for an external target |
| `edit` | Target path for `edit`, `write`, and `patch`; all three tools share this action |
| `glob` | The requested glob pattern |
| `grep` | The requested regular expression, not the search path |
| `shell` | The complete raw shell command string |
| `subagent` | The target agent ID |
| `skill` | The skill ID |
| `question` | `*` |
| `webfetch` | The requested URL |
| `websearch` | The search query |
| `external_directory` | A canonical external directory boundary, normally ending in `/*` |
| `<server>_<tool>` | `*` for an MCP tool; unsupported characters in both names become `_` |
| `execute` | `*`; controls availability of the Code Mode dispatcher, while each nested tool still enforces its own permission |
Built-in agent policy also reserves `plan_enter` and `plan_exit` for plan-mode
transitions. `doom_loop` and `lsp` are not current V2 Core permission actions.
## External directories
An external path requires a separate `external_directory` decision before the
tool's own `read` or `edit` decision. This applies to external paths used by
`read`, `edit`, `write`, and `patch`, and to an external `shell` working
directory.
```jsonc
{
"$schema": "https://opencode.ai/config.json",
"permissions": [
{
"action": "external_directory",
"resource": "~/projects/reference/*",
"effect": "allow"
},
{
"action": "read",
"resource": "~/projects/reference/*",
"effect": "allow"
},
{
"action": "edit",
"resource": "~/projects/reference/*",
"effect": "deny"
}
]
}
```
For `external_directory`, `read`, and `edit` resources, a leading `~`, `~/`,
`$HOME`, or `$HOME/` is expanded when configuration loads. Shell resources are
raw command text and are **not** home-expanded.
<Warning>
`shell` runs with the host user's filesystem, process, and network authority.
Its resource is raw text, not a parsed command. External command arguments
produce only best-effort warnings; `external_directory` is enforced for the
working directory, not every path embedded in a command. Prefer a narrow
shell allowlist over patterns intended to identify every dangerous command.
</Warning>
Relative mutation paths cannot escape the active Location, and symlink escapes
from inside it are rejected. Explicit external paths are canonicalized before
matching, so authorize only trusted directory boundaries.
## Defaults
The evaluator's fallback is `ask`, but shipped agents include ordered defaults:
| Agent | Effective default policy |
| --- | --- |
| `build` | Allows most actions; asks for external directories and `.env` reads; allows questions and entering plan mode; denies exiting plan mode |
| `plan` | Uses the same base, allows questions and exiting plan mode, and denies edits except OpenCode plan files |
| `general` | Uses the base policy but cannot launch another subagent; questions and plan transitions remain denied |
| `explore` | Denies everything except `read`, `glob`, `grep`, `webfetch`, and `websearch`; cannot launch subagents and asks for external directories |
| Hidden maintenance agents | Deny all actions |
The base read rules are ordered as follows:
```jsonc
[
{ "action": "read", "resource": "*", "effect": "allow" },
{ "action": "read", "resource": "*.env", "effect": "ask" },
{ "action": "read", "resource": "*.env.*", "effect": "ask" },
{ "action": "read", "resource": "*.env.example", "effect": "allow" }
]
```
OpenCode also permits its managed tool-output and temporary directories where
needed. These exceptions do not grant general external-directory access.
## Agent overrides
Configure shared policy at the top level and append narrower rules to a named
agent under `agents.<id>.permissions`:
```jsonc
{
"$schema": "https://opencode.ai/config.json",
"permissions": [
{ "action": "shell", "resource": "*", "effect": "ask" },
{ "action": "shell", "resource": "git diff *", "effect": "allow" },
{ "action": "shell", "resource": "git status *", "effect": "allow" }
],
"agents": {
"reviewer": {
"description": "Review code without changing it",
"mode": "subagent",
"permissions": [
{ "action": "edit", "resource": "*", "effect": "deny" },
{ "action": "shell", "resource": "git diff *", "effect": "allow" },
{ "action": "shell", "resource": "git status *", "effect": "allow" }
]
}
}
}
```
Agent rules do not replace the global array; they are appended after it. A
custom subagent executes with its own permissions, not a permission subset
derived from the parent agent.
## Approval choices
When an `ask` rule matches, clients can reply with:
- **Allow once** (`once`): approve only the pending request.
- **Allow always** (`always`): approve this request and save the patterns
proposed by the tool for the current project.
- **Reject** (`reject`): reject the request. Rejecting also rejects other
pending permission requests in the same session; clients may attach feedback.
Saved approvals are durable and project-scoped. They are additional `allow`
rules, but they can never override a configured `deny`. The proposed saved
pattern may be broader than the displayed resource: several tools propose `*`,
shell proposes the exact command text, and skills and subagents propose their
IDs. Review the confirmation carefully and remove saved approvals that are no
longer needed.
For non-interactive runs, `opencode2 run --auto` replies `once` to permission
requests. It does not save approvals, and explicit `deny` rules remain enforced.
Without `--auto`, a non-interactive run rejects permission requests.

View file

@ -2,3 +2,368 @@
title: "Plugins"
description: "Extend OpenCode with plugins."
---
Plugins extend OpenCode in-process. They can transform agents, models, commands,
integrations, references, skills, and tools; intercept model requests and tool
execution; and call a location-scoped subset of the V2 client.
<Warning>
The V2 plugin API is beta. Entrypoints, hooks, draft shapes, and configuration
may change before the stable release. Use only the `/v2` exports described on
this page; the root `@opencode-ai/plugin` API is the legacy API.
</Warning>
## Load plugins
Plugins can be loaded from npm packages, explicit local paths, or config
directories. Each module must have one default export containing a unique
plugin `id` and either a Promise `setup` function or an Effect `effect`
function.
### Configuration
Add ordered entries to the plural `plugins` field in `opencode.json(c)`:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"plugins": [
"opencode-acme-plugin@1.2.0",
"@acme/opencode-plugin",
"./plugins/local.ts",
{
"package": "./plugins/reviewer.ts",
"options": {
"agent": "reviewer",
"strict": true
}
}
]
}
```
A string is either a package specifier or a local path. Local paths must start
with `./` or `../` and resolve relative to the configuration file containing
the entry. Absolute paths and `file://` URLs are also supported. Both scoped
packages and versioned package specifiers are supported.
Use the object form to pass JSON configuration to the plugin. OpenCode passes
`options` unchanged as `ctx.options`; omitted options become an empty object.
The plugin owns validation and defaults for its options.
See [Config](/config#locations) for configuration locations and precedence.
Entries from all applicable files are processed from lowest to highest
precedence rather than replacing the entire array.
### Local discovery
OpenCode automatically scans both of these directories in every discovered
OpenCode config directory:
```text
.opencode/plugin/
.opencode/plugins/
```
The equivalent global directories are
`~/.config/opencode/plugin/` and `~/.config/opencode/plugins/`. Direct `.ts` and
`.js` children are loaded. An immediate child directory is also loaded as a
package when OpenCode can resolve a string `exports`, `module`, or `main`
entrypoint, or an `index.ts` or `index.js` file.
A `plugin/` directory beside a project-root `opencode.json` is not discovered
automatically. Put it under `.opencode/`, or add its file explicitly with a
relative config entry.
### Enable and disable
A string beginning with `-` removes a previously selected target. `*` matches
everything, and a suffix of `.*` matches an ID or target prefix. Directives are
applied in order:
```jsonc title="opencode.jsonc"
{
"plugins": [
"-opencode.provider.*",
"opencode.provider.openai",
"-./plugin/old.ts",
"-*",
"./plugins/only-this-one.ts"
]
}
```
Use the same package specifier or resolved local target to remove an external
plugin. Built-in and embedded plugins can be selected by their plugin ID.
Explicit config directives run after local auto-discovery, so they can disable
discovered plugins.
User plugins are activated in configured order between OpenCode's internal
plugin phases. Hooks run sequentially in registration order, and later hooks
observe earlier mutations. Do not depend on the internal phase ordering while
the API is beta.
### Installation and dependencies
OpenCode installs bare package entries and their production dependencies into
an isolated cache. Package installation does not run lifecycle scripts.
Published packages should expose their plugin entrypoint and include every
runtime import in `dependencies`.
Local files and local package directories are imported directly. OpenCode does
**not** install their dependencies. Install dependencies in a `package.json`
visible from the plugin file, for example:
```sh
cd .opencode
bun add @opencode-ai/plugin@1.17.15 effect@4.0.0-beta.83
```
`effect` is required for Effect plugins and for the `Schema` values used by
typed tools. A Promise plugin that does not define tools may only need
`@opencode-ai/plugin`. Match these versions to the OpenCode release you target.
Configuration and discovered plugin files under watched config directories are
reloaded when they change. Reloading replaces the active plugin generation and
releases its scoped registrations. Restart OpenCode after changing an npm
package version or a local dependency when no watched file changed.
## Create a plugin
The Promise API is the simplest option. Export the result of `Plugin.define`
as the module default:
```ts title=".opencode/plugins/reviewer.ts"
import { Plugin } from "@opencode-ai/plugin/v2"
export default Plugin.define({
id: "acme.reviewer",
setup: async (ctx) => {
const description =
typeof ctx.options.description === "string"
? ctx.options.description
: "Reviews code for regressions"
await ctx.agent.transform((agents) => {
agents.update("reviewer", (agent) => {
agent.description = description
agent.mode = "subagent"
})
})
},
})
```
`setup` runs each time the plugin is activated for a Location. Register
long-lived behavior during setup; do not wait there on an infinite event
stream.
### Effect plugins
Use the Effect entrypoint when the implementation benefits from Effect
composition, fibers, or scoped resources:
```ts title=".opencode/plugins/reviewer-effect.ts"
import { Plugin } from "@opencode-ai/plugin/v2/effect"
import { Effect } from "effect"
export default Plugin.define({
id: "acme.reviewer-effect",
effect: (ctx) =>
Effect.gen(function* () {
yield* ctx.agent.transform((agents) => {
agents.update("reviewer", (agent) => {
agent.description = "Reviews code for regressions"
agent.mode = "subagent"
})
})
}),
})
```
The plugin effect is scoped. Finalizers, scoped fibers, and registrations are
released when the plugin reloads or unloads. OpenCode deliberately isolates the
effect from its private Core services; use only the public `ctx` capabilities.
## Context
Promise methods return Promises; the equivalent Effect methods return
`Effect`. Read and action methods use the same inputs and location-aware
responses as the V2 client APIs.
| Capability | Available operations |
| --- | --- |
| `ctx.agent` | `list`, `transform`, `reload` |
| `ctx.catalog.provider` | `list`, `get` |
| `ctx.catalog.model` | `list`, `default` |
| `ctx.catalog` | `transform`, `reload` |
| `ctx.command` | `list`, `transform`, `reload` |
| `ctx.integration` | `list`, `get`, `connect`, `attempt`, `transform`, `reload`, and connection lookup/resolution |
| `ctx.plugin` | `list` currently active plugin IDs |
| `ctx.reference` | `list`, `transform`, `reload` |
| `ctx.session` | `create`, `get`, `prompt`, `command`, `interrupt`, and `hook` |
| `ctx.skill` | `list`, `transform`, `reload` |
| `ctx.tool` | `transform` and `hook` |
| `ctx.aisdk` | `hook` |
| `ctx.event` | `subscribe` to the current public server event stream |
| `ctx.options` | Readonly options from the matching config object |
Unlike the legacy API, V2 does not provide `$`, `directory`, `worktree`, or a
general SDK client on the context. A plugin is Location-scoped, and the exposed
domain clients apply that Location by default.
### Transform hooks
Transforms synchronously edit a draft whenever a stateful domain is built.
Registering or disposing a transform rebuilds the domain from fresh state and
runs all active transforms in order. Call the domain's `reload()` method when
external data captured by a transform changes.
| Transform | Draft operations |
| --- | --- |
| `agent.transform` | `list`, `get`, `default`, `update`, `remove` |
| `catalog.transform` | Provider `list`, `get`, `update`, `remove`; model `get`, `update`, `remove`; default model `get`, `set` |
| `command.transform` | `list`, `get`, `update`, `remove` |
| `integration.transform` | Integration `list`, `get`, `update`, `remove`; method `list`, `update`, `remove` |
| `reference.transform` | `add`, `remove`, `list` |
| `skill.transform` | `source`, `list` |
| `tool.transform` | `add` |
Hook registrations are owned by the plugin scope. Transform and runtime hook
calls also return a `Registration` with `dispose` for explicit cleanup. Tool
contributions currently remain until the owning plugin scope closes, so prefer
scope cleanup for plugin-wide teardown while this API is beta.
### Runtime hooks
Runtime hooks intercept live operations. Their event objects expose specific
mutable fields:
| Hook | Mutable fields |
| --- | --- |
| `ctx.aisdk.hook("sdk", callback)` | `sdk`, after inspecting `model`, `package`, and `options` |
| `ctx.aisdk.hook("language", callback)` | `language`, after inspecting `model`, `sdk`, and `options` |
| `ctx.session.hook("request", callback)` | `system`, `messages`, and the `tools` record immediately before model dispatch |
| `ctx.tool.hook("execute.before", callback)` | `input`, before the selected tool executes |
| `ctx.tool.hook("execute.after", callback)` | `result`, `output`, and `outputPaths`, after execution settles |
For example, remove a tool from selected model requests and normalize another
tool's input:
```ts title=".opencode/plugins/guards.ts"
import { Plugin } from "@opencode-ai/plugin/v2"
export default Plugin.define({
id: "acme.guards",
setup: async (ctx) => {
await ctx.session.hook("request", (event) => {
delete event.tools.write
})
await ctx.tool.hook("execute.before", (event) => {
if (event.tool !== "lookup" || typeof event.input !== "object" || event.input === null) return
event.input = { ...event.input, source: "plugin" }
})
},
})
```
A hook failure fails the operation it intercepts. Keep runtime hooks fast and
handle expected errors inside the callback.
## Add a tool
Use `Tool.make` with Effect schemas. Promise tools use async executors:
```ts title=".opencode/plugins/greeting.ts"
import { Plugin } from "@opencode-ai/plugin/v2"
import { Tool } from "@opencode-ai/plugin/v2/tool"
import { Schema } from "effect"
const greeting = Tool.make({
description: "Create a greeting",
input: Schema.Struct({ name: Schema.String }),
output: Schema.String,
execute: async ({ name }) => `Hello, ${name}!`,
})
export default Plugin.define({
id: "acme.greeting",
setup: async (ctx) => {
await ctx.tool.transform((tools) => {
tools.add("greeting", greeting)
})
},
})
```
Unsupported characters in tool and group names are normalized to underscores.
The resulting exposed key must begin with a letter and contain at most 64
letters, digits, underscores, or hyphens. `tools.add` also accepts
`{ group, deferred }`:
- `group` prefixes and groups the exposed tool name.
- `deferred: true` makes the tool available through the deferred `execute`
tool instead of exposing it directly.
The executor receives a second context argument containing `sessionID`,
`agent`, `assistantMessageID`, and `toolCallID`. Use
`Tool.withPermission(tool, "permission-name")` to assign a permission key.
Effect plugins import the helper from `@opencode-ai/plugin/v2/effect/tool` and
return an `Effect` from `execute`.
## Types
`Plugin.define` infers the context and callbacks. The Promise root also
re-exports the canonical `Agent`, `Command`, `Connection`, `Credential`,
`Integration`, `Model`, `Provider`, `Reference`, and `Skill` schema namespaces.
Import narrower API types from their public subpaths when needed:
```ts
import { Plugin, Model } from "@opencode-ai/plugin/v2"
import type { Context } from "@opencode-ai/plugin/v2/plugin"
import type { AgentDraft } from "@opencode-ai/plugin/v2/agent"
import type { ToolExecuteBeforeEvent } from "@opencode-ai/plugin/v2/tool"
```
Effect equivalents live below `@opencode-ai/plugin/v2/effect`, such as
`@opencode-ai/plugin/v2/effect/plugin` and
`@opencode-ai/plugin/v2/effect/tool`. Avoid importing types or runtime values
from `@opencode-ai/core` or `@opencode-ai/server`; those are private host
implementation details.
## Publish a package
A package plugin uses the same default export as a local plugin. A minimal
manifest is:
```json title="package.json"
{
"name": "opencode-acme-plugin",
"version": "1.0.0",
"type": "module",
"exports": "./src/index.ts",
"dependencies": {
"@opencode-ai/plugin": "1.17.15",
"effect": "4.0.0-beta.83"
}
}
```
Use versions compatible with the OpenCode release you target and test the
installed package, not only a workspace-linked copy. Because the plugin API is
beta, publish compatible plugin updates when V2 entrypoints or contracts
change.
## Verify loading
List active plugin IDs for the current Location through the V2 API:
```sh
opencode2 api get /api/plugin
```
If a plugin is absent, check the server log described in
[Troubleshooting](/troubleshooting#read-logs). Invalid modules and setup failures are
logged; one failing package does not prevent unrelated valid packages from
being resolved.

272
packages/docs/providers.mdx Normal file
View file

@ -0,0 +1,272 @@
---
title: "Providers"
description: "Connect LLM providers and configure endpoints, packages, models, and variants."
---
OpenCode builds its provider and model catalog from [Models.dev](https://models.dev), then applies the `providers`
overlays from your [configuration](/config). A provider needs both a usable runtime package and, when required, an
active connection.
## Connect a provider
Run `/connect` in the TUI, choose an integration, and complete one of the methods it offers:
```text
/connect
```
An integration may support an API key, OAuth, environment variables, or a combination of them. API keys and OAuth
tokens entered through `/connect` are stored by the OpenCode service in its database. Run `/connect` again to replace
or remove a stored credential.
Providers from Models.dev also declare their standard environment variables. A non-empty declared variable is exposed
as an environment connection automatically, so common providers usually need no config:
```bash
export ANTHROPIC_API_KEY="your-key"
```
For a custom provider, `env` declares the variables that can supply its key:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"acme": {
"env": ["ACME_API_KEY"]
}
}
}
```
When several credential sources exist, OpenCode uses the stored credential first, then the first non-empty variable in
`env`, then `settings.apiKey`. Use config substitution instead of committing a literal key:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"acme": {
"settings": {
"apiKey": "{env:ACME_API_KEY}"
}
}
}
}
```
<Warning>Do not commit API keys or authorization headers to your repository.</Warning>
## Configure providers
The `providers` object is keyed by provider ID. Each provider accepts these fields:
| Field | Purpose |
| --- | --- |
| `name` | Display name. |
| `env` | Ordered environment variable names that provide a connection. |
| `package` | Runtime provider package. |
| `settings` | JSON settings passed to the runtime package, such as `baseURL`. |
| `headers` | String-valued HTTP headers added to requests. |
| `body` | JSON fields merged into request bodies. |
| `models` | Models to add or override, keyed by catalog model ID. |
Configuration files are applied from lowest to highest precedence. `settings` and `body` are deep-merged. Headers are
merged case-insensitively. At request time, provider values are inherited by the model, model values override them, and
the selected variant is applied last.
### Custom endpoint
Override `settings.baseURL` to send an existing provider through a proxy or compatible endpoint. Its existing package,
models, and connection continue to apply:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"anthropic": {
"settings": {
"baseURL": "https://llm-proxy.example.com/anthropic"
}
}
}
}
```
`settings` is package-specific. A field only has an effect when the selected package supports it.
### Custom headers and body
Headers and body fields can be set at provider, model, or variant scope:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"openai": {
"headers": {
"X-Gateway-Tenant": "engineering"
},
"body": {
"metadata": {
"application": "opencode"
}
},
"models": {
"gpt-5.2": {
"headers": {
"X-Model-Policy": "coding"
}
}
}
}
}
}
```
These are request overlays, not a generic authentication scheme. Prefer `/connect`, `env`, or `settings.apiKey` for
provider credentials unless the endpoint explicitly requires a custom header.
## Custom providers and packages
For an OpenAI-compatible service, use the V2 native compatible package. The model map is explicit because a custom
provider has no Models.dev catalog entries:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": "acme/qwen3-coder",
"providers": {
"acme": {
"name": "Acme Gateway",
"env": ["ACME_API_KEY"],
"package": "@opencode-ai/llm/providers/openai-compatible",
"settings": {
"baseURL": "https://llm.acme.example/v1"
},
"models": {
"qwen3-coder": {
"name": "Qwen 3 Coder",
"capabilities": {
"tools": true,
"input": ["text"],
"output": ["text"]
},
"limit": {
"context": 131072,
"output": 32768
}
}
}
}
}
}
```
Omit `env` for an endpoint that does not require authentication. The native compatible package requires
`settings.baseURL` and uses bearer authentication when a key is available.
The `package` field supports two runtime contracts:
| Form | Contract |
| --- | --- |
| `"@opencode-ai/llm/providers/openai-compatible"` | A V2 native package exporting `model(modelID, settings)`. An npm specifier or absolute `file://` URL may use the same contract. |
| `"aisdk:@ai-sdk/openai-compatible"` | An AI SDK provider package. The `aisdk:` prefix is required. |
Native packages receive the merged `settings` plus the resolved `apiKey`, `headers`, `body`, and `limits`. AI SDK
packages receive their merged provider options. Use a package's own documentation for accepted settings; OpenCode does
not validate package-specific keys.
`package` may also be set on one model to override the provider package for that model.
## Models
`models` adds a model or overlays an existing catalog model. The object key is the model ID used in OpenCode. Set
`modelID` when the upstream API expects a different ID:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"model": "openai/coding",
"providers": {
"openai": {
"models": {
"coding": {
"modelID": "gpt-5.2",
"name": "GPT-5.2 Coding",
"family": "gpt-5",
"limit": {
"context": 200000,
"input": 180000,
"output": 32000
}
}
}
}
}
}
```
A model supports `modelID`, `family`, `name`, `package`, `settings`, `headers`, `body`, `capabilities`, `variants`,
`cost`, `disabled`, and `limit`. If supplied, `capabilities` requires `tools`, `input`, and `output`. `limit` may set
`context`, `input`, and `output`. Cost values are USD per million tokens:
```jsonc
{
"cost": {
"input": 3,
"output": 15,
"cache": {
"read": 0.3,
"write": 3.75
}
}
}
```
Set `disabled: true` on a model to remove it from the available model list. V2 does not define provider-level
whitelist or blacklist fields.
## Variants
Variants are named request overlays for one model. They can override `settings`, `headers`, and `body`; the selected
package determines which values are meaningful.
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"openai": {
"models": {
"gpt-5.2": {
"variants": [
{
"id": "fast",
"settings": {
"reasoningEffort": "low"
}
},
{
"id": "deep",
"settings": {
"reasoningEffort": "high",
"reasoningSummary": "auto"
}
}
]
}
}
}
}
}
```
Choose a variant with the TUI variant picker or `/variants`. Explicit CLI, agent, and command model references use
`provider/model#variant`; for example:
```bash
opencode2 run --model openai/gpt-5.2#deep "Review this project"
```
The current V2 top-level `model` default does not retain a `#variant` selection, so choose the variant separately.
Selecting an ID that is not defined for the model fails instead of silently using the default request settings.

View file

@ -0,0 +1,177 @@
---
title: "References"
description: "Make local directories and Git repositories available as project context."
---
References give OpenCode named access to directories outside the current
project. Use them for documentation, shared libraries, examples, or source from
another repository.
Configure references by alias in `opencode.json` or `opencode.jsonc`:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"references": {
"docs": {
"path": "../product-docs",
"description": "Use for product behavior and terminology"
},
"effect": {
"repository": "Effect-TS/effect",
"branch": "main",
"description": "Use for Effect implementation details"
}
}
}
```
## Local directories
Use `path` for a local directory:
```jsonc
{
"references": {
"design-system": {
"path": "../design-system",
"description": "Use when working with components or design tokens"
}
}
}
```
Relative paths resolve from the directory containing the config file that
defines them. Absolute paths and home-relative paths such as `~/docs` are also
supported.
The string shorthand is useful when no other fields are needed:
```jsonc
{
"references": {
"docs": "../docs",
"shared": "~/work/shared"
}
}
```
<Note>
A shorthand string is treated as a local path only when it starts with `.`,
`/`, or `~`. Use `./docs`, not `docs`; a bare `docs` value is interpreted as
a Git repository.
</Note>
## Git repositories
Use `repository` for a remote Git repository. GitHub `owner/repo` shorthand,
Git URLs, host/path forms, and SCP-style remotes are supported.
```jsonc
{
"references": {
"effect": {
"repository": "Effect-TS/effect",
"branch": "main"
},
"internal-sdk": {
"repository": "git@gitlab.example.com:platform/sdk.git",
"branch": "release/v2"
}
}
}
```
Without `branch`, OpenCode checks out and refreshes the remote's default
branch. Branch names may contain letters, numbers, `/`, `_`, `.`, and `-`, but
cannot start with `-` or contain `..`. Local `file:` repositories are not
supported.
Git references also support shorthand:
```jsonc
{
"references": {
"effect": "Effect-TS/effect",
"sdk": "gitlab.com/platform/sdk"
}
}
```
### Cloning and storage
OpenCode normalizes a remote and stores one checkout under its global data
directory at `opencode/repos/<host>/<repository-path>`. On a typical Linux
installation, for example, `Effect-TS/effect` is stored at:
```text
~/.local/share/opencode/repos/github.com/Effect-TS/effect
```
Missing repositories are cloned. Existing checkouts are fetched and reset to
the requested branch, or to the remote default branch when `branch` is omitted.
Materialization runs asynchronously when references load or reload, so a new
reference can appear before its checkout is ready. Clone and refresh failures
are logged and do not stop other references from loading.
<Warning>
The cache has one checkout per normalized remote, not one per branch. Do not
configure the same repository at multiple branches; only one branch can be
exposed. Avoid editing cached checkouts because a refresh resets them.
</Warning>
## Description and visibility
`description` tells agents when a reference is relevant. References with a
description are included in agent instructions with their alias and resolved
path. References without one remain available in `@` autocomplete but are not
advertised automatically.
Set `hidden` to `true` to remove a reference from TUI `@` autocomplete:
```jsonc
{
"references": {
"internal": {
"path": "../internal",
"description": "Use for internal service behavior",
"hidden": true
}
}
}
```
`hidden` controls only autocomplete visibility. It does not remove the
reference from the reference API or agent instructions when a description is
present.
## Use references
Type `@` in the TUI and select a reference alias to attach its root directory:
```text
Compare the current implementation with @effect
```
The attachment provides a non-recursive listing of the root's immediate files
and directories. V2 currently attaches references by root alias;
`@alias/path` is not a reference-specific file browser. Ask the agent to
inspect a particular path when more detail is needed.
References do not grant extra tool permissions. Access outside the active
Location remains subject to the agent's normal tool rules and the
`external_directory` permission. Editing a reference additionally requires the
applicable edit permission.
## Fields
| Field | Local | Git | Description |
| --- | --- | --- | --- |
| `path` | Required | No | Local directory path |
| `repository` | No | Required | Remote Git repository |
| `branch` | No | Optional | Branch to fetch and check out |
| `description` | Optional | Optional | Guidance describing when agents should use it |
| `hidden` | Optional | Optional | Hide it from TUI `@` autocomplete |
An alias cannot be empty or contain `/`, `\`, whitespace, a backtick, or a
comma.

39
packages/docs/sharing.mdx Normal file
View file

@ -0,0 +1,39 @@
---
title: "Session sharing"
description: "Understand the current beta status of session sharing in OpenCode V2."
---
Session sharing is not yet available in OpenCode V2. V2 does not currently
publish sessions, upload conversation history to a sharing service, or create
public links.
<Warning>
The V2 TUI registers `/share`, but it currently only reports that sharing is unavailable. There is no functional
share/unshare command or server API endpoint.
</Warning>
## Configuration
The V2 configuration schema accepts a `share` field with three values:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"share": "manual"
}
```
- `"manual"` represents sharing only when explicitly requested.
- `"auto"` represents automatically sharing new sessions.
- `"disabled"` represents preventing session sharing.
These values are parsed but are not acted on by the current V2 runtime. In
particular, setting `"auto"` does not publish sessions. If `share` is omitted,
V2 leaves the sharing policy unspecified.
## Beta limitations
V2 currently provides no public session viewer, share URL, history sync,
retention controls, or unshare/delete operation. Until those surfaces are
implemented in the V2 server and protocol, keep using sessions locally and do
not treat the `share` configuration field as a privacy or publishing control.

228
packages/docs/skills.mdx Normal file
View file

@ -0,0 +1,228 @@
---
title: "Skills"
description: "Add reusable, on-demand instructions to OpenCode."
---
Skills are Markdown instructions that OpenCode can advertise to an agent and
load when they are relevant. A skill can include supporting scripts,
references, and other files in the same directory.
## Create a skill
Create one directory per skill with a `SKILL.md` file:
```text
.opencode/skills/
└── git-release/
├── SKILL.md
├── scripts/
│ └── changelog.ts
└── references/
└── release-policy.md
```
```markdown title=".opencode/skills/git-release/SKILL.md"
---
name: Git Release
description: Prepare release notes, version bumps, and GitHub releases
metadata:
opencode/slash: "true"
---
## Workflow
1. Read `references/release-policy.md`.
2. Summarize merged changes since the previous tag.
3. Propose the version bump before changing files.
4. Run `scripts/changelog.ts` only after the user approves the version.
```
Paths in a skill are relative to the directory containing `SKILL.md`.
## Discovery
OpenCode automatically adds the following source directories:
| Scope | Sources |
| --- | --- |
| Global | `~/.config/opencode/skill`, `~/.config/opencode/skills` |
| Global compatibility | `~/.claude/skills`, `~/.agents/skills` |
| Project | `.opencode/skill`, `.opencode/skills` |
| Project compatibility | `.claude/skills`, `.agents/skills` |
For project sources, OpenCode searches from the current directory upward to
the project root and includes matching directories at every level.
Within each source directory, OpenCode discovers:
- Markdown files at the source root, such as `skills/git-release.md`
- `SKILL.md` files at any depth, such as `skills/git-release/SKILL.md`
The directory form is recommended because it gives the skill a private base
directory for supporting files.
## Configure sources
Use the `skills` array in any `opencode.json` or `opencode.jsonc` to add local
directories or HTTP catalogs:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"skills": [
"./team-skills",
"~/shared/opencode-skills",
"/opt/company-skills",
"https://example.com/opencode/skills/"
]
}
```
Relative paths are resolved from the active OpenCode working directory, not
from the directory containing the config file. Paths beginning with `~/` use
the current user's home directory. Only `http://` and `https://` values are
treated as URL sources.
Every discovered config document contributes its `skills` entries; the arrays
are additive rather than replacing one another.
### HTTP catalogs
An HTTP source is a base URL containing an `index.json`:
```json title="index.json"
{
"skills": [
{
"name": "git-release",
"version": "3",
"files": [
"git-release.md",
"references/release-policy.md"
]
}
]
}
```
OpenCode downloads those files from
`<base-url>/git-release/<file>`. File paths must be safe, relative,
same-origin paths. Each entry must include either `SKILL.md` or a Markdown file
named after the index entry, such as `git-release.md`.
Use the named Markdown form for HTTP catalogs. Each downloaded skill directory
is itself a source root, so `git-release.md` produces the ID `git-release`; a
root-level `SKILL.md` produces the literal ID `SKILL` in the current V2
implementation. Increment `version` when files change so OpenCode refreshes
the cached copy.
## Frontmatter
V2 reads these fields:
| Field | Purpose |
| --- | --- |
| `name` | Display name; defaults to the path-derived ID |
| `description` | Summary shown to the model and command catalog |
| `slash` | Set to `false` to hide the skill from the V2 slash-command catalog |
| `metadata.opencode/slash` | Boolean or `"true"`/`"false"`; overrides `slash` |
| `metadata.opencode/autoinvoke` | Set to `false` to omit the skill from model-facing discovery |
Frontmatter, `name`, and `description` are optional at runtime. However, a
clear `description` is strongly recommended: skills without one are not
advertised to the model. `license`, `compatibility`, and other metadata may be
included for portability, but V2 does not interpret them.
`opencode/autoinvoke: false` only removes the skill from the model's available
skills list. The skill remains registered and can still be activated explicitly
by its ID.
## IDs and validation
The skill ID comes from its path, not its frontmatter:
| File | ID |
| --- | --- |
| `<source>/git-release.md` | `git-release` |
| `<source>/git-release/SKILL.md` | `git-release` |
| `<source>/teams/release/SKILL.md` | `release` |
IDs are exact and case-sensitive. V2 currently does not enforce the Agent
Skills name regex, length limits, a match between `name` and the directory, or
a maximum description length. The frontmatter `name` is only a display label.
For portable, predictable skills, use a unique lowercase kebab-case ID of 1-64
characters and keep it aligned with the directory name:
```text
^[a-z0-9]+(-[a-z0-9]+)*$
```
## Precedence
Skills are keyed by ID. If several sources define the same ID, the later source
wins. Sources are registered in this order, from lower to higher precedence:
1. Built-in skills
2. `.claude/skills` sources, global first and then from the current directory upward
3. `.agents/skills` sources, global first and then from the current directory upward
4. `~/.config/opencode/skill` and `~/.config/opencode/skills`
5. Project `.opencode/skill` and `.opencode/skills`, from the project root toward the current directory
6. Explicit `skills` config entries, in config priority and array order
Within one `.opencode` directory, `skills` has precedence over `skill`. Avoid
duplicate IDs unless an override is intentional.
## Runtime loading
At each model step, OpenCode advertises permitted skills that have a
description and do not set `opencode/autoinvoke` to `false`. The advertisement
contains only each skill's ID, name, and description; it does not add every
skill body to the prompt.
When the model calls the `skill` tool with an exact ID, OpenCode:
1. Resolves the current winning definition for that ID
2. Checks the `skill` permission for the selected agent
3. Adds the Markdown body, without frontmatter, to the conversation
4. Provides the skill's base directory and a sample of up to ten supporting file paths
Supporting file contents are not loaded automatically. The agent can read a
referenced file when the skill instructs it to do so. The supporting-file
sample is available for directory-based `SKILL.md` skills; flat Markdown skills
receive no neighboring file list.
In the V2 CLI, skills appear as `/id` commands unless `slash` resolves to
`false`. Selecting one appends the skill body as a skill message and resumes
the session.
## Permissions
Permission rules use the `skill` action and the skill ID as the resource. Rules
are evaluated in order, with the last matching rule winning:
```jsonc title="opencode.jsonc"
{
"permissions": [
{ "action": "skill", "resource": "*", "effect": "allow" },
{ "action": "skill", "resource": "internal-*", "effect": "deny" },
{ "action": "skill", "resource": "experimental-*", "effect": "ask" }
]
}
```
`deny` removes matching skills from model-facing discovery and rejects skill
tool loading. `ask` advertises the skill but requests approval when the model
loads it. The same rules can be placed under an individual
`agents.<id>.permissions` array.
## Troubleshooting
If a skill is missing or loads the wrong content:
1. Confirm the file is either a root-level `*.md` or a nested file named exactly `SKILL.md`.
2. Check the path-derived ID rather than the frontmatter `name`.
3. Add a `description` if the skill should be advertised to the model.
4. Check `opencode/autoinvoke` and the selected agent's `skill` permissions.
5. Look for a later source defining the same ID.
6. For HTTP catalogs, verify `index.json`, same-origin file paths, and a changed `version`.

109
packages/docs/snapshots.mdx Normal file
View file

@ -0,0 +1,109 @@
---
title: "Snapshots and undo"
description: "Understand filesystem snapshots, undo, redo, and message reverts in OpenCode V2."
---
OpenCode snapshots let the default interactive TUI roll back conversation history and related file changes. They are a
convenience for revising recent work, not a replacement for Git commits or backups.
## Configuration
Snapshots are enabled by default. Set `snapshots` to `false` in your [configuration](/config#snapshots) to stop capturing
filesystem state:
```jsonc title="opencode.jsonc"
{
"$schema": "https://opencode.ai/config.json",
"snapshots": false
}
```
Filesystem snapshots require a Git repository. With snapshots disabled, unavailable, or missing, undo can still stage a
conversation rollback, but it has no captured file state to restore. Disabling snapshots does not delete snapshots that
were already stored.
## What is captured
For each model step, OpenCode attempts to capture the worktree immediately before the model call and after a cleanly
completed step. It records the paths changed between those two points on the assistant message.
Snapshots use a separate internal Git object database in the OpenCode data directory. They do not create commits, move
branches, or intentionally modify your repository's Git index. Capture is limited to the session's active directory, which
may be a subdirectory of the repository.
Within that directory, snapshots include tracked files and untracked files that are not ignored by Git. An individual
untracked file larger than 2 MiB is excluded. Ignored files, files outside the active directory, and changes to Git
metadata are not captured.
During undo, OpenCode does not check out an entire tree. It restores only paths attributed to cleanly completed assistant
steps after the selected conversation boundary. Each path is restored to its state before the first affected step.
## Undo
Wait for the session to become idle, then run:
```text
/undo
```
The TUI finds the latest non-empty user message and stages a revert at that message:
- The selected user message and every later message are hidden, but not deleted yet.
- The selected message's text, attachments, and agent mentions are placed in the composer for revision.
- Captured files changed by the affected assistant steps are restored to their earlier contents. Files created by those
steps are removed when they did not exist in the earlier snapshot.
- A summary shows the staged message count and restored paths.
Running `/undo` again moves the staged boundary to an earlier user message. OpenCode keeps the filesystem state from
immediately before the first undo as the redo baseline, so repeated undos form one wider staged revert rather than a redo
stack.
<Warning>
Sending a new prompt while an undo is staged commits the revert. The hidden message range is removed from the active
session history, the currently reverted files are kept, and redo is no longer available.
</Warning>
## Redo
While a revert is staged, run:
```text
/redo
```
Redo clears the staged boundary, makes the hidden messages visible again, and restores affected files to their exact state
immediately before the first undo. It does not rerun the model. After multiple undos, one redo restores the whole staged
range; there is no step-by-step redo stack.
## Revert a message
The TUI's **Message Actions** menu also provides **Revert**. It stages the selected message as the conversation boundary
and uses the same file restoration and redo behavior, but it does not copy that message into the composer.
For a conversation-and-files rollback, select a user message. If an assistant message is selected, that message is hidden,
but only file changes attributed to later assistant steps are restored; the selected assistant message's own file changes
are not included.
## Limitations and safety
- Capture is best effort. A failed capture is logged and the model step continues, so conversation rollback may have no
matching file rollback.
- Interrupted or failed steps do not receive a completed end snapshot. File changes made before the failure may remain.
- Shell commands can change databases, services, processes, network resources, Git state, ignored build output, or files
outside the active directory. Undo and redo do not reverse those side effects.
- Undo overwrites the current contents of affected paths with older contents. Redo likewise overwrites those paths with
the pre-undo state, including edits made after running undo.
- Other processes can edit the worktree between capture and restore. The server rejects revert operations while the
session is actively running, but it cannot protect against external editors or commands.
- Snapshot objects can contain complete contents of tracked and non-ignored untracked files. They are stored locally in
the OpenCode data directory; do not treat snapshots as secret-free metadata.
- Undo is not secure erasure. Committing a revert removes messages from the active projection, not from durable session
history or existing snapshot storage.
Review the staged file summary and your Git diff before continuing. Commit or back up important work independently before
using undo on a dirty worktree.
<Note>
`/undo` and `/redo` are interactive commands in the default V2 TUI. The non-interactive `run` command and the minimal
interactive interface do not provide these slash commands.
</Note>