diff --git a/packages/docs/agents.mdx b/packages/docs/agents.mdx
new file mode 100644
index 0000000000..54503dfb0a
--- /dev/null
+++ b/packages/docs/agents.mdx
@@ -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 Tab and Shift+Tab 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/.md
+.opencode/agents/.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`.
+
+
+ Put broad wildcard rules first and exceptions afterward. For example, deny
+ all subagents first, then allow `explore`.
+
+
+`~` 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 }
+ }
+ }
+ }
+}
+```
+
+
+ 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.
+
diff --git a/packages/docs/attachments.mdx b/packages/docs/attachments.mdx
new file mode 100644
index 0000000000..16e009fa18
--- /dev/null
+++ b/packages/docs/attachments.mdx
@@ -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.
+
+
+ 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.
+
+
+## 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,",
+ "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. |
+
+
+ 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.
+
+
+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.
diff --git a/packages/docs/commands.mdx b/packages/docs/commands.mdx
new file mode 100644
index 0000000000..9c889bd48e
--- /dev/null
+++ b/packages/docs/commands.mdx
@@ -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.
+
+
+ Shell interpolations run when the command is evaluated, outside the agent's
+ tool permission flow. Only use commands from sources you trust.
+
+
+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.
diff --git a/packages/docs/compaction.mdx b/packages/docs/compaction.mdx
new file mode 100644
index 0000000000..79316019e9
--- /dev/null
+++ b/packages/docs/compaction.mdx
@@ -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 `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.
diff --git a/packages/docs/config.mdx b/packages/docs/config.mdx
index 09294d094a..096620a6b0 100644
--- a/packages/docs/config.mdx
+++ b/packages/docs/config.mdx
@@ -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.
diff --git a/packages/docs/docs.json b/packages/docs/docs.json
index e8559d72f6..35d81ac0d9 100644
--- a/packages/docs/docs.json
+++ b/packages/docs/docs.json
@@ -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"
+ ]
}
]
},
diff --git a/packages/docs/formatters.mdx b/packages/docs/formatters.mdx
new file mode 100644
index 0000000000..a8e8702e85
--- /dev/null
+++ b/packages/docs/formatters.mdx
@@ -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.
+
+
+ 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.
+
+
+## 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` | 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.
diff --git a/packages/docs/index.mdx b/packages/docs/index.mdx
index 8e61f92a6b..d0e967b0fd 100644
--- a/packages/docs/index.mdx
+++ b/packages/docs/index.mdx
@@ -3,6 +3,11 @@ title: "Intro"
description: "Get started with OpenCode."
---
+
+ 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.
+
+
[**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
- 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.
@@ -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).
diff --git a/packages/docs/instructions.mdx b/packages/docs/instructions.mdx
new file mode 100644
index 0000000000..7c45ddb968
--- /dev/null
+++ b/packages/docs/instructions.mdx
@@ -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.
+
+
+ Current V2 discovery only recognizes `AGENTS.md`. The `CLAUDE.md` fallback
+ and related precedence described by older OpenCode documentation do not apply.
+
+
+### 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.
+
+
+ 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.
+
+
+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.
diff --git a/packages/docs/lsp.mdx b/packages/docs/lsp.mdx
new file mode 100644
index 0000000000..baf09b278a
--- /dev/null
+++ b/packages/docs/lsp.mdx
@@ -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.
+
+
+ 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.
+
+
+## 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` | No | Environment variables for the server process. The property is named `env`, not `environment`. |
+| `initialization` | `Record` | 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.
diff --git a/packages/docs/mcp.mdx b/packages/docs/mcp.mdx
new file mode 100644
index 0000000000..c19b76ce8d
--- /dev/null
+++ b/packages/docs/mcp.mdx
@@ -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 `_`. 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 `:` 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.
diff --git a/packages/docs/migrate-v1.mdx b/packages/docs/migrate-v1.mdx
new file mode 100644
index 0000000000..c9be38907d
--- /dev/null
+++ b/packages/docs/migrate-v1.mdx
@@ -0,0 +1,89 @@
+---
+title: "Overview"
+description: "Move from OpenCode V1 to the OpenCode 2.0 beta."
+---
+
+
+ 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.
+
+
+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)
+/opencode.json(c)
+/.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.
+
+
+ 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.
+
+
+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.
+
+
+ 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).
+
+
+## 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.
diff --git a/packages/docs/models.mdx b/packages/docs/models.mdx
new file mode 100644
index 0000000000..937fbb80e3
--- /dev/null
+++ b/packages/docs/models.mdx
@@ -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 `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"
+```
+
+
+ `opencode2 run` accepts `provider/model#variant`. The current `opencode2 mini --model` option accepts only
+ `provider/model`; choose its variant from the interactive interface.
+
+
+## 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.
+
+
+ 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.
+
+
+## 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.
diff --git a/packages/docs/permissions.mdx b/packages/docs/permissions.mdx
new file mode 100644
index 0000000000..7da958df0c
--- /dev/null
+++ b/packages/docs/permissions.mdx
@@ -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.
+
+
+ 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`.
+
+
+## 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 `/*` |
+| `_` | `*` 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.
+
+
+ `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.
+
+
+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..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.
diff --git a/packages/docs/plugins.mdx b/packages/docs/plugins.mdx
index cdc38a8808..878473c5e4 100644
--- a/packages/docs/plugins.mdx
+++ b/packages/docs/plugins.mdx
@@ -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.
+
+
+ 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.
+
+
+## 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.
diff --git a/packages/docs/providers.mdx b/packages/docs/providers.mdx
new file mode 100644
index 0000000000..10faab33fa
--- /dev/null
+++ b/packages/docs/providers.mdx
@@ -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}"
+ }
+ }
+ }
+}
+```
+
+Do not commit API keys or authorization headers to your repository.
+
+## 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.
diff --git a/packages/docs/references.mdx b/packages/docs/references.mdx
new file mode 100644
index 0000000000..3fbd624274
--- /dev/null
+++ b/packages/docs/references.mdx
@@ -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"
+ }
+}
+```
+
+
+ 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.
+
+
+## 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//`. 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.
+
+
+ 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.
+
+
+## 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.
diff --git a/packages/docs/sharing.mdx b/packages/docs/sharing.mdx
new file mode 100644
index 0000000000..5acceb7060
--- /dev/null
+++ b/packages/docs/sharing.mdx
@@ -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.
+
+
+ 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.
+
+
+## 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.
diff --git a/packages/docs/skills.mdx b/packages/docs/skills.mdx
new file mode 100644
index 0000000000..d97c60c99a
--- /dev/null
+++ b/packages/docs/skills.mdx
@@ -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
+`/git-release/`. 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 |
+| --- | --- |
+| `/git-release.md` | `git-release` |
+| `/git-release/SKILL.md` | `git-release` |
+| `/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..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`.
diff --git a/packages/docs/snapshots.mdx b/packages/docs/snapshots.mdx
new file mode 100644
index 0000000000..022460cbf6
--- /dev/null
+++ b/packages/docs/snapshots.mdx
@@ -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.
+
+
+ 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.
+
+
+## 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.
+
+
+ `/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.
+