Skip to content

Latest commit

 

History

History
822 lines (682 loc) · 46.5 KB

INSTALLATION.md

File metadata and controls

822 lines (682 loc) · 46.5 KB

Pantheon Installation Guide — 2.0 (OpenCode 2.x)

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.

TL;DR (Quick Start)

# 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 plural plugins key 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 an index.ts/ index.js — a file path warns "must be a directory" and a symlink is ignored. Pantheon's npm package is pantheon-opencode; its plugin export is pantheon-opencode/plugin. See Compatibility for the supported family and the known-broken exception.

  • Components. npx pantheon-opencode init installs agents, skills, instructions, commands, and the plugin registration + TUI (plugins component) by default; runtime (Python/MCP) and prompts are 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, init only previews unless --yes is supplied; --yes suppresses prompts but still shows the preview and performs the integrity/drift checks.

  • Direct init and npm run setup are different entry points. npm run setup runs the config review before installer init. Config migration and installer changes are both preflighted before one final authorization; --yes does not replace it. This wrapper is separate from direct init.

  • The setup stage inspects $XDG_CONFIG_HOME/opencode/opencode.json and $XDG_CONFIG_HOME/opencode/opencode.jsonc separately when XDG_CONFIG_HOME is set to an absolute path; otherwise it uses ~/.config/opencode (the global init target). Direct global init uses the same target resolver. Relative XDG_CONFIG_HOME values are rejected before writes. The host layers these files in order: opencode.json then opencode.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. Legacy permission rules can be prepared as native permissions; MCP ask cannot 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 in opencode.jsonc are not rewritten by the installer, so setup blocks before consent or writes and reports only a redacted key path and count. Convert them manually to mcp.servers or remove them before retrying. Already nested third-party servers remain untouched.

  • --yes alone authorizes no npm run setup writes. Headless/CI/non-TTY setup runs are preview-only and do not invoke writing init, even with --yes. --dry-run previews the migration and invokes init's dry-run only. Direct npx pantheon-opencode init --headless --yes does 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/; use pantheon-opencode restore to review and recover from a backup set.

  • With npm's default configuration, mismatched versions can emit EBADENGINE as 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=true can 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 optional pantheon_cost backend, 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 init uses Clack prompts for one component selection and one global/project target, then shows the preview and requests final authorization (default no). --yes suppresses 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 exits 130 without changing configuration; after a write it records partial with applied: 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 --doctor cannot 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.json in the selected target; install-state.json is 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 subagent tool, with no task_status/cancel_task or 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.

Installer review, authorization, and recovery

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 --yes

The 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.

Removal and purge

The uninstaller can also be reviewed before applying:

npx pantheon-opencode uninstall --project --dry-run
npx pantheon-opencode uninstall --project --force

Without --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.

Review drift in installed files

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 --yes

The 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.

Prerequisites

  • 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 init and Node-based Pantheon tooling (engines.node = >=26.4.0). Node 22, Node 24, and Node 26.0–26.3 are unsupported.
  • The optional pantheon_cost backend separately needs node: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

Package and dependency contract

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-scripts

npm 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.

The nested TUI lockfile is still shipped, and still load-bearing

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 by scripts/sync-tui.mjs into the user's live plugin dir, where npm ci --omit=dev runs against it, not the root one. A manifest/lock divergence there makes every end user's npm install pantheon-opencode hard-fail inside postinstall.

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.

Why overrides: { "rolldown": "1.2.7" } exists

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:

  • overrides bypasses range validation rather than erroring, so it can silently contradict a dependent's declared range. tests/tui-workspace-lock.test.mjs asserts the override against tsdown's declared range and that the root lock resolves exactly one rolldown; CI additionally asserts the installed version is exactly 1.2.7.
  • Changing it can alter minifier output and break the TUI dist freshness gate. Update both overrides, regenerate both locks, rebuild dist, and review the generated diff together.

TUI production security overrides

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.

Coverage reporting

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.

The plugin contract

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.

Config fields

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.

Jev decision model (optional)

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.

Model overrides (pantheon_model)

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.

Optional vision step

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.

Delegation

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.

TUI installation

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.

TUI dev build

npm ci --ignore-scripts --no-audit --no-fund
npm run build --prefix src/plugins/tui

Install 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.

Commands

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

Troubleshooting — keys and legacy presets

Static presets are retired from the runtime: PANTHEON_MODEL_PRESET and preset markers 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. The set-tier CLI still activates a preset profile for provider configuration; the runtime ignores the marker.

No hardcoded secrets — only env var names + baseURLs below.

Quick diagnosis

# 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())))"

Environment variables per preset profile (names, no values)

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-tier and 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.

Common errors

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/

Headless / CI (no TTY)

A non-interactive apply requires --yes. Without a TTY, init prints the plan and changes nothing unless --yes (alias -y) explicitly authorizes it. TTY defaults and headless defaults match; runtime and prompts are opt-in. Exit codes: 0 ok/plan-only/refused restore · 2 apply requested without authorization · 3 drift · 130 canceled · 1 failure.

Backup, restore, and approval flow

  • Preview: init --dry-run prints 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 mode 0600, 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 code 3 on drift. Saved plans with --force/--clean are not accepted.
  • The host gate in the preview: a dry run runs the same gate as the apply, including the --version probe, 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; pass OPENCODE_VERSION=v2 to 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-tui is outside the approval flow. It is not a transaction and does not guarantee automatic rollback.
  • Exact backup: before the first write, init saves an exact set under .pantheon/backups/<timestamp>/ (directory 0700, files and manifest 0600; 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 exits 0, cancellation exits 130, 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 + restore is the recovery path (an interrupted apply is recoverable the same way).
  • Outside the approval flow: postinstall/sync-tui only resynchronizes TUI artifacts; it does not prompt or pass through the --yes gate.
# 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.10

Verification

After 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 doctor

If (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.

TUI troubleshooting

TUI runtime versus development build

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 copied package.json exports map (./tui → dist/tui.js, ./server → dist/server.js), so users do not need tsconfig.json, tsdown, or development dependencies after installation. The TUI build and typecheck scripts are maintainer/development tasks for the repository, not post-install health checks; their failure in ~/.config/opencode/plugins/pantheon-tui does 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.json references 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 troubleshooting

MCP startup and packaging failures are separate from TUI loading.

Memory MCP packaging

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.

MCP servers and spawn failure (ENOENT)

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:

  1. OpenCode 1.18 does not retry a failed local MCP server. If the spawn fails at startup, the server stays in failed state 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.
  2. posix_spawn ENOENT means 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/opencode

If 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 list

Hermetic path policy (P2, 2026-08-05)

  • scripts/install-mcp.mjs generates 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 PATH python3.
  • scripts/doctor.mjs now 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.

Installation Flow

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"]
Loading

Host/API task() Delegation Concept

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
Loading

TUI Sidebar Layout

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
Loading