Pantheon 2.0 requires OpenCode 2.x — the OpenCode V1 contract was removed
entirely (1.7.x was the last line with V1 support). There are two complementary
ways to install. Loading the plugin through the host's native flow is the
recommended path; npx pantheon-opencode init remains the installer for the
full component set. pantheon_model offers only manual status, show,
set, and reset, with no preset wizard: the host default stays intact
without an explicit per-agent override, and preset markers and
PANTHEON_MODEL_PRESET are ignored and preserved. Without an override,
delegates inherit the parent chat model natively.
# Recommended: load the plugin through the host (npm package; a git URL works too)
opencode plugin add pantheon-opencode
# Full component set: agents, skills, instructions, commands, plugins (+ MCP/TUI)
npx pantheon-opencode init
# Headless preview; add --yes to authorize applying the same defaults
npx pantheon-opencode init --headless
npx pantheon-opencode init --headless --yes
# Separate repository wrapper: config migration review + installer preflight
npm run setup
# Verify installation
npm run doctor
# Optional: activate the Jev decision model in the same pass
npx pantheon-opencode init --decision-model opencode-console-jev/jev-1.13 --decision-monthly-usd 5 --decision-per-decision-usd 0.10-
Requirements: a supported host — OpenCode 2.x (Pantheon 2.0 does not support an OpenCode 1.x host) — plus Node.js >=26.4.0 for Pantheon's CLI/installer/Node tooling and Python 3.11+ (optional, MCP servers).
-
Known broken: OpenCode 2.0.22 does not expose
ctx.permission.assert. Node 22, Node 24, and Node 26.0–26.3 are outside the supported range. -
The host-native plugin flow.
opencode plugin add <npm-package|git-url>records the plugin under the pluralpluginskey as an object entry ({ "package": …, "options": { … } }). The host also auto-discovers every directory under.opencode/plugins/in the project and the global config directory. A local path must be a DIRECTORY containing anindex.ts/index.js— a file path warns "must be a directory" and a symlink is ignored. Pantheon's npm package ispantheon-opencode; its plugin export ispantheon-opencode/plugin. See Compatibility for the supported family and the known-broken exception. -
Components.
npx pantheon-opencode initinstalls agents, skills, instructions, commands, and the plugin registration + TUI (pluginscomponent) by default;runtime(Python/MCP) andpromptsare opt-in via selection or--components. A TTY run selects components and target, shows the plan, then requests one final authorization with default no. Without a TTY,initonly previews unless--yesis supplied;--yessuppresses prompts but still shows the preview and performs the integrity/drift checks. -
Direct
initandnpm run setupare different entry points.npm run setupruns the config review before installer init. Config migration and installer changes are both preflighted before one final authorization;--yesdoes not replace it. This wrapper is separate from directinit. -
The setup stage inspects
$XDG_CONFIG_HOME/opencode/opencode.jsonand$XDG_CONFIG_HOME/opencode/opencode.jsoncseparately whenXDG_CONFIG_HOMEis set to an absolute path; otherwise it uses~/.config/opencode(the global init target). Direct globalinituses the same target resolver. RelativeXDG_CONFIG_HOMEvalues are rejected before writes. The host layers these files in order:opencode.jsonthenopencode.jsonc; later scalar values win, plugin and permission arrays append, and MCP servers merge by name with the later whole server object replacing the earlier one. Other fields follow the host's field-specific merge behavior; there is no universal array-union rule. The review treats each file as a separate layer, never deletes either file, and never silently selects one. Interactive users select the target file(s) and choose a policy source when permission values conflict; the setup wrapper then shows the redacted policy and installer summaries before its final authorization. Legacypermissionrules can be prepared as nativepermissions; MCPaskcannot be enforced on this host, so the user must explicitly choose an outcome (allow, deny, leave accessible without a rule, or disable a nested MCP server where supported). Verified Pantheon-owned plugin/MCP entries are reported; JSONC entries move only when order and same-name overrides remain safe. An unsafe managed plugin blocks setup. For an unsafe managed MCP entry, interactive setup offers Continue, leaving those entries unchanged or Abort setup. Continuing keeps the entry in JSONC (where its whole server object can override the JSON layer); if init would also seed that server and create a duplicate layered entry, setup blocks before writes. Inspect and reconcile the layered config when that check blocks. Flat third-party MCP entries inopencode.jsoncare not rewritten by the installer, so setup blocks before consent or writes and reports only a redacted key path and count. Convert them manually tomcp.serversor remove them before retrying. Already nested third-party servers remain untouched. -
--yesalone authorizes nonpm run setupwrites. Headless/CI/non-TTY setup runs are preview-only and do not invoke writing init, even with--yes.--dry-runpreviews the migration and invokes init's dry-run only. Directnpx pantheon-opencode init --headless --yesdoes authorize its own apply. JSONC files with comments inside values that would be rewritten are preview-only rather than serialized lossily. Applied files receive a verified backup set under<config-dir>/.pantheon/backups/<timestamp>/and per-file atomic replacements; the writer preserves the existing permission mode. Ownership, group, ACLs, and extended attributes are not preserved or guaranteed. A partial failure may leave earlier file replacements applied; multiple files are not applied atomically. To locate a setup backup, inspect the timestamped entries under<config-dir>/.pantheon/backups/; usepantheon-opencode restoreto review and recover from a backup set. -
With npm's default configuration, mismatched versions can emit
EBADENGINEas a warning during install. Pantheon's postinstall then skips Node/config sync and lets the package finish installing; the CLI blocks configuration with an explicit message before writing files. -
engine-strict=truecan make npm itself stop the install. To switch without a global/system-wide install, use a user-managed Node version manager:nvm install 26.4.0 && nvm use 26.4.0 # or: fnm install 26.4.0 && fnm use 26.4.0 # or: volta install node@26.4.0
-
This does not assume a universal Node runtime for the OpenCode host: official OpenCode 2.x — a supported host — has a Bun runtime path and a separate Node build pinned to Node 26.4.0.
-
That host runtime does not change the package floor, and no Bun minimum is declared.
-
node:sqlite>=22.5 is only a capability threshold for the optionalpantheon_costbackend, not the package's support requirement. -
In an interactive TTY setup summary, the installer recommends a persistent external location for future worktrees:
<project-parent>/.pantheon-worktrees/<exact-repository-directory-basename>/<task>(for example,/home/user/.pantheon-worktrees/My Project/feature-name). Use the project directory's exact basename, including spaces; do not normalize it into a slug. -
The recommendation is guidance for agents only; Pantheon does not create Git worktrees or directories automatically.
-
Interactive TTY
inituses Clack prompts for one component selection and one global/project target, then shows the preview and requests final authorization (default no).--yessuppresses prompts; runtime and prompt templates remain opt-in in either mode. -
Every path that ends without applying prints exactly one line naming where it stopped (
canceled at the review prompt,declined at the install location,canceled at the component selection, …). Ctrl-C is handled after the install starts and until its outcome is recorded: before a managed write it exits130without changing configuration; after a write it recordspartialwithapplied: true, warns that changes may be partial, preserves the backup, and does not roll back automatically. The handler is deactivated once apply is recorded, so Ctrl-C during optional post-install--doctorcannot rewrite a completed install. While a prompt is on screen, the prompt library also turns Ctrl-C into the matching cancel. A cancel, decline, failure, or apply is written to.pantheon/last-run.jsonin the selected target;install-state.jsonis written by apply. Pre-authorization and drift refusals keep their no-write guarantee. A run-record write failure is reported as a warning and never changes the exit code. -
Delegation and background mode depend on the tools exposed by the host version/runtime. In the reported Beta 10 session, Zeus had the
subagenttool, with notask_status/cancel_taskor guarantee of tracking/recovery. -
Check the runtime's schema and documentation; do not assume
task()or background are available. The experimental variable below applies only to hosts/versions that document it. -
5-minute walkthrough: QUICKSTART.md.
init --dry-run shows the managed-file operations without applying them. To
review in one invocation and authorize in another, save a versioned plan:
# Human-readable or JSON preview (stdout only)
npx pantheon-opencode init --project --no-mcp --components agents --dry-run
npx pantheon-opencode init --project --no-mcp --components agents --dry-run --json
# Save and apply the reviewed plan; without --yes, a TTY session asks
npx pantheon-opencode init --project --no-mcp --components agents --dry-run \
--save-plan .pantheon-install-plan.json
npx pantheon-opencode init --apply-plan .pantheon-install-plan.json --yesThe artifact is versioned JSON written atomically with 0600 permissions; it
stores identities, options, snapshots/fingerprints, and operations, not the
contents of generated files. It is bound to the plan's original path, target,
and package identity. On apply, the installer recomputes the plan and compares
source and destinations; on drift it aborts before writing (exit code 3).
--apply-plan accepts only the plan path and authorization (--yes/-y or a
TTY prompt). Saved plans with --force/--clean are not supported.
A dry run runs the same host gate as the apply, including the --version
probe, which executes inside a throwaway HOME/XDG sandbox with a 2-second
timeout, so the preview writes nothing and cannot disagree with the apply it
authorizes. A host the gate cannot confirm as OpenCode 2.x refuses the preview
exactly like the apply; OPENCODE_VERSION=v2 is the deterministic escape
hatch when no host binary can be probed. The plan carries v2_capabilities
(what the plugin registers, plus the statically known not-registered seed and
the scope of that list) and plugin_options (whether the written plugin entry
can receive the opt-in options), so a machine sees the same facts the text
notice prints. A sibling opencode.jsonc that the installer
neither reads nor writes is reported in the plan's advisories array. The plan
describes managed-file operations and, when runtime is selected, names whether
the virtual environment will be created, reused, or recreated and that MCP
dependencies may be installed/downloaded. It does not enumerate every
dependency. Per-file detail is opt-in via --verbose (or structured --json).
postinstall/sync-tui is also outside the init approval flow and may update
TUI artifacts without a prompt. Do not treat the preview as a transaction or as
a promise of automatic rollback.
Before the first managed write, init creates an exact backup under
.pantheon/backups/<timestamp>/ (directory 0700, files 0600, retaining the
5 most recent). Because the backup is byte-for-byte, it can contain credentials
already present in your files; do not share it. To recover, use
npx pantheon-opencode restore [--project] [--yes]. A failure or interruption
before the first managed write leaves configuration unchanged; after a write,
the run is recorded as partial/applied and the backup is preserved. Recovery is
manual through restore, not an automatic rollback.
The uninstaller can also be reviewed before applying:
npx pantheon-opencode uninstall --project --dry-run
npx pantheon-opencode uninstall --project --forceWithout --force, applying asks for confirmation on a TTY and fails closed in a
non-interactive environment. The uninstaller removes only items identified as
Pantheon's, preserves symlinks, and backs up regular files it will change or
remove; backups are not deleted by uninstall. On global installs it also
undoes the native Context negation in cli.json, removing only the
-opencode.sidebar.context directive (and the whole file when Pantheon created
it), so the native Context block returns. --purge-data is narrower: it only
allows removing .pantheon/install-state.json when it is a regular file and a
verifiable schema-v2 state. It is not a general cleanup of .pantheon and does
not remove backups. Review with --dry-run first; to apply purge
non-interactively, combine --purge-data --force.
New installs keep a versioned per-file inventory for regular managed files
(exact relative path, component/layout, hash, and mode; no content is stored).
Use reconcile to identify files that have left the current source and remain
identical to the installer's last write:
# Read-only preview; select --global or --target PATH when appropriate
npx pantheon-opencode reconcile --project --dry-run
npx pantheon-opencode reconcile --project --dry-run --components agents,skills
npx pantheon-opencode reconcile --global --target /caminho/config-opencode --dry-run
npx pantheon-opencode reconcile --target /caminho/projeto --dry-run
# Reviewable plan, applied in a separate invocation with explicit authorization
npx pantheon-opencode reconcile --project --components agents --save-plan .pantheon-reconcile.json
npx pantheon-opencode reconcile --apply-plan .pantheon-reconcile.json --yes
# Alternative: review and apply in the same invocation
npx pantheon-opencode reconcile --project --apply --yesThe preview writes or removes nothing. Apply revalidates the inventory, hashes,
and exact operation set; any drift cancels before removal. Before deleting, it
creates a restorable backup under .pantheon/backups/; use
pantheon-opencode restore to recover. Only exact regular files are removed —
directories are never deleted recursively. Unselected components remain outside
reconciliation.
Pre-2.0 states and installs older than the inventory migrate to an empty list:
their existing files are unknown/ambiguous and are never inferred as
Pantheon-owned or removed automatically. Changed, missing, symlinked,
invalid-path, or incompatible-layout files are also preserved. doctor
distinguishes previously managed files that were modified or disappeared; when
the current payload is unavailable, it reports that only the recorded hash was
checked. reconcile accepts --project, --global, or --target PATH; an
explicit target uses the project layout by default. Combine --global --target PATH to indicate that an explicit global target uses the flat global layout.
This choice is neither inferred nor reinterpreted from the path. Without a
readable inventory, there is no automatic cleanup.
- OpenCode 2.x — Install OpenCode. OpenCode 2.0.22 is known broken (no
ctx.permission.assert). An OpenCode 1.x host is refused before any write. - Node.js >=26.4.0 — for
npx pantheon-opencode initand Node-based Pantheon tooling (engines.node=>=26.4.0). Node 22, Node 24, and Node 26.0–26.3 are unsupported. - The optional
pantheon_costbackend separately needsnode:sqlite(Node >=22.5); this capability threshold does not lower package support. - Official OpenCode 2.x — a supported host — has a Bun runtime path and a separate Node build pinned to 26.4.0; no Bun minimum is implied.
- Python 3.11+ — for MCP servers (optional, used by
npm run setup) - Git — for version detection in TUI sidebar
The TUI plugin is a root workspace. package.json declares
"workspaces": ["src/plugins/tui"], and the root package-lock.json carries a
link: true entry for node_modules/pantheon-tui resolving to
src/plugins/tui. A single npm ci --ignore-scripts at the root installs the
root project and the TUI subpackage; there is no second install step, and
npm ci --prefix src/plugins/tui is no longer part of any workflow.
For a checkout validation:
npm ci --ignore-scriptsnpm ci is the only accepted path: there is no fallback to npm install, and
PANTHEON_ALLOW_NPM_INSTALL_FALLBACK is not a supported escape hatch. The
post-install TUI sync likewise propagates a failing npm ci; it does not
silently repair or rewrite a lockfile.
Two lockfiles exist and both are committed, but they now serve different consumers, which is worth being explicit about:
- root
package-lock.json— what the workspace install and CI use. src/plugins/tui/package-lock.json— a published artifact. It is copied byscripts/sync-tui.mjsinto the user's live plugin dir, wherenpm ci --omit=devruns against it, not the root one. A manifest/lock divergence there makes every end user'snpm install pantheon-opencodehard-fail insidepostinstall.
That second lock can no longer be regenerated in place: under a workspaces
parent, npm install --package-lock-only in the member directory reports "up
to date" and folds the change into the root lock instead, and neither
--workspaces=false nor --ignore-workspace-root-check changes that. Hand
applying the same version to both files is valid only for a version change on
a dependency already present in the lock's tree; adding or removing one
requires generating the lock in an isolated directory (no workspace parent)
and copying it back — and that regeneration re-resolves the entire transitive
tree, so its diff must be reviewed. tests/tui-workspace-lock.test.mjs is the
gate that catches a drift in either direction.
tsdown@0.23.0 requires rolldown: "~1.2.7". The exact 1.2.7 override
satisfies that range and keeps the workspace and standalone TUI installs on
the same bundler, whose minifier output is part of the committed bundle. Pinning
tsdown alone does not pin its dependency; a direct devDependency would not
hold either because Node resolution can prefer a nearer nested copy. The root
and standalone TUI overrides both enforce the same version. tsdown 0.23.0's
engine range (^22.18.0 || ^24.11.0 || >=26.0.0) includes the project's
declared Node support floor (>=26.4.0).
Two consequences a maintainer must know before touching it:
overridesbypasses range validation rather than erroring, so it can silently contradict a dependent's declared range.tests/tui-workspace-lock.test.mjsasserts the override againsttsdown's declared range and that the root lock resolves exactly onerolldown; CI additionally asserts the installed version is exactly1.2.7.- Changing it can alter minifier output and break the
TUI dist freshnessgate. Update both overrides, regenerate both locks, rebuilddist, and review the generated diff together.
The root and standalone TUI manifests override @babel/core to 7.29.7
(security floor >=7.29.6) and seroval to 1.6.8 (security floor
>=1.6.3). The affected packages are reachable from the shipped TUI runtime;
these pins clear the audited production advisories while upstream
@opentui/solid@0.5.10 still pins Babel 7.28.0 and Solid 1.9.12.
seroval@1.6.8 is outside Solid 1.9.12's declared ~1.5.0 range. Keep
solid-js pinned to the exact 1.9.12 peer required by OpenTUI rather than
allowing a workspace copy of 1.9.15; tests/tui-workspace-lock.test.mjs
checks both lock trees and exercises the Seroval serializer API used by
Solid's server renderer. Run that regression, both clean install paths, and
the production audit before changing these overrides. Remove them only when
upstream dependency ranges accept patched releases and the production audit
remains clear without them.
npm run coverage runs test:node under Node's built-in
--experimental-test-coverage and prints line/branch/function coverage for the
.mjs/.js modules under scripts/, src/ and bin/. It adds no
dependencies and enforces no threshold — the repository has no established
coverage floor, so a percentage gate would fail immediately on pre-existing
untested code and block unrelated work. The number is a baseline to improve
against, not a gate.
What it does not cover: test:ts is a hand-rolled tsx loop with no
node --test runner, so the 106 TypeScript files under src/ and bin/ are
entirely unmeasured by this command; test:ci is pytest and reports no
JavaScript coverage at all. Python coverage additionally requires
pytest-cov, which is not declared in any requirements file — see
src/mcp/requirements-mcp.txt.
Pantheon registers one plugin. The installer writes the <installed>/src/plugin
directory under the plural plugins key, in the object form the host needs to
deliver options:
{ "plugins": [{ "package": "<installed>/src/plugin", "options": {} }] }The host gate must CONFIRM the host is OpenCode 2.x before any write:
OPENCODE_VERSION=v2 pins the answer, an OPENCODE_BIN basename of
opencode2 is an explicit operator hint, and otherwise a --version probe
decides. A confirmed 1.x host, a failed probe, or an unreadable version is a
hard refusal before any write — there is no fallback and no --opencode-version
flag. The resolution order is documented once in
Compatibility. The exact tool list is in
Architecture; user-facing
behavior is in OpenCode 2.x.
The installer strips Pantheon references from the legacy singular plugin key
(a pre-2.0 config is recognized and migrated, never recreated) before writing
the current registration, so a host never loads two Pantheon plugins.
Third-party entries are retained and not converted.
The installer writes native fields: providers, permissions, and MCP entries
under mcp. The enforceable MCP rule is
{ "action": "<server>_*", "resource": "*", "effect": "deny" } (or the exact
tool name). action: "mcp" and the legacy key permission.mcp.<server> are not
evaluated by the host; the installer converts allow/deny and emits no rule
for ask, because ask hides the tool instead of prompting.
The Zeus-only pantheon_jev_decide advisory tool registers automatically on
hosts that expose the native Decisions API; using it requires the user-level
decision-route configuration. Two ways:
# One command at install time (explicit caps required — no defaults):
npx pantheon-opencode init --decision-model opencode-console-jev/jev-1.13 \
--decision-monthly-usd 5 --decision-per-decision-usd 0.10
# Or any time later:
npx pantheon-opencode decision-router setup-model opencode-console-jev/jev-1.13 \
--monthly-usd 5 --per-decision-usd 0.10
npx pantheon-opencode decision-router doctor # read-only route diagnosis--decision-no-consent configures everything except egress consent. Other
providers (TypeSafe direct, OpenRouter-hosted) and the full gate chain:
Native Decisions. Performance tuning (hook
costs, prompt budget, model tiers): Performance.
There is no install-time model wizard. init accepts --model and
--small-model for an explicit top-level override in the generated config
(validating provider/model-id); --preset is refused — the legacy
active-preset marker is not read by the installer.
Per-agent overrides belong to the pantheon_model tool. Both the tool and the
/pantheon-model command write overrides to active-preset.json and never
write .env or top-level model/small_model.
/pantheon-model status
/pantheon-model set --agent hermes --model opencode-go/gpt-5.6-terra --effort medium --scope project
/pantheon-model reset --agent hermes --scope project
The tool exposes explicit status, show, set, and reset actions and no
wizard: project-only, rejecting scope=global and authorization flags in the
same call. The /pantheon-model command with no arguments runs a legacy
interactive wizard that still accepts global scope with separate confirm +
authorize-global. Validation errors name the accepted agents, require
provider/model-id, reject an unknown model or invalid effort (a valid effort
above the model ceiling is clamped with a warning), and warn when a text-only
model is requested for vision. Without an override the host model stays intact;
delegates inherit the parent chat model natively. See
MCP Tools.
An interactive TTY install offers an optional vision step after the component
and target prompts: automatic discovery (default, writes nothing), pin a
provider/model (merges visionModel into the global opencode-vision.json
atomically with a .bak), or skip. Non-interactive flows never prompt and
never write vision config. /pantheon-vision shows the resolved target and
origin, and its picker applies visionModel with the same atomic merge. See
MCP for model precedence and the degradation chain.
OpenCode 2.x documents subagent as the child-session tool. It is a host tool,
not a Pantheon-specific API; availability, schema, background support, and
completion observability depend on the active runtime. A foreground call waits
for the result; with background: true, where the schema supports it, the call
returns a sessionID and the parent is notified on completion. task() is
host/API terminology, not a guaranteed root tool name.
The brief, return contracts, retry rule, and the caller/target matrix are in Architecture. The installer does not register a Pantheon delegation tool.
The TUI is an optional component installed by npm run setup or an install that
includes the plugins component. The installer discovers the local plugin's
index.ts + tui.ts pair and registers the copied directory through the
plugins entry. Without the plugins component, no entry is installed.
The installer also cancels the host's builtin Context block by appending
-opencode.sidebar.context to the global ~/.config/opencode/cli.json
plugins array. The negation is global-only: a project install neither creates
nor edits cli.json, so the native Context block can appear duplicated.
uninstall removes exactly that directive (and the file when Pantheon created
it). A cli.json that is not valid JSON, or whose plugins is not an array, is
never overwritten; the install warns and continues.
The sidebar block and /dispatch panel are documented in
OpenCode 2.x.
npm ci --ignore-scripts --no-audit --no-fund
npm run build --prefix src/plugins/tuiInstall the built plugin into .opencode/plugins/pantheon-tui so the directory
contains the index.ts and tui.ts entrypoints. TUI plugins load
at process start, so restart OpenCode after each build. Never add
src/plugins/tui to
opencode.json; that file is packaged and merged into user configs.
Type these in the OpenCode chat:
| Command | Description |
|---|---|
/pantheon |
Multi-perspective council synthesis via inline agents |
/pantheon-audit |
3-layer code audit: heuristic scan → Themis deep review → OWASP Top 10 |
/pantheon-deepwork |
Heavy multi-phase task with persisted checkpoints and Themis review gates |
/pantheon-model actions; pantheon_model tool (status|show|set|reset) |
Manual per-agent overrides in active-preset.json (overrides.agents[agent]). The tool: explicit actions without a preset wizard, project-only; status shows project overrides and warnings; set takes agent, model, optional effort, and optionally scope: project; reset takes agent and optionally scope: project. Global files are neither read nor applied by the tool; the no-argument command runs a legacy wizard that still accepts global scope with separate confirm + authorize-global. Atomic write .bak+lock; never writes .env or top-level model. |
/pantheon-optimize |
Project optimization: bloat scan, deepwork archive, cache migration, token report |
/pantheon-consolidate |
Merge and deduplicate memory entries through the persistence MCP server |
Static presets are retired from the runtime:
PANTHEON_MODEL_PRESETandpresetmarkers do not activate models. Only explicit project per-agent overrides are applied, global files are ignored by the tool, and without an override the host model stays intact. Theset-tierCLI still activates a preset profile for provider configuration; the runtime ignores the marker.
No hardcoded secrets — only env var names + baseURLs below.
# see whether a legacy marker is present (it is ignored at runtime)
echo $PANTHEON_MODEL_PRESET
cat .pantheon/active-preset.json 2>/dev/null || cat ~/.config/opencode/.pantheon/active-preset.json 2>/dev/null || echo "nenhum active-preset.json (herança nativa)"
# per-agent status (no secrets)
/pantheon-model status
# or
/pantheon-model show
# validate preset defs
node scripts/validate-routing.mjs
# or
node -e "import('./src/pantheon/presets.mjs').then(m=>console.log(m.validatePresetDefs(m.loadPresetDefs())))"| Preset | Provider | BaseURL | Key env (name) | Compatibility alias | When required |
|---|---|---|---|---|---|
go-free (legacy) |
opencode |
https://opencode.ai/zen/v1 |
PANTHEON_OPENCODE_API_KEY |
OPENCODE_GO_API_KEY |
When that profile is applied via set-tier |
go-fast (legacy) |
opencode-go |
https://opencode.ai/zen/go/v1 |
PANTHEON_OPENCODE_API_KEY |
OPENCODE_GO_API_KEY |
When that profile is applied via set-tier |
go-premium (legacy) |
opencode-go |
https://opencode.ai/zen/go/v1 |
PANTHEON_OPENCODE_API_KEY |
OPENCODE_GO_API_KEY |
When that profile is applied via set-tier |
go-hybrid (OpenCode Go) |
opencode-go |
https://opencode.ai/zen/go/v1 |
PANTHEON_OPENCODE_API_KEY |
OPENCODE_GO_API_KEY |
When that profile is applied via set-tier |
openai |
openai |
https://api.openai.com/v1 |
OPENAI_API_KEY |
— | When that profile is applied via set-tier |
anthropic |
anthropic |
https://api.anthropic.com/v1 |
ANTHROPIC_API_KEY |
— | When that profile is applied via set-tier |
| (no preset) | — | — | — | — | Native inheritance — uses the chat model (/model ...) |
- Configure the appropriate variable in the environment before using the profile.
set-tierand the tool show only the variable name: they do not collect or validate keys, print secret values, or write.env. - Install logs report names/state, never key values.
| Symptom | Cause | Solution |
|---|---|---|
| Provider without a credential | Go/OpenAI/Anthropic profile active, but the expected variable is unavailable | Configure PANTHEON_OPENCODE_API_KEY (alias OPENCODE_GO_API_KEY for Go), OPENAI_API_KEY, or ANTHROPIC_API_KEY, as the profile requires |
unknown agent "foo" in /pantheon-model set |
non-canonical agent | Use one of the 14: zeus, athena, apollo, hermes, aphrodite, demeter, themis, prometheus, hephaestus, nyx, gaia, iris, mnemosyne, talos |
model must use provider/model-id format |
model without a slash, or with a space/.. |
Use provider/model-id, e.g. openai/gpt-5.6-sol, opencode-go/kimi-k2.7-code (validates MODEL_REF_PATTERN) |
global set/reset requires explicit confirmation... |
--scope global without confirmation (command path) |
Add --confirm --authorize-global (or use --scope project, the safe default). The pantheon_model tool does not support global at all. |
.pantheon/active-preset.json has invalid JSON |
corrupted file | Delete or fix the JSON; readActivePresetRaw treats it as absent + logs warn without secrets; write preserves the previous file on failure |
health-check: active-preset.json verification failed |
writeActivePreset wrote, but preset mismatched |
Check permissions (mode default 600, O_NOFOLLOW, fsync dir); the writer logs a warning and does not expose the key |
text-only model with a hasVision warning |
used big-pickle/kimi-k2.7-code etc. for vision |
Switch to a multimodal model (mimo-v2.5*, gpt-5.6*, minimax-m3) or accept the limitation (vision:false in the CAPABILITY_TABLE) |
Presets defined: 6 fails in validate-routing.mjs |
corrupted routing.yml |
node --check src/pantheon/presets.mjs + yamllint src/routing.yml |
| Plugin not loading after install | the entry is a file path or a symlink, not a directory | Register a directory: opencode plugin add pantheon-opencode, or place the plugin directory under .opencode/plugins/ |
A non-interactive apply requires
--yes. Without a TTY,initprints the plan and changes nothing unless--yes(alias-y) explicitly authorizes it. TTY defaults and headless defaults match;runtimeandpromptsare opt-in. Exit codes:0ok/plan-only/refused restore ·2apply requested without authorization ·3drift ·130canceled ·1failure.
- Preview:
init --dry-runprints the plan and does not write to the target. For review across invocations, use--save-plan <file>; to apply the reviewed artifact, use--apply-plan <file> --yes(or confirm on the TTY). The versioned plan is written with mode0600, includes metadata/fingerprints, and does not include generated content. It is bound to the original path, target, and package; apply recomputes operations and snapshots and aborts with code3on drift. Saved plans with--force/--cleanare not accepted. - The host gate in the preview: a dry run runs the same gate as the apply,
including the
--versionprobe, which runs in a throwaway HOME/XDG sandbox with a 2-second timeout. A host the gate cannot confirm as 2.x refuses the preview like the apply; passOPENCODE_VERSION=v2to make the answer deterministic when no host binary can be probed. - Preview limits: the plan represents managed-file operations and names
runtime effects when selected (venv create/reuse/recreate and dependency
installation/download); it does not enumerate every installed dependency.
Per-file detail is shown only with
--verbose(or structured--json).postinstall/sync-tuiis outside the approval flow. It is not a transaction and does not guarantee automatic rollback. - Exact backup: before the first write,
initsaves an exact set under.pantheon/backups/<timestamp>/(directory0700, files and manifest0600; keeps the 5 most recent). It is a byte-for-byte copy and therefore also contains any API keys/tokens already present in your files — so it is never printed (nor are its hashes). - Restore:
pantheon-opencode restore [--project] [--from <dir>] [--dry-run] [--yes]. It verifies the manifest integrity and refuses to restore a tampered set; it backs up the current state before reverting. In a non-interactive run it requires--yes; missing authorization fails closed without a backup or mutation. On a TTY it asks (default no): explicit decline exits0, cancellation exits130, and both leave files and backups unchanged. - Failure mid-apply: it aborts the remaining writes, cleans temporary files
(
<file>.tmp-<pid>), lists the paths already applied (without content), and prints the recovery instruction. There is no automatic rollback — the exact backup +restoreis the recovery path (an interrupted apply is recoverable the same way). - Outside the approval flow:
postinstall/sync-tuionly resynchronizes TUI artifacts; it does not prompt or pass through the--yesgate.
# Headless apply with the default components
npx pantheon-opencode init --headless --yes
# No Python/MCP runtime
npx pantheon-opencode init --headless --yes --no-mcp
# Project-scoped install
npx pantheon-opencode init --headless --yes --project
# Decision-model activation in the same pass (explicit caps required)
npx pantheon-opencode init --headless --yes \
--decision-model opencode-console-jev/jev-1.13 \
--decision-monthly-usd 5 --decision-per-decision-usd 0.10After installation, verify everything works:
# 1. Check agents are installed
ls ~/.config/opencode/agents/
# Should show 14 .md files
# 2. Check MCP servers
ls ~/.config/opencode/scripts/
# 3. Launch normally; use the conditional setting below only when supported.
opencode
# 4. Test delegation with the actual tool schema shown to Zeus
# In the reported Beta 10 runtime, dispatch through `subagent`; request
# background only if the active schema offers it.
# 5. Run health check
npm run doctorIf (and only if) the active host version/runtime documents and requires the
experimental background setting, export
OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true before launching OpenCode.
Verify delegation against the tools actually exposed to Zeus; do not infer
completion tracking or recovery from a successful dispatch.
Isolated global install: to test the installed package in an isolated
sandbox (without contaminating the dev environment, which mixes several
installs), use ~/pantheon-sandbox/ — run
bash ~/pantheon-sandbox/run-test.sh (mcp list 5/5 + doctor + isolated TUI).
This gate covers the prepared sandbox; it is not a support claim for every real
host. The runner installs the current stable @opencode/cli by default;
OPENCODE_V2_SPEC lets you select another build. Discard with
rm -rf ~/pantheon-sandbox; details in ~/pantheon-sandbox/README.md.
The installer treats src/plugins/tui as the single source of truth and always
copies it into the target config as plugins/pantheon-tui (the bundled dist/
package.json). OpenCode's TUI loader reads the copiedpackage.jsonexportsmap (./tui→dist/tui.js,./server→dist/server.js), so users do not needtsconfig.json,tsdown, or development dependencies after installation. The TUIbuildandtypecheckscripts are maintainer/development tasks for the repository, not post-install health checks; their failure in~/.config/opencode/plugins/pantheon-tuidoes not indicate a broken runtime.
Every install (global or project) registers exactly ONE Pantheon TUI reference
system-wide — the copied plugins/pantheon-tui directory in the installed
config. Stale references from older installs (dist file paths, in-package
src/plugins/tui dirs, bare plugins/pantheon-tui copies, npx paths) are
removed from all tui.json locations OpenCode reads: ~/.opencode/tui.json,
~/.config/opencode/tui.json and <project>/.opencode/tui.json.
Windows/WSL: each environment (Windows native vs. WSL/Linux) uses its own OpenCode binary, config directory, and Node/npx install. Run the installer separately inside each environment — never share a config dir or copy
tui.jsonreferences between them, since paths are environment-specific.
| Problem | Solution |
|---|---|
| Agents not found | Run npx pantheon-opencode init again |
| MCP servers not starting | Check Python 3.11+, run npm run setup |
| Background delegation not available | Check the active host version/runtime's tool schema and documentation. Set OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true only if that version documents support and requires it; the setting does not guarantee status, completion tracking, or recovery tools. |
| TUI sidebar not showing | Check the installed config's tui.json has exactly one plugins/pantheon-tui entry (absolute path) |
| Plugin not loading | Ensure <config>/plugins/pantheon-tui/dist/tui.js exists (copied at install time) |
| Health check fails | Run npm run doctor for detailed diagnostics |
MCP startup and packaging failures are separate from TUI loading.
src/mcp/memory_mcp.py is the canonical Memory MCP server. The source-tree
scripts/memory_mcp.py entrypoint delegates to it, and the runtime installer
copies the canonical source into the installed scripts directory.
install-mcp.mjs first resolves that installed copy and falls back to the
source-tree entrypoint when running from a checkout. The script-sync test checks
that both entrypoints expose the same tool list.
Pantheon's MCP servers (resources, code-mode, memory, persistence, vision) are spawned by OpenCode as local child processes. Two facts shape how spawn failures behave:
- OpenCode 1.18 does not retry a failed local MCP server. If the spawn
fails at startup, the server stays in
failedstate for the whole TUI process. There is no reconnect hook; the only recovery is restarting the TUI. Session data and persistence are safe — they live in the SQLite database, not in the MCP process. posix_spawn ENOENTmeans the command path does not exist. The most common cause is a stale hardcoded path (e.g. a config that still points at/home/admin/.config/opencode/...after the home directory was migrated) or a path relative to an ambiguous root. OpenCode spawns the command verbatim — a missing interpreter or script fails with ENOENT before any Python code runs.
Diagnosis
# Verify the interpreter and script exist for each configured server
ls /home/ils15/.config/opencode/.venv/bin/python3
ls /home/ils15/.config/opencode/scripts/mcp_resources.py
# etc. — compare against the paths in ~/.config/opencode/opencode.json
# Static check — `npm run doctor` (or `pantheon-opencode doctor`) validates
# that every local MCP command's executable and absolute script args exist
npm run doctor -- --target /home/ils15/.config/opencodeIf the doctor reports MCP "..." executable does not exist, fix the path in
~/.config/opencode/opencode.json (or re-run the installer, which resolves
hermetic absolute paths — see below) and restart the OpenCode TUI.
Recovery
# Close the TUI (Ctrl+C / exit) and relaunch. Sessions and data persist.
opencode
# Verify MCP servers are back:
opencode mcp listHermetic path policy (P2, 2026-08-05)
scripts/install-mcp.mjsgenerates commands with absolute interpreter and script paths, resolved in this order: canonical user install (~/.config/opencode/.venv/bin/python3+~/.config/opencode/scripts/*.py), then the local checkout (<ROOT>/.venv+<ROOT>/scripts|src/mcp), with a warning fallback to PATHpython3.scripts/doctor.mjsnow includes a hermetic spawn check: every local MCP entry is verified for an existing executable and existing absolute script args, so the ENOENT class is caught statically before OpenCode tries to spawn.- Avoid editing MCP commands to relative paths or paths under another user's home — they reproduce this failure class.
flowchart LR
A["opencode plugin add pantheon-opencode<br/>or npx pantheon-opencode init"] --> B{"--project?"}
B -->|No| C["~/.config/opencode/agents/ (global)"]
B -->|Yes| D[".opencode/agents/ (project-local)"]
C --> E["npm run setup"]
D --> E
E --> F["MCP servers + skills + TUI"]
F --> G["Check background support; configure only if documented"]
G --> H["opencode"]
H --> I["🚀 Pantheon 2.0 ready"]
This is an API-level illustration for hosts that expose the documented native
task() shape and completion semantics. It is not a guarantee that Zeus has a
root task() tool, that this signature is available, or that dispatch returns
a trackable result. In the reported Beta 10 Zeus-root runtime, dispatch was via
subagent; task_status and cancel_task were not exposed, and no status,
completion-tracking, or recovery guarantee follows from this diagram. Check the
active runtime's tool schema and documentation. Background support, the
experimental environment variable, and completion notifications are
version/runtime-dependent.
sequenceDiagram
participant Z as Zeus
participant A as Apollo (child)
participant H as OpenCode host
Z->>H: host API dispatch (e.g. task(), if exposed)
H-->>A: child session starts (host-dependent)
A-->>H: child completion (if emitted by host)
opt Host exposes a result or completion notification
H-->>Z: host-defined result/notification
end
Note over Z,H: Illustrative host API only; no Pantheon-specific delegate API
block-beta
columns 1
block["Pantheon 2.0"]
block("⎇ main")
end
block Sessions["▶ Sessions (N total)"]
end
block Commands["▶ Commands (5)"]
end
block Agents["▶ Agents (14)"]
end
block Config["▶ Config"]
end
block Memory["▶ Memory"]
end
end