Tool Packs and Installed Assets
This page covers CLI commands that manage bundled OAT tool packs and installed OAT skill/agent assets in canonical directories.
Quick Look
- What it does: explains how bundled OAT packs are installed, updated, inspected, and removed.
- When to use it: when you need to add capabilities to a repo, update installed skills, or understand which packs own which tools.
- Primary commands:
oat tools list,oat tools has,oat tools install,oat tools update,oat tools remove,oat tools migrate - Coming from an earlier CLI: read Upgrading from an earlier CLI for the changed install-scope default, PJM adoption gating, sparse
toolsconfig map, and per-pack--jsonshape
Bundled packs at a glance
| Pack | What it provides | Allowed scopes | Fresh-install default |
|---|---|---|---|
core | Foundational diagnostics and docs access (oat-doctor, oat-docs) plus the bundled documentation tree | user | user |
docs | Docs and agent-instructions governance workflows, docs-app scaffold templates, and the shared tracking-resolution script | project, user | user |
workflows | Project lifecycle skills, wave-program orchestration (oat-wave-program, oat-wave-execute), the project subagent-dispatch adapter, wrap-up reporting, reviewer agents, project templates, and helper scripts | project, user | user |
ideas | Lightweight ideation and promotion flows plus idea templates | project, user | user |
utility | Review and repo-maintenance helpers plus portable subagent-selection guidance and the provider-neutral dispatch engine | project, user | user |
project-management | File-backed backlog/reference skills plus backlog, roadmap, current-state, file-per-record decision, and AGENTS-guide templates | project, user | user |
research | Recon evidence packets, research, analysis, comparison, and synthesis skills plus the recon-worker and skeptical-evaluator agents | project, user | user |
brainstorm | Always-on brainstorming entry point with visual companion | project, user | user |
Every reusable pack is a complete user-scope capability. On a fresh install
each pack lands at user scope by default, so capabilities follow you across
repositories. core is the one pack that is user-only: passing a conflicting
explicit --scope project is rejected rather than silently ignored.
On a re-install, existing placement wins. A pack already installed at
project scope stays at project scope; the fresh-install default never migrates
an existing install. Use oat tools migrate when you
actually want to move one.
Orchestration guidance and dispatch dependencies
The utility pack ships the reusable orchestration pair:
subagent-orchestrationowns durable task classes, model-selection principles, dated provider selection matrices, and refresh evidence.oat-dispatch-subagentsowns capability checks, live catalog intersection, authorized routes, launch acceptance, recovery, and dispatch records.
Provider-aware callers load the generic principles plus exactly one provider selection reference, then the dispatch contract plus exactly one matching provider mechanics reference. The first reference answers which candidates satisfy the task-class floor; the second answers how the active provider can launch them. Root callers retain classification, selection judgment, cross-lane synthesis, and final authority.
Custom utility selection has a directional dependency: choosing
oat-dispatch-subagents automatically includes subagent-orchestration, while
the self-contained guidance skill may be installed alone. Full utility-pack
update and removal treat both as explicit members. Removing either skill by
name does not implicitly remove the other.
The OAT lifecycle adapter intentionally lives in a separate pack:
workflowsownsoat-project-dispatch-subagents, the adapter that adds OAT project lifecycle context without duplicating provider mechanics.
Install both packs when project lifecycle skills need the adapter:
oat tools install utility
oat tools install workflowsThe two packs do not need to share a scope. For example, a repository can keep the lifecycle adapter at project scope while reusing the utility contracts from user scope:
oat tools install workflows --scope project
oat tools install utility --scope userLifecycle skills resolve each required sibling independently from the loaded scope, then user scope, then project scope. This makes mixed-scope placement a supported execution path rather than relying on one frozen skills root.
Materialized agents from any pack that ships them (workflows here,
research elsewhere) use a two-step order instead — user scope, then project
scope — because no provider exposes a stable loaded-agent source directory to
derive a loaded scope from. They still bind each dependency independently and
still fail closed with the owning pack's install or update command. See
Writing Skills → Portable sibling reads
for the full contract.
Skills that read a fixed canonical workflows role use a different, local
${AGENT_PROVIDER_ROOT} binding. They try the loaded skill's provider root,
then ${HOME}/.agents, then the repository's .agents root. The loaded tier
qualifies only when the exact unsuffixed agents/<canonical-name>.md target is
the same-scope canonical Markdown file or a symlink resolving exactly to it.
Copies, broken or escaping links, Cursor model/effort variants, and Codex TOML
roles are misses; resolution continues to the next tier.
That binding only locates canonical source instructions for the one owning dependency. It does not select a provider, model, effort, variant, route, or native agent. Resolve simultaneous pack dependencies independently. If every workflows candidate misses, stop before fallback dispatch and report the intended-scope recovery explicitly:
oat tools install workflows --scope <user|project>
oat tools update --pack workflows --scope <user|project>The packs remain independently installable. If the workflows adapter is present without the utility contracts, it fails closed and reports the missing dependency instead of inventing a fallback route. Non-project analytical skills can use the utility guidance and dispatch layers directly.
Research-pack dispatch dependencies
The research pack owns recon and recon-worker, but the utility pack retains
exclusive ownership of the two reusable dispatch skills that recon needs:
oat-dispatch-subagents; andsubagent-orchestration.
Installing research acquires those two utility assets at the same scope. OAT
records the research request as direct intent (tools.research: true) and a
transitive lease under tools.requiredBy.utility, which includes research.
The lease makes the selected utility assets available without pretending that
research owns them or that the entire utility pack was directly installed.
Direct utility intent and transitive leases are independent. If
tools.utility: true already exists, removing research releases only the
research lease and keeps the direct utility installation. When several packs
require utility, each name remains in the sorted, deduplicated requiredBy
list; dependency assets are removed only after no direct intent and no other
lease retains them.
Dependency lifecycle follows the root pack:
- install and update reconcile the declared dependency assets idempotently at the root pack's scope;
- migration acquires and verifies destination leases before releasing source leases, then removes the source only after the destination is complete; and
- removal releases the root pack's lease, removes only dependency assets with no remaining owner, and prunes provider views only for canonical assets that were actually removed.
Use explicit scopes when installing or updating recon's pack:
oat tools install research --scope user
oat tools update --pack research --scope user
oat tools install research --scope project
oat tools update --pack research --scope projectCross-pack explainer dependency
The public explainer family also spans two packs:
utilityownsexplainer-kit, the destination-neutral core with its contracts, recipes, themes, templates, render QA, durability verifier, and optional publishing connector.workflowsownsoat-explainer-kit, the adapter that resolves OAT config, project artifacts, output paths, and lifecycle intent.
Install the core at user scope before using the adapter:
oat tools install utility --scope user
oat tools install workflowsThe dependency is one-way: the adapter invokes the core, while the core remains usable without OAT. The adapter checks the installed canonical core path and minimum compatible version before reading config or running. If the core is missing it fails closed with the utility install command; if it is too old it reports:
oat tools update --pack utility --scope userIt never falls back to a source checkout or copies core logic into the workflows pack. See Explainer Kit for the usage and lifecycle contract.
Scope, intent, and inventory
Two independent facts describe a pack:
- Intent is why a pack is present. Direct intent is stored per scope as
tools.<pack>: true; transitive dependency intent is stored as a sorted, deduplicatedtools.requiredBy.<pack>list. Both live in that scope's config file (.oat/config.jsonfor project scope,~/.oat/config.jsonfor user scope). Direct intent remains true-or-absent: removing a direct pack deletes its key rather than writingfalse. - Inventory is what is actually on disk right now: every managed asset the release manifest declares for that pack and scope, its presence, and its version or content digest.
Versioned skills and agents must match on both version metadata and canonical content. A same-version content change is therefore reported as outdated rather than current. Content comparison ignores the executable-bit normalization OAT intentionally applies while materializing managed scripts, but still detects file content, entry-type, and symlink-target changes.
Commands report both. oat tools list and oat tools info show placement,
intent source, and completeness. oat status and oat doctor report drift
between intent and inventory with a scoped recovery command.
Requested scope versus realized placement
Intent and inventory are reported as separate, source-qualified facts, so a
pack you declared but never installed is never rendered as installed. The
interactive picker labels each pack from realized placement — the scopes
where managed assets actually exist on disk — not from declared tools.*
intent. A pack declared at project scope with nothing materialized shows as
not installed, which is the honest answer.
Installation outcomes carry realizedPlacement alongside the requested scope,
so JSON consumers can tell the two apart. Selecting User scope installs at
user scope only; it never silently widens to project + user.
One caveat worth knowing: oat tools list and oat tools info human output,
and oat status JSON, still print a placement field derived from
declaration rather than realization. For a declared-but-absent pack that field
reads project even though nothing is installed. Prefer the picker labels and
realizedPlacement when you need the truthful answer. Retiring the
declaration-derived field is tracked in the repository backlog.
A pack is complete at a scope when every managed asset the current release declares for that scope is present, partial when only some are, and absent when none are.
Three independent repository choices
Tool-pack setup separates three decisions that do not imply one another:
- Capability placement chooses whether pack assets live at project scope,
user scope, or both. Placement controls where the capability is available;
it does not authorize a repository
AGENTS.mdedit. - Project guidance is an explicit choice to create an absent root
AGENTS.mdor print a manual patch for its managedOAT toolssection. Use--project-guidanceto accept or--no-project-guidanceto decline onoat init --setup,oat init tools, andoat tools installflows. The interactive prompt defaults to decline. Non-interactive runs perform no guidance write unless--project-guidanceis present and report the exact opt-in command instead. WhenAGENTS.mdalready exists or is a contained symlink, accepted guidance performs zero writes and prints the same repository-relative, copy-pasteable managed block on every run. The patch describes the complete realized project-and-user pack inventory and tells the operator to remove a legacyOAT workflowsblock manually when needed. - PJM adoption is the repository decision to use project-management state.
Make it separately with
oat pjm init; neither installing theproject-managementpack nor accepting tool guidance adopts PJM.
These choices stay independent for standalone workflows installs too. For
example, oat init tools workflows --scope user --project-guidance installs
the workflows capability at user scope while updating guidance in the current
repository. A blocked guidance update is reported separately and does not
undo or misreport the successful capability install.
Where pack assets land
| Asset kind | Project scope | User scope | Ownership |
|---|---|---|---|
| Skill | .agents/skills/<name>/ | ~/.agents/skills/<name>/ | Managed — updated and removed by OAT |
| Agent | .agents/agents/<name>.md | ~/.agents/agents/<name>.md | Managed; provider projection is capability-driven |
| Template | .oat/templates/<name> | ~/.oat/templates/<name> | Seeded once at project scope; managed at user scope |
| Script | .oat/scripts/<name> | ~/.oat/scripts/<name> | Managed, executable |
| Bundled docs tree | not applicable | ~/.oat/docs/ | Managed directory (core pack) |
| Seed file | .oat/ideas/…, .oat/projects-root, .oat/config.json, project .gitkeep files | ~/.oat/ideas/… | Seeded once, then owned by you |
Seed files default to both scopes, so oat tools install ideas --scope user
creates ~/.oat/ideas/backlog.md and ~/.oat/ideas/scratchpad.md. Only the
workflows project-scaffold seeds — .oat/projects-root, .oat/config.json,
and the project .gitkeep files — are pinned to project scope.
User-scope agent projection is provider-capability driven
Canonical agents are installed at either scope. User sync scans them when an active provider declares user-agent capability. Explicit enablement wins without filesystem detection, explicit disablement wins despite detection, and an unset adapter follows detection. Provider mappings and registered materialization extensions then determine the output: entry sync, native read, extension-owned role materialization, or explicit unsupported evidence.
Core sync does not write extension-owned role paths. Codex and Cursor own their generated managed-role outputs through provider extensions, while other user agents continue through each active adapter's declared mapping. Per-asset sync results distinguish changed, current, missing, failed, unsupported, and unknown outcomes, so one failed or unsupported asset does not erase evidence for its siblings.
Materialization is not runtime visibility. oat status and oat doctor report
the registered refresh policy separately and say not-reported when they did
not inspect a running provider catalog. A provider file being current therefore
does not mean the active session is proven to see it.
After a successful provider-visible file change, OAT conservatively advises starting a new provider session so it has an opportunity to load the changed asset. This repository policy is not a provider hot-reload guarantee, an application-process restart requirement, or proof of visibility. No advice is emitted for current/no-op, planned-only, failed, missing, inactive, or unsupported materialization.
Every pack lifecycle outcome (install, update, remove, and the aggregate
init path) and every pack inventory surface (oat tools list, oat tools info, oat status, oat doctor) carries per-provider reachability evidence
in its JSON, derived from the same config-aware registry the sync engine uses:
activation, content capability, projection, materialization, catalog-refresh
visibility, and recovery guidance. Human output names the provider on its own
line and on any provider-attributed diagnostic. Reachability is never inferred
from the presence of a provider directory on disk.
The four inventory surfaces agree on managed user-scope roles. list and
info resolve the same provider surface as status and doctor, so an
active Codex or Cursor adapter that supplies managed roles suppresses the
unmaterialized-user-agent report on all four rather than only two.
Each provider state maps to exactly one row below. Pack evidence status and the lifecycle exit code derive from the severity column, never from the code name, so adding a code later cannot silently change an exit code.
| Provider state (per provider, scope, content kind) | Diagnostic code | Severity | Pack evidence status | Lifecycle outcome / exit code |
|---|---|---|---|---|
| Active, supported, projection and materialization succeeded | none | -- | ok | complete / 0 |
| Explicitly disabled in sync config for this scope | provider-inactive | info | ok | complete / 0 |
| Active but the content kind is unsupported by the adapter | provider-unsupported | info | ok | complete / 0 |
| Active, supported, no projection exists (never synced or sync skipped) | provider-materialization-missing | warning | partial | complete / 0 (install succeeded; sync advised) |
| Active, supported, the sync operation for this asset failed | provider-materialization-failed | error | partial | partial / 1 |
| Active, projected, no sourced refresh contract for the host | visibility-unknown | info | ok | complete / 0 |
| Active, projected, host catalog needs a refresh or a restart | refresh-required / restart-required | info | ok | complete / 0 |
Read-only inventory (list, info, status, doctor): no sync ran | registry-derived codes only | as above | as above | not applicable |
| Auto-sync not run (disabled or skipped) after a lifecycle operation | none; providerSync.status: 'not-run' | -- | ok | complete / 0 |
Rows with info severity are visible in JSON and in the human provider line
but do not turn ok into partial. A provider that was never detected and
never configured is not reported per pack: its inactive state is still carried
on the evidence row, but emitting a diagnostic for every registered provider on
every pack would bury the actionable rows. unknown visibility is reported as
unknown; it never claims reachability and never fails an otherwise successful
install. A read-only inventory surface ran no sync, so it never reports
provider-materialization-failed.
The three visibility codes — visibility-unknown, refresh-required, and
restart-required — are lifecycle-only for the same reason. They require an
established projection, and a read-only surface never establishes one: it
observed no sync, so it reports materialization as a non-claim rather than
asserting the provider view exists. For an active provider that supports the
content kind, the registered catalog state remains readable in the row's
visibility field on every surface; only the diagnostics are withheld. Run
oat tools install, update, or remove to see them emitted. An inactive or
unsupported row reports not-applicable visibility instead, matching its
not-applicable projection: catalog advice about a provider that cannot
receive the content would read as a step to act on.
Repository templates under .oat/templates/ are owner-owned seeds. OAT
compares a source-backed seed with its bundled default: a byte-equivalent copy
is reported as current, while an edited copy is retained and reported as a
retained-override. OAT will not rewrite the retained copy. Delete a repository
template only when you intentionally want the bundled default seeded again;
never delete edited mutable seed content merely to clear the diagnostic.
Evolving pack membership
Packs evolve between releases. A pack that is installed at a scope is reconciled against the current release membership, not the membership it had when you first installed it:
oat tools update --pack <pack>andoat tools update --alladd newly bundled skills, agents, templates, and scripts to the scopes where the pack is already installed.- If every managed asset went missing but intent is still declared, update repairs the pack from intent rather than treating it as uninstalled.
- Identical non-versioned assets are compared by content digest, so a refresh that changes nothing performs no write.
Legacy false intent
Older repositories may still carry tools.<pack>: false in shared config.
That value is not authoritative and is never treated as an opt-out. When
managed assets exist alongside a legacy false, OAT reports a
legacy-false-conflict diagnostic and points at the scoped update command that
adopts the install. Removing the pack deletes the key entirely.
Duplicate cross-scope installs
A pack installed at both project and user scope is a legitimate state, not an
error. OAT reports it as a duplicate-scope diagnostic with the affected paths
and installed versions, and does not infer provider precedence — which copy
a given host loads is the host's behavior, not OAT's. Resolve it explicitly
with oat tools migrate when you want a single owner.
oat tools command group
The oat tools command group provides a unified interface for managing installed tools (skills and agents) across scopes.
CLI updates before bundled-tool mutations
Tool packs ship inside the OAT CLI package. An older CLI can therefore install or update only the tool versions in its own bundle, while a newer stable CLI release may contain newer bundled versions. This is a bundle-freshness warning, not a claim that tools installed by the current CLI are immediately incompatible with it.
Before an eligible interactive oat init, oat tools install (including pack subcommands), or oat tools update mutation, OAT checks the cached stable CLI availability. If a newer version is known, it explains the bundle difference and offers, with a default answer of no, to install that exact validated version:
npm install --global @open-agent-toolkit/cli@<validated-version>- Accepting updates the CLI package only, stops before changing bundled tools, and asks you to rerun the original command under the new CLI.
- Declining or aborting warns that the current bundle may be older, then continues the requested command.
- If npm fails, OAT does not run the tool mutation and reports how to retry the CLI installation.
- Dry-run, JSON, non-interactive, opted-out, CI, test, source-development, and ephemeral package-runner invocations do not prompt or install.
Set NO_UPDATE_NOTIFIER to a truthy value (for example, 1, true, yes, or on) to suppress checks for one process; empty, 0, and false do not suppress them.
Other eligible commands keep the passive update notice and never launch the installer.
Install vs. initialize
The project-management pack separates capability from repository
adoption, and the two are tracked independently:
- Install (capability):
oat tools install project-management(or the legacyoat init tools project-management) installs the PJM skills and template sources. Like every other pack it defaults to user scope on a fresh install, so~/.agents/skills/and~/.oat/templates/receive the managed assets and no repository file is touched. - Initialize (adoption):
oat pjm initinstantiates the two-layer working repo-reference surface under.oat/repo/, creates repository guidance only when the rootAGENTS.mdis absent, and records explicit adoption in.oat/config.jsonaspjm.initialized: truewith apjm.schemaVersion. When the root file or a contained symlink already exists, adoption completes independently and OAT prints a manual project-management/decision patch without changing that file.
Having the pack installed does not mean this repository uses PJM. Adoption
is a per-repository decision recorded by oat pjm init:
oat pjm initThe canonical surface splits active operational state (under pjm/) from durable append-mostly references (under reference/). The command creates missing files and directories for:
.oat/repo/pjm/current-state.md.oat/repo/pjm/roadmap.md.oat/repo/pjm/backlog/.oat/repo/reference/decisions/(file-per-record decisions plus a generated index)- AGENTS guides at
.oat/repo/AGENTS.md,.oat/repo/pjm/AGENTS.md, and.oat/repo/reference/AGENTS.md
Decisions are now file-per-record under reference/decisions/ (created and indexed with the oat decision command group), replacing the legacy single decision-record.md. Repos still on the old reference/ layout can migrate with oat pjm migrate.
Migration eligibility is independent of where the project-management pack is
installed. oat pjm migrate resolves the repository's adoption state for
diagnostic context, then inventories recognized legacy sources before writing.
A complete current layout returns already-migrated; recognized legacy input
can be previewed or applied from any adoption state; and a repository with
neither recognized legacy input nor a complete current layout is skipped with
an oat pjm init recovery. --print-prompt only reads the bundled prompt and
does not inspect adoption or modify the repository.
Decision records still require repository PJM adoption. Run oat pjm init first: like every repository-mutating PJM command, oat decision init fails closed in an unadopted repository, writes nothing, and returns oat pjm init as the recovery. Once the repository is adopted, oat decision init scaffolds only the decision surface — the decision directory, generated index, and decision-specific AGENTS guidance — without touching current state, roadmap, or backlog artifacts. It does not require the project-management pack. A missing AGENTS file can be created exclusively; an existing file or symlink is left byte-for-byte unchanged and receives a manual decision-guidance patch. If the pack is installed later, project-management guidance remains a separate managed section so the decision instructions stay independently reusable.
oat pjm init is idempotent and non-destructive. Existing reference docs are skipped and left unchanged, so curated repo state is not overwritten on repeated runs.
Adoption state and the write guard
resolvePjmAdoption() resolves one of four repository states, and every
repository-mutating PJM command checks it before writing anything:
| State | Meaning | Recovery |
|---|---|---|
declared | pjm.initialized: true is present in .oat/config.json | none needed |
inferred-legacy | The full canonical scaffold exists without an explicit marker | none needed |
partial-initialization | Some canonical scaffold files exist but the surface is incomplete | oat pjm init |
none | The repository has not adopted PJM | oat pjm init |
Normal oat pjm, oat backlog, and oat decision mutations fail closed
for partial-initialization and none. They write nothing and return an error
naming the repository path and oat pjm init as the recovery. The explicit
oat pjm migrate command is the bounded exception: recognized legacy sources
authorize that migration regardless of the adoption label. oat backlog init
and oat decision init are no longer alternate adoption paths for the full PJM
surface.
Run oat pjm doctor to inspect an existing surface. It is read-only, reports
missing canonical files, leftover template frontmatter, and
legacy/loose/second-roadmap drift, and accepts --json. Its JSON result carries
an additive adoption object:
{
"adoption": {
"state": "declared",
"repoRoot": "/repo/.oat/repo",
"recovery": null
}
}Consumers that write repository PJM state branch on adoption.state, not on
pack presence. Project-scope oat doctor aggregates the same pjm:* checks and
keys them on adoption state rather than on tools.project-management, so a
repository that adopted PJM with a user-scope pack is diagnosed correctly.
Template precedence
PJM templates resolve through three tiers, first match wins:
- Repository —
.oat/templates/<name>in this repo. An existing repository template is an owner override and is never rewritten by pack updates. - User —
~/.oat/templates/<name>, the managed default thatoat tools update --pack project-management --scope userkeeps current. - Bundle — the templates shipped inside the installed CLI.
Delete a repository template to fall back to the managed user default. If a template is missing from all three tiers, the command errors rather than inventing content.
Useful options:
--repo-root <path>- scaffold a different repo-reference root instead of.oat/repo--json- emit a machine-readable result with created and skipped paths
Backlog scaffolding is delegated to the lower-level oat backlog init helper. Use oat pjm init for the full PJM repo-reference surface, and use oat backlog init directly only when you need to create or repair the backlog sub-surface by itself.
oat tools list
Purpose:
- List all installed tools with version, pack membership, scope, and update status
Key behavior:
- Scans installed skills and agents across project and user scopes
- Displays version, pack (
core,docs,ideas,workflows,utility,project-management,research,brainstorm,custom), and status (current,outdated,newer,not-bundled) - Also reports per-pack placement (
project,user,both,unavailable), intent source (declared,inferred-legacy,none), completeness, missing managed assets, and any inventory diagnostics - Supports
--scopefiltering and--jsonoutput
oat tools outdated
Purpose:
- Show only tools that have available updates (status
outdated)
Key behavior:
- Filters scan results to tools where the installed version is older than the bundled version
- Also reports non-versioned managed assets whose content has drifted from the bundle, and packs that are declared but absent, as repairable
- Displays installed and available versions side by side
- Supports
--scopefiltering and--jsonoutput
oat tools info <name>
Purpose:
- Show detailed information about a single installed tool
Key behavior:
- Displays name, type (skill/agent), version, bundled version, pack, scope, and status
- Reports whether the tool is invocable (for skills) and whether an update is available
- For a skill, adds a read-only provider-view section per active provider and scope: the expected provider path, the view class, and versions where a copy makes them comparable
- Suggests the narrowest safe repair, one
oat sync --scope <concrete>per affected scope and never--scope all - Returns exit code 1 if the tool is not found in any scope
An unknown name keeps the existing Tool '<name>' not found. wording and prints
no provider-view section, so a skill that exists but was never distributed is
never confused with a name the repository does not have.
$ oat tools info probe-skill --scope project
probe-skill
Type: skill
Version: 1.4.2
Pack: custom
Scope: project
Status: not-bundled
Provider views (project):
claude: missing-additive .claude/skills/probe-skill
Canonical skill has never been projected to this view: no manifest entry and nothing at the expected path.
cursor: inactive
Repair: oat sync --scope projectThe section is additive in --json output as providerViews, an array of one
entry per concrete scope where the canonical skill exists. The command reads the
manifest, the sync config, and the filesystem; it never writes, and it never
runs a sync. If the sync config or the manifest cannot be read, that scope's
section reports unavailable with a redacted reason and the tool detail and exit
code are unchanged; a failure reading one provider's view degrades only that row,
to unverified with the same redacted reason. An absent sync config is not a
failure: it resolves to the defaults and the scope is diagnosed normally. See
Manifest and Drift
for the view classes.
oat tools has <pack>
Purpose:
- Check current availability of one bundled pack without treating shared config as a runtime capability cache
Key behavior:
- Defaults to effective availability across project and user scopes
- Accepts
--scope project,--scope user, or--scope all - Availability is complete-only. A scope counts as available only when every
managed asset the current release declares for that scope is present. A
partially installed pack reports
falseso callers never build on a half-installed capability - Prints
trueorfalsein plain mode - With the global
--jsonflag, emits the full availability record:
{
"pack": "docs",
"available": false,
"scopes": [],
"unavailableScopes": [],
"completeness": { "project": "partial", "user": "absent" },
"missing": [
{
"scope": "project",
"asset": "skill:oat-docs-apply",
"path": "/repo/.agents/skills/oat-docs-apply"
}
]
}scopeslists only the scopes where the pack is complete;unavailableScopeslists requested scopes the pack could not be checked in — either because the pack does not allow that scope at all (for exampleprojectforcore) or because the scope could not be resolved here;completenessandmissingexplain afalseresult so it is actionable- Outside a Git repository, the default
--scope allquery still answers from user scope and listsprojectinunavailableScopesinstead of failing. An explicit--scope projectoutside a repository is still a hard failure - Exits
0for every valid query, including an unavailable pack,1for an invalid pack or other actionable input error, and2for an unexpected scan or runtime failure oat tools has <pack>answers capability availability, not repository adoption. Forproject-managementspecifically, useoat pjm doctor --jsonto learn whether this repository has adopted PJM
Examples:
oat tools has project-management
oat tools has brainstorm --scope user
oat --json tools has workflowsoat tools install
Purpose:
- Install bundled OAT tool packs (
core,docs,ideas,workflows,utility,project-management,research,brainstorm)
Key behavior:
- Same pack selection and install flow as
oat init tools - Pack-oriented install subcommands:
core,docs,ideas,workflows,utility,project-management,research,brainstorm - Interactive installs show each pack's current install location in the picker so already-installed packs are visible before you submit
- Installing is additive: choosing a scope for a pack never removes it from another scope. A pack installed at user scope plus a project install ends up at
project + user, not moved - For every pack that allows both scopes (
ideas,docs,workflows,utility,project-management,research,brainstorm), the interactive flow offers a per-pack end-state selector (project,user, orboth) defaulting to the pack's current placement; leaving the default makes no changes for that pack - Fresh installs default to user scope for every pack; existing installs keep their current placement
oat init --setupuses this same additive scope resolver. In guided setup, choosing to customize scope reaches the per-pack selector; choosing the recommended defaults, or running non-interactively, applies additive per-pack defaults without removals- Every pack's fresh-install default is user scope (
defaultScopein the release manifest); existing installs keep their current placement on re-install, so a re-install never moves a pack between scopes or creates a second copy at the other one. This applies to the per-pack subcommands too: a bareoat tools install docsin a repository wheredocsis already at project scope stays at project scope, and only an explicit--scopeoverrides that - Removing a pack from a scope happens only when you explicitly choose a narrower end-state in the interactive flow (e.g. a pack at
bothset toprojectonly). All staged removals are shown in a single change summary and applied only after one batch confirmation — declining makes no changes - Non-interactive installs (including
--scope project,--scope user, and the default pack set) are strictly additive and never remove a pack from a scope. Removal is interactive-only - Tracks installed versus bundled skill and agent versions and canonical content, reporting same-version content drift as outdated while ignoring expected executable-bit normalization
- Writes pack intent into the config file for the scope you installed into: a project install sets
tools.<pack>: truein.oat/config.json, and a user install sets it in~/.oat/config.json. A user-only install never writes repository config - When a project install discovers existing project-scoped packs whose intent
is missing, it writes those exact intents in canonical pack order. Human
output prints
Adopted project tool pack: <pack>once per adopted pack; JSON output addsadoptedPacksonly when the list is non-empty. This is pack intent reconciliation, not repository PJM adoption - A user-only capability install needs no Git repository and performs no repository writes unless
--project-guidanceexplicitly requests the separate repository guidance update - Offers repository
AGENTS.mdguidance independently of capability scope. Pass--project-guidanceto create an absent file or print a manual managedOAT toolspatch for an existing file/symlink, or--no-project-guidanceto decline; the interactive prompt defaults to decline and non-interactive runs write nothing without the explicit opt-in - Repository
AGENTS.mdguidance for project management is owned by adoption, not by pack placement. Installing theproject-managementpack never writes the section.oat pjm initcreates guidance only when the root file is absent; otherwise it completes scaffold/adoption and prints a manual patch without changing the existing file or symlink - Interactive runs can prompt to update selected outdated skills
- Successful installs report the final scope chosen for each pack, including
project + userwhen a pack is installed in both, and auto-sync only the scopes actually changed by the install so untouched scopes are never re-synced or pruned - Install-triggered auto-sync limits removal planning to the canonical entries from the pack that was just installed, so stale manifest drift in unrelated packs does not delete other provider views
- Use
--no-syncto skip auto-sync
oat tools update
Purpose:
- Update installed tools to the latest bundled versions
Key behavior:
- Accepts a tool name,
--pack <pack>, or--all(mutually exclusive) - With no target, exits without mutation and suggests the explicit bulk command
oat tools update --all; invalid packs and conflicting targets keep their own targeted diagnostics - Compares installed versions and canonical content against bundled assets and copies updates; equal version metadata does not hide content drift
- For
--pack <pack>and--all, an already-installed pack is reconciled to the current release membership in the scopes where it is installed, adding newly bundled skills, agents, templates, and scripts - Pack dependencies are reconciled at the same scope without converting a transitive-only dependency into direct pack intent
- A pack whose managed assets are all missing but whose intent is still declared is repaired from intent rather than skipped
- Managed template and script companions are refreshed at user scope. A project-scope repository template is an owner override and is left alone
- Non-versioned assets are compared by content digest, so an identical refresh is a no-op instead of a rewrite
- Update persists inferred legacy intent only after a successful explicit mutation, so a read never rewrites your config
- A successful project-scope update reports the exact newly adopted legacy pack
intents in canonical order. Human output uses
Adopted project tool pack: <pack>; JSON output adds the optionaladoptedPacksarray only when at least one intent was written --scope alloutside a Git repository completes the user-scope work instead of failing. Unlikeoat statusandoat doctor, update skips project scope silently and does not name it in the result; an explicitly requested--scope projectoutside a repository is still a hard failure- Dry-run mode with
--dry-run; auto-sync after mutations by default - Use
--no-syncto skip auto-sync - Reports tools that are already current, newer than bundled, or not bundled (custom)
oat tools remove
Purpose:
- Remove installed tools (skills and agents)
Key behavior:
- Accepts a tool name,
--pack <pack>, or--all(mutually exclusive) - Removal is planned from the release manifest, so it deletes only assets OAT manages for that pack and scope
- Retained by design: project-scope repository template overrides, mutable seed files (
.oat/ideas/…,.oat/projects-root, project.gitkeepfiles), and shared scripts still owned by another installed pack. Retained owner data is reported rather than silently skipped - Already-missing files are not an error
- On success the pack's intent key is deleted from that scope's config file. It is never rewritten as
false. A failed removal retains the key - A removal that finds no trace of a pack at a scope leaves that pack's intent alone.
No tools to remove.means no durable state changed either, so--allin a repository whose packs are already gone will not quietly rewrite.oat/config.jsonor destroy the intentoat tools updaterestores a fully-missing pack from - In
--jsonoutput, the additivepackOutcomesarray reportspack,scope, andremovedfor each attempted pack/scope pair. Automation should useremoved: trueas the evidence that removal acted on that pack and its scoped intent was eligible to be cleared;removed: falseis a no-op and preserves intent. - Removal-triggered sync prunes exactly the canonical provider views for the removed paths in that scope, leaving other scopes and packs untouched
- Removing a root pack releases its
requiredByleases. A dependency asset is retained while its owning pack has direct intent or another pack still requires that asset - Dry-run mode with
--dry-run; auto-sync after mutations by default - Use
--no-syncto skip auto-sync
oat tools migrate
Purpose:
- Move one installed pack between scopes with a preview, a verified destination, and an explicitly confirmed source removal
Key behavior:
- Requires
--pack <pack>,--from <scope>, and--to <scope>; source and destination must differ - Always previews first. The preview lists additions, duplicates, conflicts, planned source removals, retained owner data, and any inventory diagnostics
--dry-runstops after the preview and exits1when the migration is blocked by destination conflicts- Destination first, source second. The destination is installed and re-inventoried, and destination intent is written only after the pack is verified complete there. Only then is source removal offered
- Dependency leases follow the same order: acquire and verify destination leases before releasing source leases, so a failed move remains retryable
- Source removal requires an interactive confirmation. Declining leaves the pack installed at both scopes with the destination verified — a safe, recoverable state, not a failure. Non-interactive runs stop at the same point
- If source removal or its sync fails partway, the destination is kept, source intent is retained, and structured recovery instructions are printed. Rerun the same command to finish
- There is no force flag: a blocked migration is resolved by fixing the conflict, not by bypassing verification
- With
--json, emits{ "operation": "migrate", "dryRun": <boolean>, "preview": …, "status": …, "recovery": [...] }.statusis one ofpreviewed,migrated,retained-both,blocked,destination-sync-failed,source-removal-failed, orsource-sync-failed
oat tools migrate --pack docs --from project --to user --dry-run
oat tools migrate --pack docs --from project --to userTo roll a migration back, run it in the opposite direction. Because the destination is always verified before the source is touched, a rollback is the same safe two-phase operation.
Pack intent: tools.<pack> and tools.requiredBy
Pack intent is recorded per scope, in that scope's own config file:
| Scope | Config file | Key |
|---|---|---|
| project | .oat/config.json | tools.<pack>: true |
| user | ~/.oat/config.json | tools.<pack>: true |
- Intent is true-or-absent. Installing writes
truefor the scope you installed into; removing deletes the key. OAT never writesfalse. tools.requiredBy.<pack>is a sorted list of packs that currently lease selected assets from that pack at the same scope. It is transitive intent, not a substitute fortools.<pack>: true.- A user-scope install never writes repository config, and a project-scope install never writes user config.
- Intent for one pack is never derived from another pack's key, and no
command writes the full eight-pack map. Dependency operations update only
the owning pack's
requiredByentry. .oat/config.local.jsonis not an intent surface; per-developer state stays out of pack intent.- A manual
oat config set tools.<pack> trueis an override that the next lifecycle mutation for that scope may replace with the reconciled value.
Use oat config get tools.<pack> to inspect declared intent, and oat tools list, oat tools info <name>, oat status, or oat doctor to inspect what is
actually installed.
Use oat tools has <pack> when a workflow needs current effective capability
availability across project and user scope. Availability is a capability
question; it is not evidence that the current repository adopted anything.
Workflows that write repository PJM state check oat pjm doctor --json for
adoption instead.
Workflows pack
The workflows pack is installable at project scope, user scope, or both, and defaults to user scope on a fresh install. A user-scope install carries the complete versioned asset set:
- skills and agents →
~/.agents/skills/and~/.agents/agents/; - templates →
~/.oat/templates/; - executable helper scripts →
~/.oat/scripts/.
Use either the direct or aggregate form:
oat tools install workflows --scope user
oat init tools workflows --scope userUser scope intentionally skips project-root scaffolding: it does not create a
home-level .oat/projects-root, .oat/projects/ tree, or projects-root config.
Projects remain anchored in the target repository. Project-scope installation
retains its existing scaffolding and local-path setup.
oat tools update --pack workflows --scope user refreshes all four asset
classes in place, including newly bundled skills and agents. Pack removal and a
confirmed aggregate scope reduction remove those same user-level assets.
Companion template removal is user-scope-only; project-scope removal leaves
repo-local .oat/templates/ overrides and mutable project seeds intact. The
shared resolve-tracking.sh script is co-owned by the docs and workflows
packs and is retained while either pack still owns it in that scope.
Core pack
The core pack contains foundational diagnostic and documentation skills:
- oat-doctor — Setup diagnostics with two modes: check mode (terse
brew doctor-style warnings with fix commands) and summary mode (full dashboard of installed packs, config values, and sync status). - oat-docs — Interactive Q&A skill backed by locally-bundled OAT documentation at
~/.oat/docs/.
Key behavior:
- Core pack always installs at user scope (
~/.agents/skills/). It is the only pack whose sole allowed scope isuser, and passing a conflicting explicit--scope(e.g.oat init tools core --scope project) is rejected with an error rather than silently ignored; omit--scopeor pass--scope user. This ensures core skills are available in any directory. ~/.oat/docs/is a managed directory asset: its completeness and drift are compared as a whole tree, so a partially deleted docs tree is reported as a partialcoreinstall rather than passing silently.- Core is checked by default in the
oat init toolsguided setup. - Installation also bundles OAT documentation to
~/.oat/docs/for the oat-docs skill. oat tools update --pack corerefreshes both skills and~/.oat/docs/documentation.oat tools update --allalso refreshes~/.oat/docs/when an installed core pack is reconciled.
Docs pack
The docs pack contains active documentation and instruction-governance
workflows:
- oat-docs-bootstrap — Guide users through bootstrapping a docs app
end-to-end: preflight detection, input gathering, scaffold (via
oat docs init) with capability-gated post-patches, build verification, config inspection, and an educational walkthrough. - oat-docs-analyze — Analyze a docs surface for contract coverage, nav drift, stale claims, and coverage gaps.
- oat-docs-apply — Apply only approved, evidence-backed docs-analysis recommendations.
- oat-agent-instructions-analyze — Evaluate
AGENTS.mdand provider instruction coverage, quality, and drift. - oat-agent-instructions-apply — Generate or update approved instruction files from an analysis artifact.
Key behavior:
- Docs pack installs at the selected scope and defaults to user scope on a fresh install.
- It complements the
corepack:oat-docsanswers questions from bundled docs, while thedocspack adds analyze/apply workflows. oat tools install docsis the preferred install path;oat init tools docsremains available for backward compatibility.oat tools update --pack docsandoat tools remove --pack docsmanage the workflow skills as a unit.
Brainstorm pack
The brainstorm pack ships a single skill plus a bundled visual companion for
project-independent brainstorming conversations:
- oat-brainstorm — Brainstorming entry point with an explicit activation
contract. The OAT brainstorm banner is a workflow commitment marker, not a
response style — the skill enters mode only on Hard Activation (explicit
brainstormverb: "let's brainstorm", "brainstorm this", "can we brainstorm X", "help me brainstorm X", or/oat-brainstorm). Ambiguous exploratory phrasing ("I've been thinking about", "what if we", "help me think through") follows the Soft Exploratory Path — answered conversationally with brainstorm-quality reasoning (options, tradeoffs, no premature implementation, no destination guess) without the banner. After ≥2 sustained exploratory turns the skill offers mode once: "If you want, I can switch into structured brainstorm mode for this." Advisory / review / debug / PR / status / implementation / active-workflow questions ("thoughts?", "what's your take?", "does this seem right?", "why is this failing?") follow No Activation — direct response, no banner, no offer. Once entered, brainstorm mode runs a structured design conversation (one question at a time, 2-3 approaches with a recommendation) without committing the user to an idea or project artifact, and ends in a pack-aware terminal-state picker that hands off to existing OAT skills (idea capture, scoped backlog item, project promotion, active-project fold-back, doc-to-path) based on which packs are installed in the current repo. Two base outcomes (inline-only and write a brainstorming doc to a user-specified path) are always available regardless of installed packs.
Key behavior:
- Default user scope. The
brainstormpack defaults to user scope so the always-on trigger fires consistently across directories and machines. Every other reusable pack now shares that default; the per-packdefaultScopelives in the release manifest. - Existing-install precedence. Re-running install on a pack that is
already installed at project scope preserves that scope —
defaultScopeonly applies to fresh installs. This protects users from unexpected scope migrations. - Default-on in
oat init.brainstormis checked by default in theoat init toolsguided setup, so new repos get the brainstorming entry point out of the box. - Visual companion. A bundled local browser-based UI (Node-based HTTP +
WebSocket server, content-fragment authoring, per-question terminal-vs-browser
routing) ships with the skill at
.agents/skills/oat-brainstorm/scripts/and is documented in.agents/skills/oat-brainstorm/references/visual-companion.md. The companion is offered only when the topic is visual-likely (mockups, layout comparisons, diagrams, visual option comparisons). Text-likely brainstorms skip the offer and can surface it later if the conversation turns visual. Persistence paths use OAT-managed prefixes (.oat/brainstorm/<session-id>/repo-scope or~/.oat/brainstorm/<session-id>/user-scope). - Terminal-state picker filtered by effective pack availability. When the
user converges on a destination, the skill runs
oat tools has ideas,oat tools has project-management, andoat tools has workflowsto filter the available terminal states. Pack-gated outcomes (capture-as-idea, scoped backlog item, project promotion, active-project fold-back) appear when the corresponding pack is available at project or user scope. The separateoat config get activeProjectlookup resolves active project state. - Destinations playbook. The full set of terminal-state stanzas — trigger
phrases, required template fields, confirmation patterns, handoff targets —
lives at
.agents/skills/oat-brainstorm/references/destinations.mdand is consulted by the skill at destination-identification time. - Pack lifecycle.
oat tools install brainstorm,oat tools update --pack brainstorm, andoat tools remove --pack brainstormmanage the skill plus visual-companion bundle as a unit. A project install recordstools.brainstorm: truein.oat/config.json; a user install records it in~/.oat/config.json. Either placement satisfiesoat tools has brainstorm.
Auto-sync behavior
All mutation commands (install, update, remove, migrate) automatically run oat sync --scope <scope> after successful operations. This reconciles provider files without manual intervention; it does not prove that an already-running provider catalog has loaded them.
Use --no-sync on any mutation command to skip this step.
For oat tools install, the follow-up sync still reconciles provider views immediately, but its removal pass is scoped to the canonical entries that were just installed. New-session advice is shown only when a registered provider/content policy applies to a successful current-run change. It gives the provider an opportunity to load the changed asset; it does not tell you to restart the application process or prove runtime visibility. This avoids deleting unrelated provider views when a worktree has stale manifest entries for packs whose canonical content is absent locally.
Removal and migration use the symmetric contract: the follow-up sync prunes exactly the provider views for the canonical paths that were removed, in that scope only, and only after the canonical source is confirmed absent.
Upgrading from an earlier CLI
User-scope tool packs and explicit PJM adoption changed several defaults and
exit codes. Nothing here is data-destructive, and legacy config keeps working —
tools.<pack>: false still parses, complete pre-adoption PJM scaffolds are
grandfathered as inferred-legacy, and existing installs remain additive. One
inert per-pack install flag was removed, and several commands you already run
behave differently.
Install scope defaults flipped to user
Every pack's fresh-install default is now user scope; it used to be project
scope. A fresh oat tools install docs in a repository therefore lands in
~/.agents/ rather than the repository.
Existing installs are not moved: both the aggregate installer and the per-pack
subcommands resolve an already-installed pack to its current placement, so a
re-install neither migrates the pack nor creates a second copy at the other
scope. An explicit --scope still wins over that. Use
oat tools migrate when you actually want to move a pack.
If you already have a pack at both scopes, oat doctor reports it as
duplicate-scope and exits 1. That is a
new non-zero exit for a state the previous release did not diagnose; resolve it
with oat tools migrate, or treat the exit code accordingly in scripts.
PJM commands fail closed without adoption
Repository PJM state now requires explicit adoption via oat pjm init:
oat pjm doctorexits 1 in an unadopted repository. It previously reported a passingpjm:disabledcheck.oat backlog init,oat backlog new,oat backlog regenerate-index,oat backlog archive,oat decision init,oat decision new, andoat decision regenerate-indexexit 1 and write nothing in an unadopted repository, namingoat pjm initas the recovery. Previouslyinitwas an alternate adoption path and the other subcommands were ungated.
Installing the project-management pack does not adopt a repository. Run
oat pjm init once per repository that should carry PJM state.
The tools config map is now sparse
Pack intent is written as
true-or-absent. Earlier
releases wrote all eight pack keys as explicit true/false on reconcile;
current releases write true for installed packs and delete the key otherwise.
Scripts that read tools.<pack> === false now read undefined for a pack that
is not installed. Test for a truthy value instead. Legacy false values that
already exist in a config file are still read (see
Legacy false intent) — they are just never written.
Direct config writes now enforce the same true-or-absent contract.
oat config set tools.<pack> false exits 1 without changing the config. To
remove a project-scoped pack and clear its intent, run:
oat tools remove --pack <pack> --scope projectExisting legacy false values remain readable migration input.
Per-pack install --force was removed
oat tools install <pack> --force is now rejected as an unknown option. The
flag was inert in the unified additive lifecycle. To reconcile installed
assets, use the scoped update command instead:
oat tools update --pack <pack> --scope <scope>Per-pack install --json changed shape
The per-pack subcommands (oat tools install docs, oat tools install workflows, and the rest) now emit one multi-scope payload:
{
"status": "ok",
"pack": "docs",
"scopes": ["user"],
"results": [
{ "request": { "pack": "docs", "scope": "user", "scopeRoot": "/home/you" } }
]
}Each results entry is a full lifecycle result; only its request is shown
above. The previous single-scope payload's scope, targetRoot, assetsRoot,
selectedSkills, and result fields are gone, not renamed. A script
keying on them reads undefined rather than failing. Read scopes for the
scopes acted on, and results[].request.scopeRoot for the target root of each.
When a project-scope aggregate install, direct pack install, or update adopts
missing legacy pack intents, the same single payload also contains
"adoptedPacks": ["docs", "workflows"]. The field is omitted when nothing was
adopted, and entries follow the manifest's canonical pack order. Human output
reports the same set as one Adopted project tool pack: <pack> line per pack.
Other behavior changes worth knowing
oat tools remove --pack <pack>deletes every managed asset the release manifest declares for that pack and scope, including templates and scripts. It previously removed only scanned skills and agents. Project-scope repository template overrides and other owner data are still retained and reported.- A removal that finds no trace of a pack leaves that pack's intent alone, so
No tools to remove.now means no durable state changed either. oat tools info <packname>exits0and prints a pack block. It previously reported "not found" and exited 1 for a pack name.oat tools list,oat tools outdated, andoat tools remove <name>now see user-scope agents, not just skills.oat tools has <pack>is complete-only: a partially installed pack answersfalse. The exit code is unchanged at0.oat tools hasandoat tools update --scope allno longer fail outside a Git repository; seeoat tools hasandoat tools updatefor exactly what each reports.
Legacy commands
oat init tools
The oat init tools command remains available for backward compatibility. It has the same install behavior as oat tools install but does not include auto-sync — you must run oat sync --scope ... manually after install.
oat remove
The oat remove command group remains available for backward compatibility. It provides skill removal with dry-run/apply semantics and managed provider-view cleanup.
oat remove skill <name>— remove one installed skill by nameoat remove skills --pack <pack>— remove all installed skills from a bundled pack; pack membership is derived from the same release manifest theoat toolscommands use
These commands mutate by default; use --dry-run to preview deletions.
Related docs:
- Diagnosing pack drift (
oat status,oat doctor):../reference/troubleshooting.md - Bootstrap (
oat init):bootstrap.md - Provider sync (
oat status,oat sync,oat providers ...):../provider-sync/index.md - Diagnostics and local-state commands:
config-and-local-state.md