Provider Interop Commands
Shared conventions
These command definitions inherit the cross-cutting CLI conventions in:
Adjacent command docs (outside provider interop scope)
oat init(bootstrap): CLI Bootstrapoat tools ...(tool-pack lifecycle — install, update, remove, list, info): Tool Packs and Installed Assetsoat doctor(cross-cutting diagnostics):../reference/cli-reference.md
Quick Look
- What it does: defines the day-to-day provider-sync command surface for inspecting state, reconciling provider views, and changing provider enablement.
- When to use it: after you have canonical assets in place and need to check sync state, write provider views, or change provider config.
- Primary commands:
oat status,oat sync,oat providers list,oat providers inspect,oat providers set,oat providers codex materialize
oat status
Purpose:
- Report
in_sync,drifted,missing, andstraystates
Key behavior:
- Scope support (
project,user,all) - Optional interactive stray adoption
- Cursor-local and Copilot-local skills are handled individually: Adopt moves
the skill into the matching canonical
.agents/skillsdirectory, while Keep provider-only preserves it and records its exact path in the applicable sync config - Aborting a native-read skill migration preserves completed choices and leaves the current and remaining skills unresolved
- Keep provider-only is blocked when a canonical skill has the same name; rename one skill before retrying
- Non-interactive and JSON modes report unresolved native-read skill actions without choosing or mutating a disposition
- JSON output for automation
oat sync
Purpose:
- Reconcile provider views from canonical sources
Key behavior:
- Mutates by default; use
--dry-runto preview - Strategy-aware operations (
symlink,copy,auto) - Configured
automay adopt an existing exact collection-directory alias. When the destination is absent, the current runtime falls back per entry; OAT does not create or unlink a collection alias without identity-bound guarded primitives. Configuring an explicit per-entry strategy does not release an owned collection: the current runtime fails closed for deferred collection-directory copies and symlinks because it cannot keep every destination ancestor identity-bound. To leave the provider directory externally owned, runoat providers set --scope <scope> --disabled <provider>and thenoat sync --scope <scope>to detach OAT ownership. Then verify and remove the preserved alias before managing that directory manually. Automatic transition requires an identity-bound, non-following publication primitive. Deferred file operations and ordinary non-transition per-entry symlinks retain their existing behavior. Explicitsymlinkandcopyremain per-entry modes;oat syncdoes not add a--strategyoption. - Provider enable/disable honored via sync config
- Invalid canonical rules stop the run before anything is applied. Sync
checks every rule first and fails once with a message that names each
invalid rule file exactly once, even though several provider transforms
read the same rule, so one run surfaces every file to fix. See
Canonical rule frontmatter for
the accepted
alwaysApply: truealias. - Cursor skills are native-read from canonical
.agents/skills; sync does not create.cursor/skillsmirrors - Copilot skills are native-read from canonical
.agents/skills; sync does not create.github/skillsor~/.copilot/skillsmirrors. Copilot agents and project rules still sync to.github/agents,~/.copilot/agents, and.github/instructions. - Upgrade cleanup removes only verified clean legacy Cursor or Copilot skill views. Changed or unverified views are preserved and detached from obsolete manifest ownership.
- Before the dry-run/apply branch, each scope's sync manifest
oatVersionis compared with the invoking CLI version. Any difference (older or newer) logs an advisory warning naming the scope and both versions; the run continues and the apply path restamps the manifest with the invoking version. JSON output suppresses the warning and reports the same data under a top-levelversionSkewarray ({ scope, producingVersion, invokingVersion }) in both--dry-runand apply envelopes; a scope without skew is omitted from the array. - When a run's only effect is that restamp — no planned operations and no
failures, but skew present — apply reports
Manifest version refreshed; no content changes required.instead ofNo changes required., so a manifest write is never described as a no-op. - A faithful copy-strategy entry whose manifest row records a stale
contentHash(a pre-framing legacy digest or a tampered value) is planned asskipwith the reasonalready in sync; restamp stale manifest content hash, and--jsonplan entries carry the verified digest asrestampContentHash. Apply writes that digest, so the next run is a no-op. Only a row that tracks the same provider path that sync checked is restamped. Dry-run then reportsRun without --dry-run to restamp N stale manifest content hashes.instead ofNo changes to apply.. Apply reportsManifest content hash restamped for N entries; no content changes required.(combined with the version-refresh sentence when both happen), orManifest content hash restamped for N entries.beside a partial failure. - A copy-strategy canonical skill directory without
SKILL.md, or agent directory withoutAGENT.md, is planned as anerroroperation whose reason names the missing file, instead of a copy that never verifies and is retried on every run. It changes nothing and is not a planned operation; other entries still sync. Apply reports it as a failed result (status: failedwith afailurein--jsonoperationResults) and exits 1. Dry-run lists it, warnsN entry cannot sync until its configuration error is fixed., counts it insummary.failed, and still exits 0. Symlink strategy is unaffected.
oat sync is not the only command that replaces a manifest's producer version.
oat init, oat remove skill, and oat status each capture the manifest's
pre-mutation oatVersion while loading and planning, compare it with the
invoking CLI version, and log the same advisory immediately before a qualifying
save when the two differ:
Manifest version restamp [init user]: manifest produced by oat "0.2.58" will be restamped to oat "0.2.59".The advisory sits immediately before the save, so a run that aborts earlier
never announces a restamp it did not perform; a save that fails after the
advisory still leaves the manifest unrestamped. An equal or absent version stays
quiet. JSON mode suppresses the human warning; oat init and oat remove skill
report the applied restamps under a top-level manifestVersionRestamps array of
{ scope, producingVersion, invokingVersion } entries. The oat remove skill
dry-run payload omits the field rather than naming a restamp no save will
perform.
Preview project and user cleanup before applying it:
oat sync --scope all --dry-runReview every planned remove and detach operation before running the same
command without --dry-run.
Dry-run and apply output include a Collection aliases section with scope,
provider, content kind, action, ownership, scope-relative canonical/provider
directories, reason, and planned or applied result. JSON reports the same
redacted fields under collectionOperations; collection proofs are reduced to
status and a safe reason rather than filesystem identities or absolute scope
roots. Real-directory fallback is reported as fallback-per-entry.
oat providers list
Purpose:
- Summarize adapters, detection, and mapping-level health summary
- Summarize managed collection aliases by
oat-createdandadopted-exactownership in both human and JSON output
oat providers inspect <provider>
Purpose:
- Show adapter mappings and per-scope mapping state details
- Show each owned collection alias with scope-relative paths, ownership, and last-verification time
oat providers set
Purpose:
- Enable or disable providers in the selected scope's sync config; project is the default scope
Key behavior:
- Updates the selected scope's sync config to toggle provider enablement
- Scope:
--scope project|user(defaults toproject) - Options:
--enabled <providers>,--disabled <providers>(comma-separated) - Changes take effect on next
oat sync
oat providers codex materialize
Purpose:
- Materialize one canonical Markdown agent as a Codex TOML role and register it in Codex configuration
Required operands:
oat providers codex materialize <agent-name> \
--model <model-id> \
--effort <reasoning-effort>Key behavior:
- Project scope is the default and writes only
.codex/config.tomlplus the project role file;--scope userwrites only under~/.codex --agent-pathselects a specific canonical agent and--role-nameoverrides the generated role name- Managed role registration enables multi-agent support and merges an
agents.max_depthfloor of2 - Project writes preserve a higher existing project depth or inherited user depth without mutating user configuration; user writes do not inspect or mutate project configuration
- Existing unrelated Codex configuration and custom roles are preserved
Notes
oat init --scope projectis commonly used before provider-interop commands because it initializes.oat/sync/config.json.- User-scope known-stray choices are stored in
~/.oat/sync/config.json. Legacy~/.oat/config.json#knownStraysentries migrate automatically before user-scope stray filtering. oat doctorcomplements interop workflows by surfacing environment and bundled-skill version issues before or after sync operations.
Adjacent Instruction Integrity Commands
These commands are documented here because they are commonly used during interop-only repo maintenance, but they are not provider sync/drift commands.
oat instructions validate
Purpose:
- Validate project-scoped
AGENTS.mdtoCLAUDE.mdintegrity
Key behavior:
- Read-only validation of nested project-scoped instruction directories
- Validates the effective strategy:
--strategy none|pointer|symlink|copy, elseinstructions.claude.shims, elsenone(the default, which expects noCLAUDE.md); reports it asstrategyunder--json - Reports
ok,missing(shim strategies only),content_mismatch,stray, and, undernone,managed_shim(drift sync removes) andunmanaged(kept, not drift) - Detects Claude-only adoptable directories and unreadable/broken instruction paths as drift
- Under
none, warns about every remainingCLAUDE.md,.claude/CLAUDE.md, andCLAUDE.local.md(stderr, or thewarningsarray under--json) without changing the exit code - Skips the derived documentation content root and any
instructions.claude.excludespaths, using the same exclusions asoat instructions sync - Exit code
0when all entries are valid,1when drift is detected - Detailed behavior:
Instruction Sync
oat instructions sync
Purpose:
- Repair project-scoped
AGENTS.mdtoCLAUDE.mddrift
Key behavior:
- Mutates by default; use
--dry-runto preview changes - Supports
--strategy none|pointer|symlink|copyas a one-run override ofinstructions.claude.shims(defaultnone) - Under
none, removes theCLAUDE.mdshims OAT created (exact@AGENTS.mdpointer, symlink to the siblingAGENTS.md, or byte-identical copy), re-verifying each immediately before deletion; hand-written or modifiedCLAUDE.mdfiles are reported, never deleted, and while anyCLAUDE.md,.claude/CLAUDE.md, orCLAUDE.local.mdhas content of its own, nothing is removed (claude_md_blocks_shim_removal) - Under a shim strategy, creates missing
CLAUDE.mdfiles using that strategy - Adopts Claude-only stray files by writing canonical
AGENTS.mdcontent first, then regeneratingCLAUDE.md(shim strategies) or removing it (none) - Under a shim strategy, skips mismatched files unless
--forceis provided - Skips unreadable canonical or Claude-only sources and reports manual-repair guidance instead of forcing recovery
- Skips the documentation content tree (literal
documentation.rootfor toolingmarkdown, including nesteddocs; otherwise<documentation.root>/docswhen that is a directory, falling back todocumentation.root) plus anyinstructions.claude.excludespaths, so authored pages never receive pointers; app-level instruction files such asapps/oat-docs/AGENTS.mdare still synced - Never deletes an existing
CLAUDE.mdinside an excluded tree - Under
none, warns about everyCLAUDE.mdthat remains after sync, including inside excluded trees, because any one of them makes Claude Code ignore everyAGENTS.md - Uses pointer content
@AGENTS.md\n, file symlinks, or hard copies depending on the selected strategy - Detailed behavior and examples:
Instruction Sync
Safe scope for routine refresh
oat sync copies or links your canonical assets (the skills, agents, and rules
you edit under .agents/) into provider views, the per-tool files such as
.claude/skills/<name> that each agent tool reads. The scope decides which
trees it touches: project is this repository, user is your home directory,
and all is both.
WARNING
A bare oat sync uses scope all, so it also writes under your home
directory. Every sync also overwrites edits made directly in provider folders,
replaces an untracked file sitting at a view path, and removes a view whose
canonical source was deleted. Edit files under .agents/ rather than in
provider folders, and pass --scope explicitly.
- For a repository-only refresh, run
oat sync --scope project --dry-runto preview, then run it again without--dry-run. - In CI or scripts, always pass
--scope projectso the run cannot touch anything in a home directory. - To refresh your personal skills across repositories, run
oat sync --scope user. - If you do mean to refresh both, preview
oat sync --scope all --dry-runfirst.
Pilot Provider Sync with One Team
A week-one plan for trying Provider Sync on one repository with a team that mixes Claude Code, Cursor, and Codex: setup, teammate onboarding, what to watch, and how to decide.
Sync Config (`.oat/sync/config.json`)
Configuration schema and behavior for provider sync in .oat/sync/config.json.