Installation lifecycle
September 17, 2026 ยท View on GitHub
Prerequisites include Node.js ^22.9.0 || ^24.0.0 and npm. See the
compatibility matrix for supported OpenCode versions,
platform status, exact integration pins, and evidence.
Install the published CLI package
The npm package exposes the dependency-free oak lifecycle CLI and the
oc-switch model selector:
npm install --global opencode-agent-orchestration-kit
oak version
oak install --dry-run
oak install
npm install does not mutate OpenCode configuration: the package declares no
preinstall, install, or postinstall lifecycle script. oak install
remains an explicit operation with the ownership, collision, backup, and
rollback rules documented below.
On native Windows, direct oak commands are best-effort: regular-file fsync is
retained, but directory-entry durability cannot match the POSIX protocol. The
Bash lifecycle wrappers remain unsupported; use WSL2 for the supported wrapper
path.
Installing from a cloned checkout remains supported through ./install.sh or
npm install --global . followed by oak install.
Test without a global install
git clone https://github.com/<owner>/opencode-agent-orchestration-kit.git
cd opencode-agent-orchestration-kit
export OPENCODE_CONFIG_DIR="$PWD/opencode"
source env.example
(cd opencode && npm ci --ignore-scripts)
opencode
Target selection
Every lifecycle command resolves its target in the same order:
--target PATH;- a non-empty
OPENCODE_CONFIG_DIR; $HOME/.config/opencode.
An explicitly empty configured value or missing required HOME is invalid. Argument values are literal, so ~ is not shell-expanded by the manager.
Commands
./install.sh [--dry-run] [--force] [--target PATH]
./upgrade.sh [--dry-run] [--target PATH]
./doctor.sh [--accept-preserved PATH] [--target PATH]
./uninstall.sh [--dry-run] [--yes] [--target PATH]
./rollback.sh [--dry-run] [--target PATH]
Each wrapper also accepts --version by itself. It prints the same identity as
node scripts/version.mjs; combining --version with another argument is an
invalid invocation.
Start mutating workflows with the corresponding --dry-run. A preview computes the complete plan and performs zero writes: it creates no target, .oak, lock, manifest, journal, or backup. Apply recomputes the plan after acquiring the lock; a changed fingerprint aborts.
Ownership and protected files
Managed state is stored under TARGET/.oak/. manifest.json lists only kit-owned files and source-overlapping files deliberately preserved as user-owned. Arbitrary target files and node_modules are never claimed.
These pre-existing root files remain user-owned by default when they differ:
AGENTS.md
opencode.json
tui.json
package.json
package-lock.json
An exact initial match may be adopted without rewriting. A differing protected file is preserved with source and user baselines so doctor can report later changes. A legacy install with no manifest is treated as an initial install.
Initial install --force is narrow overwrite authorization for colliding regular files. It first stores exact bytes and modes in the rollback point. It cannot replace directories or symlinks, traverse unsafe paths, overwrite .oak, bypass locks/corrupt state, force an upgrade, or delete a user-modified file.
Version identity
The root package.json is the only editable kit-version source. kit_version
in a manifest identifies the kit release whose payload and ownership state it
describes. schema_version identifies the manifest format; the two values are
independent. Every complete previous or next manifest snapshot records a
canonical stable kit_version.
Use these read-only checks from the repository root:
node scripts/version.mjs
npm run check:version
node scripts/version.mjs --check-tag v1.0.33
The tag check validates only the supplied value. It does not inspect the network, create a tag, or publish a release.
Upgrade and conflicts
upgrade requires a valid manifest. It may add new files, update unchanged owned files, remove unchanged obsolete owned files, and remove obsolete preserved metadata without touching user bytes.
The complete upgrade aborts before mutation for:
- modified or missing owned files;
- modified obsolete owned files;
- unowned collisions;
- unsafe paths, symlinks, or file/directory collisions;
- invalid manifest, journal, lock, or rollback state;
- a post-lock fingerprint change.
Version comparison is an additional pre-lock gate:
- source greater than installed: normal upgrade planning continues, including a manifest-only version transition when the payload is unchanged;
- equal version and equal payload: no-op;
- equal version and different payload: the complete upgrade is blocked with
same-version-different-payload; - source lower than installed: the complete upgrade is blocked with
source-older; arbitrary downgrade is unsupported.
Preserved user changes and pending protected-file merges are warnings, not authorization to overwrite those paths.
Doctor and preserved merge acknowledgement
Ordinary doctor is lock-free and read-only. Exit codes are:
0: no finding requires action; informational and not-applicable findings are allowed;1: one or more findings require safe operator action, including incompatible runtimes, incomplete dependencies or configuration, owned drift, a pending merge, unsafe permissions, stale generated registries, legacy residue, stale locks, interrupted transactions, rollback conflicts, or cleanup residue;2: invalid invocation, corrupt/unknown state, unsafe path/symlink, structural collision, or a filesystem error that prevents safe analysis.
The human-readable report has a deterministic headline, twelve ordered checks,
an immediate action beneath every action-required finding, and a final
summary. Check statuses are pass, info, action-required, or
not-applicable. The stable check order is opencode-version,
node-version, dependencies, installed-files, file-drift,
required-configuration, optional-plugins, permissions,
executable-scripts, skill-registry, compatibility, and
legacy-residue.
External pinned plugins are reported as info when they cannot be verified
offline; missing local plugin files are actionable. Ordinary doctor does not
run npm, access the network, inspect models, authentication, or caches, repair
files, delete legacy state, or adopt unmanaged files. It never prints
configuration contents, subprocess stderr, environment values, credentials, or
lifecycle backup bytes. Only the explicit --accept-preserved workflow below
can mutate state.
The report retains sourceVersion, installedVersion, and versionState.
The state is exactly one of:
not-installed: manifest absent;current: version and payload match;upgrade-available: the local source checkout is newer;source-older: the local source checkout is older;same-version-different-payload: release identity contradicts payload;invalid-version-state: a present manifest or canonical source version is invalid.
sourceVersion is null only when the source identity is invalid.
installedVersion is null when the manifest is absent or cannot supply a valid
version. A present invalid manifest is never reported as not-installed.
Version state does not hide ownership drift, preserved-file warnings, locks,
transactions, rollback conflicts, or cleanup residue.
To record that one preserved file has been manually merged:
./doctor.sh --accept-preserved opencode.json
The command displays:
ACK-PRESERVED <path> <target-sha256> <target-mode> <source-sha256> <source-mode>
Paste that exact line to stdin. Hashes are full lowercase SHA-256 values; modes are four octal digits. yes, the path alone, partial values, EOF, or any stale tuple cancels without persistent mutation. A valid acknowledgement updates only manifest baselines and acknowledgement metadata; it never changes file bytes, mode, ownership, or preservation reason. It is rollbackable.
Uninstall
uninstall plans before prompting. It removes only owned-unchanged files and proven-empty directories. Modified, missing, preserved, and unrelated user files survive. --yes skips the confirmation only; it changes no ownership rule. The active manifest is removed after the planned file removals commit, while .oak/rollback/ remains for one rollback.
Rollback and interrupted recovery
rollback validates the current manifest presence/digest and every operation after-state before inverse writes. It restores exact previous bytes and modes, or only manifest ownership metadata for adoption, preservation, acknowledgement, and obsolete-preserved cleanup. A later user edit blocks rollback.
When .oak/transaction.json records an interrupted forward operation or rollback, rollback resumes its deterministic recovery instead of selecting another point. Backups for interrupted forward recovery are resolved only below rollback.next/; committed rollback uses only rollback/. A stale lock is replaced only when it matches the recoverable journal and its PID is no longer alive.
The manager retains one committed rollback generation. Post-commit rollback.next/ or rollback.previous/ directories are cleanup residue, never additional rollback choices.
Do not delete or edit .oak manually. Rollback backups may contain exact prior configuration bytes, including sensitive values, and are created with restrictive permissions.
An unpublished experimental manifest with schema_version: 1 but no
kit_version has no automatic migration: doctor reports invalid state and
mutations fail closed. A developer repairing that experimental state must first
back up the complete target, inspect the exact .oak path and its recovery
artifacts, and only then explicitly remove that experimental .oak state and
reinstall. Never authorize broad deletion or invent a version for an invalid
manifest.
An older installation with no ownership manifest follows the normal initial
install path: run install --dry-run, review every collision, back up the
target, and use install --force only for exact regular-file replacements that
you intentionally want the kit to own.
After installation
(cd "${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}" && npm ci --ignore-scripts)
opencode auth login
opencode models openai --refresh
Use npm ci --ignore-scripts only when the target's shipped package.json and
package-lock.json remain intact. If either protected manifest was preserved
because it differs, review and merge required dependencies into the user-owned
manifests before choosing an install command; the kit lockfile does not control
that target.
If a protected opencode.json or tui.json was preserved, merge the relevant plugin entries manually and use doctor --accept-preserved only after reviewing the complete tuple.