Open Agent Toolkit

Troubleshooting

The commands on this page use the installed oat command. A few entries also give a pnpm run ... form; those work only inside a checkout of the OAT source repository and are labelled that way.

Skills not visible in host UI

A skill shows up in your coding tool only when two things are true: the skill is installed (a folder with a SKILL.md under .agents/skills/ in this repository, or under ~/.agents/skills/ in your home directory), and the tool can read it. Some tools, such as Claude Code, read a provider view: a link or copy that OAT creates in the tool's own folder, such as .claude/skills. Others, such as Cursor, read .agents/skills directly.

Diagnose first. These commands only read; they change nothing.

  1. Check whether the skill is installed, and where:

    oat tools info <skill-name>

    It prints the skill's pack, its scope (project for this repository, user for your home directory) and its status, then a "Provider views" block for each scope with one row per coding tool.

    • Tool '<skill-name>' not found. means the skill is not installed at either scope. Install the pack that contains it (see the fixes below), or create the skill at .agents/skills/<skill-name>/SKILL.md. To see every installed skill with its pack and scope, run oat tools list.
    • in-sync on your tool's row means its view of the skill is in place, so go to the last fix step and start a new session.
    • missing-additive, removed or modified means your tool's view is absent or out of date. The block ends with a Repair: line that gives the exact oat sync --scope ... command to run.
    • inactive means that tool is not active at that scope, so OAT creates no view for it. Enable it, as shown in the fixes below.
  2. Check whether the repository's provider views are in sync:

    oat status --scope project

    Each row names a tool and a skill. ✓ in_sync means the view is in place. ✗ missing provider entry missing means the view has not been created yet; oat sync creates it. No managed entries found. means OAT manages no provider views in this repository, which usually means no tool that needs views is enabled. Run oat providers list --scope project to see which tools are enabled or detected.

Then fix, always with an explicit scope:

  • Enable the tool if step 1 or 2 showed it is not active, for example oat providers set --scope project --enabled claude.

  • For skills in this repository (.agents/skills), preview and then regenerate the provider views:

    oat sync --scope project --dry-run
    oat sync --scope project
  • For skills installed at user scope (~/.agents/skills, where a tool pack installs when you give no --scope), run oat sync --scope user. This writes provider views in your home directory, such as ~/.claude/skills.

  • If the skill comes from a pack that is not installed, install that pack with an explicit scope, for example oat tools install research --scope project (this repository) or oat tools install research --scope user (your home directory). If you installed a pack with --no-sync, running the install again does not create the views; run oat sync for that scope instead.

  • Reload the tool or start a new session: most tools read their skill list when a session starts.

  • If your repository's AGENTS.md names skills or packs, check that it matches what oat tools list reports. The <!-- OAT tools --> block that --project-guidance writes lists the installed packs and the folders agents should scan for skills; it does not list each skill.

  • If you are working in the OAT source repository: run pnpm run cli -- sync --scope all, which runs the CLI from source and syncs both project and user scope.

  • In the new worktree, run oat sync --scope project to recreate the project's provider views. Add --dry-run first to preview.
  • To prepare the whole worktree, including local-only files and the repository's setup and readiness checks, invoke /oat-worktree-bootstrap --existing in your coding agent. See Worktree bootstrap.
  • If you are working in the OAT source repository: run pnpm run worktree:init. This command copies local-only files from the main worktree, installs dependencies, configures Git hooks, builds the workspace, and runs oat sync with no scope flag. A bare oat sync uses --scope all, so it refreshes user-scope provider views in your home directory as well as the project's.

Codex appears detected but no skill sync actions are listed

Expected for native-read skill mappings. Codex can read canonical skills without mirrored provider writes.

Cursor appears detected but no skill sync actions are listed

Expected for native-read skill mappings. Verify project skills under .agents/skills and personal skills under ~/.agents/skills; Cursor reads those canonical directories directly. Sync will not create .cursor/skills mirrors.

Use .cursor/skills only for intentionally Cursor-specific packages. Interactive oat init and oat status offer each unresolved Cursor-local skill for canonical adoption or a remembered Keep Cursor-only choice.

Copilot appears detected but no skill sync actions are listed

Expected for native-read skill mappings. Verify project skills under .agents/skills and personal skills under ~/.agents/skills; Copilot reads those canonical directories directly. Sync will not create .github/skills or ~/.copilot/skills mirrors.

Legacy skills in those provider directories remain adoption candidates. Run oat sync --scope <project|user|all> --dry-run before upgrading: verified clean managed views may be removed, while changed or unverifiable paths are preserved and detached from obsolete manifest ownership. Copilot agents and project rules continue to sync to .github/agents, ~/.copilot/agents, and .github/instructions.

Interactive oat init and oat status offer each unresolved legacy skill for canonical adoption or a remembered Keep Copilot-only choice. Keep is blocked when a same-name canonical skill exists; rename one package before retrying.

sync reports provider config mismatch

  • For interactive runs, select detected providers to enable when prompted.
  • For non-interactive runs, configure providers explicitly:
    • oat providers set --scope project --enabled <providers> --disabled <providers>
  • Re-run oat sync --scope project after updating config.

instructions validate reports missing, content_mismatch, stray, or managed_shim

  • Check the effective strategy (strategy: in the output). The default is none, which expects no CLAUDE.md; missing is only reported under a configured shim strategy.
  • Run oat instructions sync --dry-run to preview changes.
  • Under none, managed_shim entries are CLAUDE.md files OAT created; oat instructions sync removes them, unless a CLAUDE.md with its own content blocks removal (see below).
  • To keep shims instead, run oat config set instructions.claude.shims pointer (or symlink / copy) and then oat instructions sync.
  • Under a shim strategy, if mismatched CLAUDE.md files should be overwritten, run oat instructions sync --force.
  • If stray is reported, oat instructions sync will adopt the Claude-only file into AGENTS.md, then regenerate CLAUDE.md under a shim strategy or remove it under none.

instructions sync removed no shims because a CLAUDE.md has content

  • Under none, removal is all or nothing: while any CLAUDE.md, .claude/CLAUDE.md, or CLAUDE.local.md has content of its own, sync removes no CLAUDE.md at all, reports each held-back removal as skipped, and exits 1. The finding (claude_md_blocks_shim_removal under --json) names the files with content (paths) and the CLAUDE.md files kept (wouldRemove); linkedBy names any AGENTS.md link to a file with content, which you replace with that file's content before removing it. oat instructions validate reports the same finding.
  • Either remove each named file or move its content into an AGENTS.md, then rerun oat instructions sync; or set instructions.claude.shims to pointer, symlink, or copy to keep CLAUDE.md files.
  • See Instruction Sync.

instructions sync warns that a CLAUDE.md makes Claude Code ignore every AGENTS.md

  • Under the default none strategy, a remaining CLAUDE.md, .claude/CLAUDE.md, or CLAUDE.local.md makes Claude Code's agents-md plugin stand down: for every session when the file is at the repository root, and for sessions started in its directory or below when it is in a subdirectory.
  • Either remove the named file, or set instructions.claude.shims to a shim strategy and rerun oat instructions sync to add shims everywhere.
  • If the warning names an AGENTS.md that links to the file (linkedBy under --json), that file holds the only copy of those instructions: replace each linking AGENTS.md with the file's content first, then remove the file.
  • Sync never deletes a hand-written or modified CLAUDE.md itself; see Instruction Sync.
  • If a broken or unreadable instruction path is reported, fix the underlying file or symlink target first; sync will intentionally skip manual-repair cases instead of forcing recovery.
  • If a directory you expected to see is missing from the scan, confirm it is not under .git, .oat, .worktrees, or node_modules.
  • Re-run oat instructions validate and confirm status is ok.

Use Instruction Sync for the full strategy matrix and state model.

doctor warns about canonical directories

  • Run oat init for the relevant scope. The check's name says which scope is missing directories: for project:canonical_directories run oat init --scope project, and for user:canonical_directories run oat init --scope user. A bare oat init initializes both.
  • Re-run oat doctor after initialization.

doctor warns about outdated installed OAT skills

  • Run oat tools update --pack <pack> --scope <project|user> to update an installed pack, or oat tools install <pack> --scope <project|user> to install one. The older form oat init tools is a legacy command that still installs or updates the bundled tool packs.
  • In TTY mode, select which outdated skills to update when prompted.
  • In non-interactive mode, run the scoped command doctor prints: oat tools update --pack <pack> --scope <scope>.
  • Per-pack install commands no longer accept the previously inert --force option.

A skill fails with unknown command 'template' or 'closeout-check'

Lifecycle skills from OAT 0.3.11 copy their templates with oat template resolve and check closeout with oat project closeout-check. An older oat on PATH rejects both:

error: unknown command 'template'
error: unknown command 'closeout-check'

oat-project-complete treats a failed closeout check as an incomplete closeout, so with an older CLI it stops and routes back to oat-project-implement even when every closeout step is done. This happens when the skills a host loads are newer than the CLI, for example repository skills from a newer checkout run against an older global CLI.

Check oat --version. Install @open-agent-toolkit/cli 0.3.11 or later, then refresh the installed skills to that CLI's bundle with oat tools update --all.

sync reports an unsafe provider parent

Errors containing Unsafe provider parent, symbolic links are not allowed in provider ancestry, or provider ancestry is not a directory mean an existing parent of a managed provider destination is a symlink or a non-directory entry. OAT refuses to traverse, unlink, or rewrite that parent because it may be user-managed or externally owned. Canonical content and external symlink targets remain untouched.

Recover explicitly:

  1. Inspect the reported parent and preserve or migrate any user-managed content.

  2. Replace that provider parent with a real directory under the intended project or user sync scope.

  3. Rerun sync with the matching scope:

    oat sync --scope project
    oat sync --scope user
    oat sync --scope all

Do not replace the parent until you understand who owns its existing target. OAT intentionally does not automate this recovery.

A user-installed pack is missing from shared tools.*

This is expected. .oat/config.json#tools records project-scope intent only. A user install writes tools.<pack>: true to ~/.oat/config.json instead, and never touches repository config. Since every reusable pack now defaults to user scope on a fresh install, an empty repository tools map is normal.

Check effective project-plus-user availability with:

oat tools has <pack>

To confirm specifically that the user copy is present, run:

oat tools has <pack> --scope user

oat tools has <pack> reports false for a pack I installed

Availability is complete-only: a scope counts only when every managed asset the current release declares for it is present. A partially installed pack — for example one missing a skill added in a newer release — reports false.

Read the reason from the JSON result and repair the named scope:

oat --json tools has <pack>
oat tools update --pack <pack> --scope <project|user>

completeness shows complete, partial, or absent per scope, and missing lists the exact managed assets and paths behind a false.

status or doctor reports a partial or stale pack

Both commands report managed pack state with a scoped recovery command:

  • partial — some managed assets are missing, or intent is declared with none installed. Fix with oat tools update --pack <pack> --scope <scope>.
  • stale — installed assets are behind the bundled release, or a managed skill or agent has the bundled version metadata but different canonical content. Expected executable-bit normalization on managed scripts is ignored. Fix with the same scoped update command.
  • newer — installed assets are ahead of the bundle. This is informational and usually means the CLI is older than the assets; update the CLI rather than downgrading the pack.
  • retained-override — a source-backed owner-owned seed differs from its bundled default, so OAT retains the edited copy instead of overwriting it. A bundle-equal seed is current and does not produce this finding. Retained overrides cover repository templates under .oat/templates/ and seeded content such as .oat/ideas/backlog.md and .oat/ideas/scratchpad.md. Deleting the file is the right move only for a template you intentionally want OAT to seed from the bundle again; never delete seeded content you have edited, because the next install or update reseeds the bundled default in its place and your edits are gone. The finding is informational and carries no recovery command for exactly that reason.
  • user-agent-unmaterialized — the pack installed canonical agents into ~/.agents/agents/, but no active provider projects them at user scope. Every registered adapter (Claude, Cursor, Codex, Gemini, Copilot) syncs or natively reads user-scope agents, so while any of them is active for user sync the finding is suppressed. It appears when no provider is enabled for user sync, in which case every installed agent is listed. Provider access to canonical instructions through loaded, user, and project reads is a separate contract and does not clear this finding. oat tools update cannot fix it. Enable a provider with oat providers set --scope user --enabled <provider> and run oat sync --scope user; reinstalling at project scope is not required. See Tool packs for how user-scope agents reach each provider.

status or doctor reports packs:inventory

OAT could not read the canonical managed-pack inventory. oat status keeps the rest of the report available and returns packs.availability.status as unavailable in JSON, with an empty packs.states array and a structured diagnostic. oat doctor emits the same condition as a warning instead of stopping its other checks.

The diagnostic message redacts only the roots that are part of the run: the project root becomes a relative path (or .) when project scope is checked, and the home root becomes ~ when user scope is checked. Redaction is a literal replacement of those exact root paths, so a path outside them, such as a global bundle path in an assets error, stays absolute in the message.

With an installed CLI, reinstall it so its bundled assets are complete and match the CLI version, then rerun the command named in the diagnostic. If you set OAT_ASSETS_DIR, point it at a complete asset bundle built for this CLI version, or unset it to use the bundled copy.

npm install --global @open-agent-toolkit/cli
oat status # or: oat doctor

The diagnostic's own recovery line says to run pnpm build. That applies only when you run the CLI from a checkout of the OAT source repository.

If you are working in the OAT source repository: rebuild the CLI workspace, then rerun the command named in the diagnostic:

pnpm build
oat status # or: oat doctor

status or doctor reports legacy-false-conflict

An older repository left tools.<pack>: false in shared config while managed assets for that pack are present. The false is never honored as an opt-out. Choose one:

# adopt the existing install and rewrite intent as true
oat tools update --pack <pack> --scope <project|user>

# or remove the pack and delete the intent key entirely
oat tools remove --pack <pack> --scope <project|user>

status or doctor reports duplicate-scope

The pack is installed at both project and user scope. This is a legitimate state, and OAT deliberately does not infer which copy a provider executes. Resolve it explicitly when you want one owner:

oat tools migrate --pack <pack> --from project --to user --dry-run
oat tools migrate --pack <pack> --from project --to user

The destination is installed and verified before source removal is offered, so declining the confirmation leaves you exactly where you started — installed at both scopes — rather than in a broken half-state.

oat tools migrate leaves the pack at both scopes

That is the safe outcome, not a failure. Migration verifies the destination first and removes the source only after an explicit confirmation. Declining, or running non-interactively, reports status retained-both. Rerun the command interactively to finish the move; the destination work is already done.

If source removal or its sync failed partway, the command prints structured recovery instructions and retains source intent. Rerun the same command to complete it. Never delete the source by hand while the recovery instructions are outstanding.

A PJM command refuses to write and asks for oat pjm init

Installing the project-management pack installs the capability. It does not adopt PJM for a repository, and oat backlog init / oat decision init are not alternate adoption paths. oat pjm, oat backlog, and oat decision mutations fail closed until adoption is recorded.

Inspect the state, then adopt:

oat pjm doctor --json   # read adoption.state
oat pjm init

adoption.state is declared (explicit marker present), inferred-legacy (complete legacy scaffold, no marker), partial-initialization (incomplete scaffold — rerun oat pjm init), or none. oat pjm init records pjm.initialized in .oat/config.json after verifying the canonical scaffold.

A template change is not taking effect

Project lifecycle and PJM templates resolve repository → user → bundle, first match wins (idea templates follow the ideas scope instead). A repository template under .oat/templates/ is an owner override that pack updates never rewrite, so it shadows the managed user default. Check which tier supplies a template:

oat template resolve plan

Delete the repository copy to fall back to the managed default, or update the managed default itself (workflows owns the project lifecycle templates, project-management the PJM templates):

oat tools update --pack workflows --scope user
oat tools update --pack project-management --scope user

A remote PJM command is pending, uncertain, or blocked

Read the JSON envelope before retrying:

oat pjm remote doctor --json

Before the first provider-contacting command, discover a matching live connector. If no capable connector exists, inspect an already configured provider CLI's live help. Construct only bounded provider-neutral capability evidence and supply it through --capability-evidence-stdin.

  • pending with externalAction means OAT durably prepared one exact semantic host action. Confirm the previously selected capability remains granted, execute that exact action at most once, then submit one bounded observation through oat pjm remote operation continue.
  • uncertain means an effect may have occurred. Follow the envelope's recovery instructions; never repeat a create or update blindly.
  • needs-review requires an explicit reconciliation choice.
  • blocked means policy, approval, capability, lifecycle state, or outbound safety prevented the action. Do not bypass the gate or broaden the field projection.

Only an ok envelope after authoritative read-back is remote success. Native provider output, a zero process exit, or a visible remote change is not.

A clone or worktree has unexpected remote PJM state

Linked worktrees share default local operational state through the Git common directory. If one worktree has a pending operation, inspect or continue it instead of starting a competing action elsewhere.

Independent clones do not share that local store. In a fresh clone, use portable binding metadata plus oat pjm remote refresh through a matching live host capability before treating snapshots as current. Offline, doctor and migration remain available, but commands requiring remote freshness stay pending or blocked.

If the repository opted into .oat/repo/pjm/remote/state/ or <project>/remote/state/, remember that shared operational storage may contain remote planning content visible to repository readers. Promotion requires an exact persisted preview and fresh approval; local projects cannot use it.

--scope all fails outside a Git repository

It should not. oat status --scope all and oat doctor --scope all complete the user-scope work and report project scope as unavailable. oat tools update --scope all also completes the user-scope work, but it skips project scope silently rather than naming it in the result, so an empty project section there means "no repository here", not "nothing to update". An explicitly requested --scope project outside a repository is still a hard failure, which is intentional — you asked for a scope that does not exist here.

Manifest not found or invalid

  • Missing manifest: run sync or init. For the repository's manifest (.oat/sync/manifest.json), use oat sync --scope project or oat init --scope project; without --scope, both commands also write to your home directory. For the user manifest (~/.oat/sync/manifest.json), use --scope user.
  • Invalid manifest: repair/remove file and rerun

Status/output mismatches with lifecycle expectations

  • Reconcile state.md, plan.md review table, and implementation.md.
  • Ensure phase/review status has been updated after reviews and fix cycles.

An Explainer Kit recap ends as built-needs-review

built-needs-review is a satisfied generation outcome. The explainer page and browser-free checks are complete, but the browser rung was unavailable or reported a visual issue that needs a human look. Lifecycle closeout can continue, and a fresh package that passes the complete package guard can be selected and archived.

Inspect:

  1. qa/result.json for the selected browser rung, browser availability, and visual findings.
  2. manifest.json for warnings, outcome, and the exact package inventory.
  3. site/index.html at 320, 768, and 1440 pixels when visual inspection is available.
  4. Adapter logs for a browser launch or capture failure.

If the page is acceptable, retain the outcome as-is; the manifest honestly records that visual review is still due. If it is not acceptable, fix the source or authoring issue and generate a new run. Do not mutate a recorded run package.

Reference artifacts

In your own repository, these project files hold the evidence most entries on this page refer to:

  • .oat/projects/<scope>/<project>/implementation.md
  • .oat/projects/<scope>/<project>/reviews/
  • .oat/projects/<scope>/<project>/explainers/<slug>/manifest.json
  • .oat/projects/<scope>/<project>/explainers/<slug>/qa/result.json

If you are working on OAT itself, in a checkout of the OAT source repository, the checks behind oat doctor, oat status and oat instructions live in these source files:

  • packages/cli/src/commands/doctor/index.ts
  • packages/cli/src/commands/status/index.ts
  • packages/cli/src/commands/tools/shared/pack-inventory.ts
  • packages/cli/src/commands/instructions/

On this page