Fusion setup
This skill configures the Fusion agent team (a main agent, a sidekick executor, an explore searcher, and optional specialists) by writing the user's GLOBAL opencode config at
(on Windows:
%USERPROFILE%\.config\opencode\
). It does not require cloning any repository.
opencode resolves that directory as
$XDG_CONFIG_HOME/opencode/
when
is set and non-empty, falling back to
otherwise. Every
path below means that resolved directory. The bundled installer in Step 4 works this out itself, so the normal flow needs no special handling; only the manual fallback in Step 4b and the verification in Step 5 need you to resolve it by hand. Installing into
on a machine with a different
writes a tree opencode never reads - the install looks successful and Fusion silently never loads.
What Fusion is
Fusion splits work across agents with asymmetric permissions:
- (main, primary): a strong model that plans, makes judgment calls, and reviews. It CANNOT edit files, search the codebase, or run arbitrary shell. Its only path to changing files is delegating to the sidekick via the tool.
- (primary): plan mode - the same planning brain as build. Produces a reviewed plan and delegates exploration, but does not execute; switch to build to carry it out. Overrides opencode's built-in plan agent so plan mode stays Fusion-aware.
- (subagent): a cheaper, fast model with full and broad access (direct / and common wrappers are denied as defense-in-depth - committing stays with the main agent). It executes precise specs handed to it by the main agent.
- (subagent): a cheap model used for read-only codebase exploration.
- (subagent): read-only external research - web search and docs. No edit access.
- (subagent): frontend/UI implementation. Loads design skills, edits files, runs the dev/build tooling.
- (subagent): critiques a plan before implementation and audits a diff before commit (correctness, scope, security). Read-only plus lint/test; no edit access.
- (subagent): reads images/screenshots the main model cannot see and reports them as text. Only needed when the main model lacks image input.
Think of the repo as a catalog of roles: the core (build/plan/sidekick/explore) is required, and the rest are optional pieces you install only if your workflow needs them. The research/design/reviewer/vision specialists are optional. Each role's model is chosen independently - that is a key reason to use Fusion: put your favorite design model on
and a different reviewer model on
.
The asymmetry is enforced by the permission layer, not by convention. Preserving the exact permission frontmatter in the bundled agent files installed during Step 4 is what makes Fusion work.
Step 0 - Offer a subscription profile
Before the per-role interview, ask whether the user's models come from one of these subscriptions. Each maps to a bundled profile - a ready-made config fragment in
<this-skill-dir>/profiles/
with sane per-role defaults:
| Profile | Subscription |
|---|
| OpenCode Go (low-cost open-model plan on OpenCode Zen) |
| OpenCode Zen pay-as-you-go credits |
| OpenCode Zen free-tier models only |
| ChatGPT Plus or Pro |
| GitHub Copilot |
If the user names one (including as a
argument):
-
Read
<this-skill-dir>/profiles/<name>.json
and show its role -> model table for confirmation. The JSON is the single source of truth - never quote model ids from memory or from this document.
-
Remind them that authentication is out-of-band: the provider must be connected once via
(or
inside opencode) with their subscription login or key. NEVER ask for a key in the chat. Profile provider blocks deliberately contain no npm adapter, baseURL, or apiKey - opencode knows these providers natively; the blocks only carry display names.
-
Skip Steps 1-3 and run the installer with the profile (Step 4's delegation rules still apply):
bash
node <this-skill-dir>/scripts/install.js apply --profile <name> --extras commands,plugin
No
flag is needed: the installer derives the role list from the profile, so every role the profile assigns a model also gets its permission-bearing agent file.
-
To change one or two picks, keep
and add
holding just the delta (for example a different
, plus a provider block only if that model's provider is outside the profile). The fragment wins over the profile. Removing an optional role a profile assigns cannot be expressed as an override - use the custom interview (Steps 1-4) without
instead.
Caveats worth mentioning when they apply:
includes a
role because its main model cannot read images, while the other profiles lead with models that read images directly;
runs on free-period models whose prompts may be used for training under OpenCode's policy - warn users to keep sensitive code off it; the single-vendor
profile keeps every role on one vendor, so its reviewer differs from build by model (GPT-5.6 Terra) rather than by vendor - a user with a second provider may still want to override the reviewer for true cross-vendor review;
defaults its main to Claude Sonnet 5 for credit-cost sanity - a user who wants max quality can override build to
github-copilot/claude-opus-5
(billed much higher per token). There is deliberately NO Claude Pro/Max provider profile. A subscription login cannot become
. If the user wants a limited Claude Pro/Max integration, offer the optional Claude Code review bridge below. Subscription lineups rotate - if a profile model errors as unknown, the ids may have drifted; fall back to the custom interview and report it.
Optional Claude Pro/Max review through Claude Code
If the user asks for Claude subscription OAuth, keep another normal OpenCode provider or profile as the build model and offer the
extra. It installs a local OpenCode plugin with two custom tools: a sanitized readiness check and a stateless plan review. Only the build and plan agents may call them.
The bridge invokes Anthropic's official
CLI in print mode. It does not read the credential store, extract an OAuth token, put a token in
, or expose Claude as an OpenCode model. It verifies that
reports a first-party Pro or Max login, removes API-key and third-party routing variables from the child process, defaults to Claude Opus 5 at high effort (the review tool accepts an optional full
model id and an effort of low, medium, high, xhigh, or max per call), disables tools and customizations, sends the review packet over stdin, and disables session persistence. It runs the CLI from a neutral temporary directory, honors session cancellation, and its tools refuse callers other than the build and plan agents even before permissions apply.
Ask the user to install Claude Code and authenticate directly with
; never ask them to paste a token. On Windows they need the native Claude Code build (the installer script or
), not the npm shim - the bridge spawns
without a shell. Then include
in Step 4's extras, for example
--extras commands,plugin,claude
. Do not enable this by default. Anthropic describes subscription usage as intended for its native applications, including Claude Code, and says third-party-tool access may be allowed at its discretion or charged to usage credits. Present this as an optional compatibility bridge, not as a guaranteed subscription entitlement or an API replacement.
If the user has none of these subscriptions, or wants full control over every pick, continue with Step 1.
Step 1 - Gather the user's model choices
Ask the user which model to use for each role. Do not assume; let them choose their own provider and models. Collect:
- Main/build model (a strong model - e.g. an Opus/GPT-class model).
- Sidekick model (a fast, cheaper coding model).
- Explore model (cheap; can be the same as sidekick).
- Research model (read-only external research; a solid general model).
- Design model (frontend/UI work; pick whichever model does design best in your opinion).
- Reviewer model (critiques plans and audits diffs; often a strong model, and deliberately can differ from the main model).
- Vision model (reads images) - ONLY ask this if the user's main/build model does not support image input. Most frontier models read images directly, so skip this question unless the main model cannot. The vision model must be one that accepts image input.
Roles 4-7 are optional a-la-carte pieces. If the user only wants the core build/plan/sidekick/explore roles, skip them - but offer them, since choosing a different model per specialist is a key reason to use Fusion. The
agent activates from its installed
(Step 4), which overrides opencode's built-in plan agent - it reuses the main/build model and needs no
block in opencode.json and no separate model question. Do not offer
when the main model already reads images.
For each distinct provider the chosen models use, collect the connection details:
- provider id (e.g. , , , )
- the npm adapter (for OpenAI-compatible local gateways use
@ai-sdk/openai-compatible
)
- baseURL (for local gateways / custom endpoints)
- if the endpoint needs a key: the NAME of an environment variable that holds it (e.g. ). NEVER ask the user to paste the actual key into the chat - the config references the variable and opencode resolves it at startup, so the secret never appears in the conversation or in plaintext config.
- the model id(s) and a display name
If the user is unsure, offer the OpenAI-compatible local-gateway shape as the default pattern and ask for their baseURL and key env var name.
Step 2 - Build the provider blocks
For each provider, build a block under
. OpenAI-compatible template:
json
"<provider-id>": {
"npm": "@ai-sdk/openai-compatible",
"name": "<display name>",
"options": {
"baseURL": "<baseURL>",
"apiKey": "{env:<ENV_VAR_NAME>}"
},
"models": {
"<model-id>": {
"name": "<display name>",
"attachment": true,
"modalities": { "input": ["text", "image"] }
}
}
}
The
placeholder is documented opencode config syntax: the key is read from the user's environment at startup, so it never sits in plaintext in opencode.json. An UNSET variable silently resolves to an empty string, which surfaces later as auth errors - tell the user to set the variable in the environment they launch opencode from. If they prefer a key file,
"apiKey": "{file:~/.secrets/<name>}"
works the same way.
Only include
/
for models that actually support image input. A main model with image input means no separate vision agent is needed.
The template above shows the OpenAI-compatible shape (
@ai-sdk/openai-compatible
). If a provider is a native vendor rather than an OpenAI-compatible gateway, use that vendor's adapter instead - for example
for an Anthropic-style endpoint or
for OpenAI. Only the
value changes; the rest of the block shape is the same.
If two roles use different models from the SAME provider (for example a main model and a cheaper research model both on provider
), do NOT emit two provider blocks with the same id - that is a duplicate key. Emit ONE block for that provider with BOTH models listed under its
object, like this:
json
"kirocc": {
"npm": "@ai-sdk/anthropic",
"name": "Kirocc",
"options": { "baseURL": "<baseURL>", "apiKey": "{env:KIROCC_API_KEY}" },
"models": {
"claude-opus-4-8": { "name": "Opus", "attachment": true, "modalities": { "input": ["text", "image"] } },
"claude-sonnet-5": { "name": "Sonnet" }
}
}
Step 3 - Build the config fragment
Build a config FRAGMENT with this exact structure and save it to a temporary file (OS temp dir is fine). Do NOT write
~/.config/opencode/opencode.json
yourself - the installer script in Step 4 merges the fragment in deterministically. When running as the restricted Fusion build agent, delegate creation of the temporary fragment and the Step 4 installer command together in one sidekick task so build never needs direct filesystem access. In plan mode, stop after specifying the choices and tell the user to switch to build (or run the command themselves); plan must not execute the install. Replace the
placeholders with the user's choices. Model references are always
. The JSON only assigns models - each role's mode, permissions, and prompt come from its agent file (Step 4), and the build agent's permission frontmatter is the core of Fusion and must not be loosened.
json
{
"$schema": "https://opencode.ai/config.json",
"subagent_depth": 2,
"model": "<main-provider>/<main-model-id>",
"provider": {
"<main-provider-id>": { "npm": "...", "options": {}, "models": {} },
"<sidekick-provider-id>": { "npm": "...", "options": {}, "models": {} }
},
"agent": {
"build": { "model": "<main-provider>/<main-model-id>" },
"explore": { "model": "<explore-provider>/<explore-model-id>" },
"sidekick": { "model": "<sidekick-provider>/<sidekick-model-id>" },
"research": { "model": "<research-provider>/<research-model-id>" },
"design": { "model": "<design-provider>/<design-model-id>" },
"reviewer": { "model": "<reviewer-provider>/<reviewer-model-id>" }
}
}
Notes:
- Keep . OpenCode 1.18.2+ defaults to , which lets build start sidekick but prevents sidekick from starting its permitted read-only explore/research helper. The installer enforces a minimum of and preserves a larger existing value.
- Replace the two
"<...-provider-id>": { ... }
placeholder lines under with the ACTUAL provider block(s) you built in Step 2. If your main and sidekick share one provider, that is a single block (see Step 2 on merging models); if they use different providers, include one block each. The placeholder shape shown is not valid config on its own - it must be filled in.
- opencode auto-loads every markdown file in
~/.config/opencode/agent/
as an agent definition: frontmatter supplies the role's and , and the body is its prompt. No fields belong in opencode.json - Step 4 installs the files that carry them.
- Backup and merge are the installer's job (Step 4): it backs up any existing config to
opencode.json.backup.<timestamp>
and deep-merges the fragment - your fragment wins on conflicting keys, everything else in the user's config is preserved. Never silently discard an existing config: if the user explicitly wants a clean overwrite instead of a merge, they should move the old aside first.
- Add
"vision": { "model": "<vision-provider>/<vision-model-id>" }
to the block ONLY if the user configured a vision role (main model lacks image input). Omit it otherwise.
- OPTIONAL top-level hardening keys (documented opencode fields; add if the user wants a tighter, cheaper, more private local setup):
"small_model": "<cheap-provider>/<cheap-model-id>"
- opencode uses a small model for background tasks like title generation; if unset it may fall back to a remote default. Pin it to one of the user's own cheap local models to keep everything on their providers.
"enabled_providers": ["<provider-a>", "<provider-b>"]
- allowlist of providers to load; keeps the model picker deterministic and ignores any other credentials present.
"compaction": { "prune": true }
- drops stale tool outputs when compacting context, which cuts main-agent token cost in a delegation-heavy Fusion flow.
- Per custom model, an optional
"limit": { "context": <n>, "output": <n> }
inside the model block lets opencode track remaining context accurately (models on models.dev supply this automatically; custom local gateways do not). Use the real context/output window for that model; do not guess.
Step 4 - Run the deterministic installer
The skill bundles an installer at
<this-skill-dir>/scripts/install.js
(plain Node, no dependencies). It owns every mechanical step - timestamped backup, deep merge, atomic write, prompt-file copies, an undo manifest, and post-install validation - so none of that depends on improvised file operations. Its version-2 manifest stores the original bytes and permissions of every managed file, plus hashes of the exact installed state. Reapply and undo refuse before writing if the config or a managed file changed after installation; they never guess which content belongs to Fusion. Run it with the fragment from Step 3:
bash
node <this-skill-dir>/scripts/install.js apply --config <path-to-fragment.json> --extras commands,plugin
- applies a bundled subscription profile (Step 0) as the base fragment; a fragment, when also given, overrides it key by key. Unknown profile names fail listing the available ones.
- is normally omitted: the installer derives the list from the config - the core plus every optional role the fragment or profile assigns a model (explore needs no file by design). An explicit can add extra roles, but one that omits a role the config assigns a model is refused: a role with a model and no agent file would run without Fusion's permissions.
- installs the optional slash commands and audit plugin described below. Add only when the user explicitly wants the Claude Code Pro/Max review bridge:
--extras commands,plugin,claude
.
- Add to print the full plan (backup name, merged keys, files) without writing anything - offer this if the user seems cautious. The plan also prints an line naming any top-level key whose current value the fragment would replace.
- is for reapplying over an the user edited by hand since the last install. Without it that mismatch refuses, which would otherwise wedge reapply permanently - including a prompt-only refresh whose managed files are all intact. The merge folds the fragment into the CURRENT file, so hand edits survive; the flag only re-records the baseline hash. The manifest keeps the pre-install , so a later undo still restores the true pre-Fusion state. It deliberately does NOT excuse a modified managed prompt: that refusal protects a deliberate local customization from being overwritten.
- The script refuses with exit 1 and changes nothing when validation or ownership checks fail, including invalid JSON/config shapes, unsafe paths, changed managed files, and invalid destination parents. It warns when a model references a provider that has no provider block.
- If the agent running this skill cannot execute bash (for example the Fusion build agent's allowlist), delegate both the fragment creation and this exact command to the sidekick. In plan mode, switch to build or have the user run it. Use the manual fallback below only when Node is unavailable.
Manual fallback - install the agent prompt files by hand
Only when Node is unavailable. Before merging or copying anything, make a timestamped backup of
and of every destination file that already exists, and record which destinations did not exist. Then copy the prompt files bundled with this skill into the global agent folder (one per role you configured).
is the directory this SKILL.md lives in - its bundled prompts are in the
subfolder next to this file. Every configured role except
needs its agent file installed (explore is opencode's built-in read-only subagent and only gets a model in the JSON); in particular the sidekick DOES need its
file - its permissions and prompt come entirely from that file:
<this-skill-dir>/agent/build.md
-> ~/.config/opencode/agent/build.md
<this-skill-dir>/agent/plan.md
-> ~/.config/opencode/agent/plan.md
<this-skill-dir>/agent/sidekick.md
-> ~/.config/opencode/agent/sidekick.md
<this-skill-dir>/agent/research.md
-> ~/.config/opencode/agent/research.md
<this-skill-dir>/agent/design.md
-> ~/.config/opencode/agent/design.md
<this-skill-dir>/agent/reviewer.md
-> ~/.config/opencode/agent/reviewer.md
<this-skill-dir>/agent/vision.md
-> ~/.config/opencode/agent/vision.md
(only if a vision role was configured)
These carry the full operating instructions and permissions for each role. Each subagent file's frontmatter sets its
and
; the files deliberately ship WITHOUT a
key, because markdown frontmatter overrides opencode.json on any key it sets - a model baked into the file would silently override the user's Step 3 choice. Models come only from opencode.json. Install only the files for the roles you configured - if the user skipped research/design/reviewer/vision, skip those.
Step 4b - Optional commands and plugins
Four optional pieces ship next to the skill. The installer groups the two commands under
, the audit plugin under
, and the Claude Code bridge under
(manual copy paths below if the script cannot run):
- Slash command: copy
<this-skill-dir>/commands/fusion-setup.md
-> ~/.config/opencode/commands/fusion-setup.md
(note the PLURAL directory). This gives a discoverable command that launches this setup flow; it accepts optional arguments for a targeted reconfigure.
- Status command: copy
<this-skill-dir>/commands/fusion-status.md
-> ~/.config/opencode/commands/fusion-status.md
. This gives a health check that verifies the setup is installed, loaded, and enforcing (live tool schema, config on disk, installed agent files, and the optional Claude bridge). It only reports - it changes nothing.
- Audit plugin: copy
<this-skill-dir>/plugins/fusion-audit.js
-> ~/.config/opencode/plugins/fusion-audit.js
(PLURAL ). It logs the delegation tree (subagent spawns and edit/write/apply_patch/task tool calls) and aggregates per-agent token usage per session via opencode's logger for auditing - the raw numbers behind "did Fusion actually save money?". It is observational only - it cannot see the calling agent, so it does not enforce anything; permissions do the enforcing. Skip it if the user does not want extra logging.
- Claude Code bridge: copy
<this-skill-dir>/plugins/fusion-claude.js
-> ~/.config/opencode/plugins/fusion-claude.js
. Also merge "fusion_claude_*": "deny"
into the top-level map. The build and plan agent files contain the two exact allows that override this global deny. If the existing top-level permission is shorthand, preserve it as before adding the Claude deny. This bridge is deliberately review-only and optional. Note that re-running the installer without the extra does not remove a previously installed bridge - the plugin file, the global deny, and the build/plan allows all stay in place; removal is (or deleting the plugin file and the deny by hand).
Step 5 - Validate and finish
- The installer validates automatically (it re-parses the written config and checks every installed file exists, failing loudly otherwise). If you used the manual fallback instead, do the same checks yourself: parse
~/.config/opencode/opencode.json
, and confirm every agent prompt file you installed exists under ~/.config/opencode/agent/
(build and sidekick at minimum).
- If you installed commands or plugins manually, confirm the selected files exist under
~/.config/opencode/commands/
and ~/.config/opencode/plugins/
. For the Claude bridge, also confirm the top-level deny and the exact build/plan allows are present.
- If any provider block references , confirm with the user that the variable is set in the environment they launch opencode from - using a presence-only check that never prints the secret:
[ -n "$VAR" ] && echo set || echo missing
in bash/zsh, if defined VAR (echo set) else (echo missing)
in cmd. Do NOT suggest - that prints the actual credential into the terminal (and into the transcript if run through an agent). An unset variable becomes an empty string and shows up later as auth errors.
- Tell the user to fully quit and restart opencode - config is loaded once at startup and is not hot-reloaded. After restart, the status bar should show the main model on the Build agent. If the Claude bridge was installed, call ; it reports readiness without returning account identity or credential data.
- Ask which toolchains the user works in. The bundled verification allowlists assume a Node/JavaScript project, and on any other stack the shipped entries match nothing the user actually runs - so the main agent cannot verify its own work and gets denied on the command it should be running. The subsets differ per role: allows , , , , and ; has the same list without ; allows only , , and . Check the file before telling the user to change an entry in it. Point them at README's "Adjust the bash allowlist" section for the per-stack tools (/ for Python, / for Rust, / for Go, / for Make-driven builds). Three things to say out loud: keep first; prefer an exact pattern over a trailing , because matches the whole rest of the command and a broad also permits the file-rewriting and (pin the command the project actually runs where you can, since enumerating every writing flag never finishes); and if you do need a broad allow, put each narrowing deny after it, because opencode resolves overlapping patterns by last-match-wins. These prompts are global, not per project, so a user who works across stacks should add their entries alongside the JS ones rather than replacing them. The git half of the allowlist needs no change.
Reconfiguring later
To change a model, build a small fragment with
(and a
block if the new model uses a new provider), then rerun the installer and restart opencode. For a profile-based install, rerun with the same
plus the small
delta. Reapply preserves the original pre-Fusion baseline and keeps all previously managed files recorded for a complete undo.
Going through the installer is preferred because it keeps the manifest accurate, but editing
in place is supported and does not lose anything: the next reapply refuses once so the mismatch is visible, and
accepts the edited file as the new baseline while still merging the fragment into it. Editing an installed
is different - that refusal has no override, because silently overwriting a deliberately customized prompt is worse than stopping. Restore the bundled copy of that file if you want the installer to manage it again.
Undoing Fusion
Run the bundled undo. It restores the exact pre-install config and every destination that Fusion replaced, removes only destinations Fusion originally created, keeps every timestamped config backup, and refuses before changing anything if the installed state was edited afterward or the manifest is unsafe:
bash
node <this-skill-dir>/scripts/install.js undo
Manual fallback after a manual install (no Node):
- Restore and every destination that existed before installation from the backups made during the manual install.
- For destinations recorded as newly created, remove them only if they are still byte-for-byte identical to the bundled file that was installed. If any file changed, leave it in place and report the conflict instead of deleting user work.
- Apply the same restore-or-remove rule to optional commands and plugins.
- Tell the user to restart opencode - it falls back to its built-in build/plan agents.
For an automatic install when Node is temporarily unavailable, wait until Node is available and use the manifest-driven undo. If an automatic manifest is missing, the timestamped files recover only
; do not remove prompts or extras unless independent backups or byte-for-byte ownership evidence prove they are still Fusion-owned. Confirm with the user before deleting anything, and never delete the backups themselves.