This is contracts/adapter.toml, shown as published.
# Adapter contract — every (primitive × adapter) pair enumerated.# Validates against ./adapter.schema.json.## Version history (continues below the [contract] block):# v0.1: base contract.# v0.2: scope dimension.# v0.3: hook-body / hook-wiring forks.# v0.4: claude-plugins install route.# v0.5: apm install route.# v0.6: codex user-scope table.# v0.7: per-IDE projection at repo scope; `allowed-prefixes.repo` on every# adapter; copilot scope table. Co-bumped with the credential-broker# governance change — the `metadata.auth` lint surface lives in verify.py# (_step_agent_artifacts), not in this file.# v0.8 (dropped-primitives coverage): codex `agent` projects to# `.codex/agents/<name>.toml` via new `codex-agent-toml` mode; codex# `hook-wiring` projects to `.codex/hooks.json` via existing `merge-json`# mode; codex `.codex/` added to allowed-prefixes; new `codex-agent-# frontmatter-v0.8` mapping. Dropped-primitives warning rail introduced# in `agentbundle install` for any (adapter, pack) pair where the# resolved adapter projects a primitive type the pack ships as `dropped`.# v0.9 (kiro adapter split): kiro splits into# `kiro-ide` (IDE, .md agents, kiro-ide-hook active, flat path confirmed by# Q6 probe no×yes 2026-06-01) and `kiro-cli` (CLI binary, .json agents,# hook-wiring retained). `kiro` retained as deprecated alias for `kiro-ide`.# `kiro-ide-hook` primitive activated; `kiro-ide-agent-frontmatter-v0.9` renamed# to `kiro-ide-agent-frontmatter-v0.9`.# v0.10 (copilot full parity): copilot becomes# a full-parity, user-scope-capable adapter. `agent` flips `dropped`→# `copilot-agent-md` (markdown → `.github/agents/<name>.agent.md`); `hook-wiring`# flips `dropped`→`copilot-hooks-json` (one self-contained `<name>.json` per# source file at `.github/hooks/`); `hook-body` retargets `tools/hooks/`→# `.github/hooks/`; `skill` gained a user target (later flipped to# `.github/skills/` SKILL.md in v0.11 — see the [contract] note below);# `[adapter.copilot.scope]` gains `user = "~"` + `allowed-prefixes.user`; new# `copilot-agent-frontmatter-v0.10` mapping. `command` stays `dropped`# (copilot-cli#618/#1113). Two-pack bump (research + core), not all-pack.# Codex agent-config correction (no version bump): corrects the existing# `codex-agent-frontmatter-v0.8` mapping to preserve `model` and `tools`# intent as documented Codex agent config instead of dropping those fields.# v0.11 (cursor full parity): new native# `cursor` full-parity adapter projecting all five primitives to `.cursor/*`# at both scopes. NO new projection mode and NO adapter.schema.json change —# skill→direct-directory, agent→direct-file (+ `cursor-agent-frontmatter-v0.11`;# `tools` dropped + `readonly` derived inline by cursor.py), hook-body→# direct-file, hook-wiring→merge-json (aggregated `.cursor/hooks.json`,# event-remapped + `version` key via cursor.py's merge helper), command→# direct-file (Cursor honours commands). kiro-ide-hook dropped. Same `.cursor/`# prefix at both scopes (claude-code/codex pattern; no prefix rewrite).# Distribution-only (not in SELF_HOST_ADAPTERS).
[contract]# v0.12 (2026-06-11): copilot `skill` flips# `instruction-file`→`direct-directory` (first-class Agent Skills at# `.github/skills/<name>/SKILL.md`); the `copilot-instruction` frontmatter-default# is retired. The copilot web-tool correction is docs/comments-only.# (Lands atop cursor v0.11; the two co-bumped — copilot takes v0.12.)# v0.13 (gemini full parity): new native# `gemini` full-parity adapter projecting all five primitives to `.gemini/*` at# both scopes. One new projection mode (`gemini-command-toml`); a bespoke# `.gemini/settings.json` merge carrying `hooks` (event-remapped, fail-closed) +# a static `context.fileName` bridge (from `context-filenames`). agent `tools:`# kept and name-mapped; tier-preserving model map. hook-body lands under# `.gemini/hooks/` (the cursor model — single prefix at both scopes). kiro-ide-hook# dropped. Distribution-only (not in SELF_HOST_ADAPTERS).# v0.14 (enriched pack manifest): pack.toml# becomes the rich metadata source of truth (optional readme / display_name /# license / maintainers / links / categories / keywords / [pack.metadata.<tool>]# / [pack].catalogue). The build projects the cleanly-projectable subset into# the claude-plugins + apm routes (plugin.json / marketplace.json entry) plus# each pack's README. All new fields are optional — packs pinned below v0.14# build and validate unchanged. Declaration + projection only (no @catalogue/pack# resolution, no search, no persisted index).# v0.15 (kiro-cli agent skill resources): kiro-cli agent projection# injects a fixed skill-resources glob (both install scopes) so headless# `--agent` runs reach the bundle's skills — Kiro custom agents don't inherit# the default agent's `.kiro/skills` auto-discovery (kiro# #6887/#6888/#4993). Additive `inject-resources` on BOTH the kiro-cli and# kiro-ide agent projections (CLI → agent JSON, IDE → quoted .md frontmatter).# v0.16 (consolidated pack layout): pack.toml# gains an optional scope-keyed [pack.layout] table (optional .repo / .user# sub-tables, each a `parent` base and/or a within-pack template-path) naming# where a consuming pack's durable output lands. Consumed by the new installer# `_append_layout_section` step (append-if-exists / never-create / never-# overwrite) into an adopter-owned `agentbundle-layout.toml`. Additive +# optional — packs pinned below v0.16 build and validate unchanged.# v0.17 (shared-prefix registry): the `[contract.shared-prefixes]` registry# (below) classifies each allowed-prefix as `shared` (a path more than one# adapter reads) or — by absence — `private`, and a shared prefix declares its# reader cohort. cursor/gemini/copilot route the `skill` primitive to the shared# `.agents/skills/` home (joining codex) and gain it in `allowed-prefixes` at# both scopes; their agent/hook/command projection stays native.# v0.18 (Claude-plugin hook parity): `hook-body` gains the plugin-root# `hooks/` target and `hook-wiring` is dropped from the ordinary projection on# that route after its entries are compiled into plugin.json.version = "0.18"
# Sibling schemas this contract references — pack metadata and the# per-pack Claude-plugin manifest.[contract.schemas]pack = "pack.schema.json"plugin-manifest = "plugin-manifest.schema.json"
# Shared-prefix registry. Each key is a `shared` prefix —# a path more than one shipped adapter reads — mapped to its reader cohort (the# shipped adapters that read it). Every prefix NOT listed here is `private`# (adapter-exclusive). The install-time cross-adapter disclosure names a shared# prefix's *other* shipped cohort members; the footprint gate co-owns a shared# path across same-pack rows. A future adapter joins a cohort by one line here.[contract.shared-prefixes]".agents/skills/" = ["codex", "cursor", "gemini", "copilot"]".kiro/skills/" = ["kiro-ide", "kiro-cli"]
# ---------------------------------------------------------------------------# Primitive types — source paths within a pack's .apm/ directory# ---------------------------------------------------------------------------
[primitive.skill]source-path = ".apm/skills/"
[primitive.agent]source-path = ".apm/agents/"
[primitive."hook-body"]source-path = ".apm/hooks/"
[primitive."hook-wiring"]source-path = ".apm/hook-wiring/"
[primitive.command]source-path = ".apm/commands/"
# kiro-ide-hook: standalone IDE event hook files (.kiro.hook JSON).# Activated at v0.9 for the kiro-ide adapter; dropped for kiro-cli and others.[primitive."kiro-ide-hook"]source-path = ".apm/kiro-ide-hooks/"
# `shared-libs/` carries Python sibling# files. The original projection into every `metadata.auth: creds`# skill's `scripts/` was RETIRED (those consumers resolve# via the `credbroker` pip library, imported in-process). What survives# in `agentbundle.build.shared_libs` is source enumeration# (`collect_sources`): the `adapter-root-bins` companion-shim rail reads# this path to locate `credentials_shim.py` for projection into# `<scope-root>/.agentbundle/bin/`. The declaration here documents the# source path so future readers see the full primitive surface in one# place.[primitive."shared-libs"]source-path = ".apm/shared-libs/"
# `adapter-root-bins/` carries executable scripts that# project to the per-scope artifact root (`~/.agentbundle/bin/` at user# scope, `<repo>/.agentbundle/bin/` at repo scope). T6 wires the# projection; the declaration is here so the contract is complete at# v0.7. Adapter projection rules for `adapter-root-bins` are NOT# declared because the target is `<scope-root>/.agentbundle/bin/`,# which is fenced by `allowed-prefixes.<scope>`, not by per-adapter# target paths.[primitive."adapter-root-bins"]source-path = ".apm/adapter-root-bins/"
# credbroker-user-scope T3 — `user-libs/` carries vendored Python package# source projected to the per-scope lib floor (`~/.agentbundle/lib/` at user# scope, `<repo>/.agentbundle/lib/` self-host staging) as the lowest-precedence# `sys.path` import target. Build-pipeline-only like `adapter-root-bins`: no# per-adapter projection rules (the target is `<scope-root>/.agentbundle/lib/`,# fenced by `allowed-prefixes.<scope>`, not by per-adapter target paths), so the# declaration carries the source path only.[primitive."user-libs"]source-path = ".apm/user-libs/"
# ---------------------------------------------------------------------------# Claude Code adapter — 5 projections (one per primitive) + v0.3 forks## The legacy `[[adapter."claude-code".projection]]` array carries the# repo-scope shape every adapter has consumed since v0.1. v0.3 adds two# forward-looking `[adapter."claude-code".projections.<primitive>]` tables# (hook-body / hook-wiring) carrying the scope-conditional shape that the# v0.3 user-scope pipeline reads. Until that pipeline lands, the legacy# entries remain authoritative; the new tables are declarative metadata.# ---------------------------------------------------------------------------
[adapter."claude-code"]
[[adapter."claude-code".projection]]primitive = "skill"mode = "direct-directory"target-path = ".claude/skills/"on-conflict = "prompt-then-preserve"
[[adapter."claude-code".projection]]primitive = "agent"mode = "direct-file"target-path = ".claude/agents/"on-conflict = "prompt-then-preserve"
[[adapter."claude-code".projection]]primitive = "hook-body"mode = "direct-file"target-path = "tools/hooks/"on-conflict = "prompt-then-preserve"
[[adapter."claude-code".projection]]primitive = "hook-wiring"mode = "merge-json"target-path = ".claude/settings.local.json"managed-key = "hooks"on-conflict = "merge-managed-key-only"
[[adapter."claude-code".projection]]primitive = "command"mode = "direct-file"target-path = ".claude/commands/"on-conflict = "prompt-then-preserve"
# hook-body — scope-conditional target.# Forward-looking v0.3 declaration; the legacy array entry above remains the# pipeline-of-record until T5/T7 land.[adapter."claude-code".projections.hook-body]mode = "direct-file"target.repo = "tools/hooks/<name>.{sh,py}"target.user = ".claude/hooks/<name>.{sh,py}"on-conflict = "prompt-then-preserve"
# hook-wiring — scope-conditional mode/target; user-scope `managed-key`# required.[adapter."claude-code".projections.hook-wiring]mode.repo = "merge-json"mode.user = "user-merge-json"target.repo = ".claude/settings.local.json"target.user = ".claude/settings.json"managed-key.user = "hooks"
# Scope dimension. Two roots: repo-local and user-local.# `allowed-prefixes.<scope>` fences every write under that scope to a# declared prefix list — at user scope this stops a buggy projection# rule from resolving under `~/Documents/`. The two-prefix shape covers# projected primitives (`.claude/`) and CLI infrastructure writes# (`.agentbundle/` — user-scope state file, per-scope discovery and# pending artifacts).[adapter."claude-code".scope]repo = "."user = "~"# Repo-scope projection lands primitives under `.claude/` (skills,# agents, commands, hook-wiring) plus `tools/hooks/` for hook bodies — the# legacy repo-scope hook-body target that pre-dates the scope dimension.allowed-prefixes.repo = [".claude/", ".agentbundle/", "tools/hooks/"]allowed-prefixes.user = [".claude/", ".agentbundle/"]
# ---------------------------------------------------------------------------# Kiro adapter — DEPRECATED ALIAS for kiro-ide.## `kiro` is retained as a working alias with no removal timeline so existing# packs declaring `allowed-adapters = ["kiro"]` keep working. The Python# registry maps "kiro" → kiro_ide.project and emits a build-time# DeprecationWarning on use. Update `allowed-adapters` to `["kiro-ide"]` or# `["kiro-cli"]` in pack.toml to silence the warning.## The projection blocks below are preserved verbatim so `validate` keeps# accepting "kiro" and `_shipped_for_cli` (derived from the adapter key set)# continues to include it.# ---------------------------------------------------------------------------
[[adapter.kiro.projection]]primitive = "skill"mode = "direct-directory"target-path = ".kiro/skills/"on-conflict = "prompt-then-preserve"
[[adapter.kiro.projection]]primitive = "agent"mode = "direct-file"target-path = ".kiro/agents/"frontmatter-mapping = "kiro-ide-agent-frontmatter-v0.9"on-conflict = "prompt-then-preserve"
[[adapter.kiro.projection]]primitive = "hook-body"mode = "direct-file"target-path = "tools/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.kiro.projection]]primitive = "command"mode = "dropped"
# hook-body — scope-conditional target. User-scope lands under `~/.kiro/hooks/`.# Forward-looking v0.3 declaration;# the legacy array entry above remains authoritative until T5/T7.[adapter.kiro.projections.hook-body]mode = "direct-file"target.repo = "tools/hooks/<name>.{sh,py}"target.user = ".kiro/hooks/<name>.{sh,py}"on-conflict = "prompt-then-preserve"
# hook-wiring — merge into the pack-owned agent JSON. Same mode at both scopes;# the agent-file target is scope-conditional via scope-root resolution.# The legacy# `degraded-info-log` array entry is removed.[adapter.kiro.projections.hook-wiring]mode = "merge-into-agent-json"target.repo = ".kiro/agents/<attach-to-agent>.json"target.user = ".kiro/agents/<attach-to-agent>.json"managed-key = "hooks"agent-event-vocabulary = [ "agentSpawn", "userPromptSubmit", "preToolUse", "postToolUse", "stop",]
# Scope dimension for Kiro.# User-scope agents live under `~/.kiro/agents/` per Kiro's documented layout# (https://kiro.dev/docs/cli/custom-agents/creating/); CLI infrastructure# shares the `.agentbundle/` prefix.[adapter.kiro.scope]repo = "."user = "~"# Kiro projects skills/agents to `.kiro/`; hook bodies land in the# legacy `tools/hooks/` at repo scope (matching the contract's hook-body# array entry above).allowed-prefixes.repo = [".kiro/", ".agentbundle/", "tools/hooks/"]allowed-prefixes.user = [".kiro/", ".agentbundle/"]
# ---------------------------------------------------------------------------# kiro-ide adapter — targets the Kiro VS Code-fork IDE.## Projects agents as `.md` (gray-matter YAML frontmatter + body); the IDE# reads `.kiro/agents/<name>.md` via its gray-matter loader. hook-wiring is# DROPPED — the IDE loader silently drops any agent carrying a `hooks` key.# `kiro-ide-hook` is ACTIVATED with the flat path confirmed by# Q6 probe (no-recursion, yes-extension-filter, 2026-06-01, Kiro 0.12.224):# `.kiro/hooks/<pack>--<name>.kiro.hook`. The split assumed yes-recursion;# T1 carries the corrected no-recursion flat path (no T-E1b fires).# ---------------------------------------------------------------------------
[[adapter.kiro-ide.projection]]primitive = "skill"mode = "direct-directory"target-path = ".kiro/skills/"on-conflict = "prompt-then-preserve"
[[adapter.kiro-ide.projection]]primitive = "agent"mode = "direct-file"target-path = ".kiro/agents/"frontmatter-mapping = "kiro-ide-agent-frontmatter-v0.9"# Skill discovery: IDE custom agents / subagents do NOT inherit# the default agent's `.kiro/skills` auto-discovery either (kiro #6887/#6888,# #4993). Inject the same fixed skill-resources glob as kiro-cli; the IDE# agent `resources` field is documented and `skill://`-aware, so it does not# trip the silent-drop bug (that's for non-schema keys like `hooks`). Emitted# into the `.md` YAML frontmatter, quoted. Author override wins; an explicit# empty `resources` list suppresses injection and is omitted from output, and so# does Claude Code's `skills: []` — the portable form of the same intent, since# `resources` is Kiro-native and not valid Claude Code agent frontmatter. A# non-empty `skills` list is a build error: turning a bare skill name into a# `skill://` URI needs templating this mapping grammar cannot express.inject-resources = ["skill://.kiro/skills/**/SKILL.md", "skill://~/.kiro/skills/**/SKILL.md"]on-conflict = "prompt-then-preserve"
[[adapter.kiro-ide.projection]]primitive = "hook-body"mode = "direct-file"target-path = "tools/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.kiro-ide.projection]]primitive = "hook-wiring"mode = "dropped"
[[adapter.kiro-ide.projection]]primitive = "command"mode = "dropped"
# hook-body — scope-conditional target.[adapter.kiro-ide.projections.hook-body]mode = "direct-file"target.repo = "tools/hooks/<name>.{sh,py}"target.user = ".kiro/hooks/<name>.{sh,py}"on-conflict = "prompt-then-preserve"
# kiro-ide-hook — activated for the IDE target (Q6 probe: no-recursion,# yes-extension-filter). Flat path uses `<pack>--<name>` prefix separator# so pack hooks coexist in the single flat `.kiro/hooks/` directory.[adapter.kiro-ide.projections.kiro-ide-hook]mode = "direct-file"target.repo = ".kiro/hooks/<pack>--<name>.kiro.hook"ide-event-vocabulary = [ "fileEdited", "fileCreated", "fileDeleted", "userTriggered", "promptSubmit", "agentStop", "preToolUse", "postToolUse", "preTaskExecution", "postTaskExecution", "sessionStart",]ide-action-vocabulary = ["askAgent", "runCommand"]
[adapter.kiro-ide.scope]repo = "."user = "~"allowed-prefixes.repo = [".kiro/", ".agentbundle/", "tools/hooks/"]allowed-prefixes.user = [".kiro/", ".agentbundle/"]
# ---------------------------------------------------------------------------# kiro-cli adapter — targets the `kiro` terminal binary.## Projects agents as `.json` with CLI short-name tool tokens. Retains# `hook-wiring` via `merge-into-agent-json`. `kiro-ide-hook` is dropped —# the IDE-only event-hook primitive is not supported by the CLI target.# ---------------------------------------------------------------------------
[[adapter.kiro-cli.projection]]primitive = "skill"mode = "direct-directory"target-path = ".kiro/skills/"on-conflict = "prompt-then-preserve"
[[adapter.kiro-cli.projection]]primitive = "agent"mode = "direct-file"target-path = ".kiro/agents/"frontmatter-mapping = "kiro-cli-agent-frontmatter-v1.0"# Skill discovery: Kiro custom agents do NOT inherit the default# agent's auto-discovery of `.kiro/skills` (kiro #6887/#6888), so a headless# `--agent` run sees zero skills unless the agent declares them. Inject this# fixed skill-resources glob (both install scopes) into every projected agent.# Skipped when the agent source already declares its own `resources` (author# override wins); an explicit empty list suppresses injection and is omitted, as# does the portable Claude Code `skills: []` opt-out (see the kiro-ide entry).# kiro-ide carries the same field on its own agent projection# entry (emitted into `.md` frontmatter rather than JSON).## Both Kiro agent projectors rewrite the *source frontmatter* and pass unmapped# keys through, unlike codex/copilot/cursor/gemini which iterate this mapping and# so drop anything unmapped. That is what admits a Kiro-native `resources` or# `prompt`, but it also means an unmapped key would reach the consumer verbatim.# `_CLI_AGENT_FIELDS` / `_IDE_AGENT_FIELDS` in `adapters/kiro.py` bound the# emitted shape and log every drop, so Claude Code fields Kiro cannot read# (`permissionMode`, `memory`, `maxTurns`) and IDE-only fields that make the CLI# loader silently drop an agent (`hooks`) stay out of the output. `skills` is# not among them: it is consumed as the portable no-skill opt-out before that# bound applies, so it is never emitted and never logged as a drop.inject-resources = ["skill://.kiro/skills/**/SKILL.md", "skill://~/.kiro/skills/**/SKILL.md"]on-conflict = "prompt-then-preserve"
[[adapter.kiro-cli.projection]]# Legacy repo-scope entry — the pipeline-of-record for repo-scope hook-body# projection (mirrors the `kiro` adapter's v0.3 hybrid). The table form# below is the scope-conditional declaration for the user-scope pipeline;# both coexist intentionally. `_iter_primitives` deduplicates via set union.primitive = "hook-body"mode = "direct-file"target-path = "tools/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.kiro-cli.projection]]primitive = "command"mode = "dropped"
# hook-body — scope-conditional target.[adapter.kiro-cli.projections.hook-body]mode = "direct-file"target.repo = "tools/hooks/<name>.{sh,py}"target.user = ".kiro/hooks/<name>.{sh,py}"on-conflict = "prompt-then-preserve"
# hook-wiring — merge into the pack-owned agent JSON (same as legacy kiro).[adapter.kiro-cli.projections.hook-wiring]mode = "merge-into-agent-json"target.repo = ".kiro/agents/<attach-to-agent>.json"target.user = ".kiro/agents/<attach-to-agent>.json"managed-key = "hooks"agent-event-vocabulary = [ "agentSpawn", "userPromptSubmit", "preToolUse", "postToolUse", "stop",]
# kiro-ide-hook — dropped for the CLI target. The IDE-only event-hook# primitive is not supported by the kiro binary; packs shipping# .apm/kiro-ide-hooks/ content must use the kiro-ide adapter instead.[adapter.kiro-cli.projections.kiro-ide-hook]mode = "dropped"
[adapter.kiro-cli.scope]repo = "."user = "~"allowed-prefixes.repo = [".kiro/", ".agentbundle/", "tools/hooks/"]allowed-prefixes.user = [".kiro/", ".agentbundle/"]
# ---------------------------------------------------------------------------# Copilot adapter — 5 projections (one per primitive)# ---------------------------------------------------------------------------
# `skill` projects as first-class Copilot Agent Skills:# `direct-directory` passthrough of our canonical# `.apm/skills/<name>/SKILL.md` to `.github/skills/<name>/SKILL.md` (repo) and# `~/.copilot/skills/<name>/SKILL.md` (user, via the install rewrite). Copilot# accepts our Claude SKILL.md verbatim. Was `instruction-file`→`.github/instructions/`.[[adapter.copilot.projection]]primitive = "skill"mode = "direct-directory"# Cohort skill home. Copilot reads `.agents/skills/`# (project) and `~/.agents/skills/` (personal); routing skills there lets the# whole cohort (codex, cursor, gemini, copilot) share one co-owned copy.target-path = ".agents/skills/"on-conflict = "prompt-then-preserve"
[[adapter.copilot.projection]]primitive = "agent"mode = "copilot-agent-md"target-path = ".github/agents/"frontmatter-mapping = "copilot-agent-frontmatter-v0.10"on-conflict = "prompt-then-preserve"
[[adapter.copilot.projection]]primitive = "hook-body"mode = "direct-file"target-path = ".github/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.copilot.projection]]primitive = "hook-wiring"mode = "copilot-hooks-json"target-path = ".github/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.copilot.projection]]primitive = "command"mode = "dropped"
# Scope dimension for Copilot (v0.10 full parity, plus the v0.12 skill# surface). Copilot is a full-parity,# user-scope-capable adapter: the app + CLI discover skills/agents/hooks from# `.github/` (repo) and `~/.copilot/` (user). The repo-only restriction v0.7# recorded is superseded-in-part. `skill` projects as a `direct-directory`# `SKILL.md` tree; its user target (`~/.copilot/skills/<name>/SKILL.md`) is# produced by the install handler's copilot user-scope prefix rewrite# (`.github/X/`→`.copilot/X/`), since the build adapter is scope-agnostic and# emits repo-relpaths.[adapter.copilot.scope]repo = "."user = "~"# Repo-scope skills at `.github/skills/`,# agents at `.github/agents/`, hook wiring + bodies at `.github/hooks/`.# Skills route to the shared `.agents/skills/` home; agents + hooks# stay native under `.github/` — only the skill primitive moves.allowed-prefixes.repo = [".agents/skills/", ".github/skills/", ".github/agents/", ".github/hooks/"]# User-scope home is `~/.copilot/` for agents/hooks (the prefix the install# rewrite produces) and `~/.agents/skills/` for skills (scope-agnostic, no# rewrite). `.agentbundle/` carries the install state file# (`~/.agentbundle/state.toml`), same as every other user-scope-capable adapter.allowed-prefixes.user = [".agents/skills/", ".copilot/skills/", ".copilot/agents/", ".copilot/hooks/", ".agentbundle/"]
# ---------------------------------------------------------------------------# Codex adapter — 5 projections (one per primitive)# ---------------------------------------------------------------------------
[[adapter.codex.projection]]primitive = "skill"mode = "direct-directory"target-path = ".agents/skills/"on-conflict = "prompt-then-preserve"
[[adapter.codex.projection]]primitive = "agent"mode = "codex-agent-toml"target-path = ".codex/agents/"frontmatter-mapping = "codex-agent-frontmatter-v0.8"on-conflict = "prompt-then-preserve"
[[adapter.codex.projection]]primitive = "hook-body"mode = "direct-file"target-path = "tools/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.codex.projection]]primitive = "hook-wiring"mode = "merge-json"target-path = ".codex/hooks.json"managed-key = "hooks"on-conflict = "merge-managed-key-only"
[[adapter.codex.projection]]primitive = "command"mode = "dropped"
# Scope dimension for Codex.# User-scope skills live under `~/.agents/skills/` per the codex# `direct-directory` projection's `.agents/skills/` target-path above;# CLI infrastructure shares the `.agentbundle/` prefix.[adapter.codex.scope]repo = "."user = "~"# Codex projects skills to `.agents/skills/`; hook bodies land in# the legacy `tools/hooks/` (matching codex's hook-body array entry).allowed-prefixes.repo = [".agents/skills/", ".codex/", ".agentbundle/", "tools/hooks/"]allowed-prefixes.user = [".agents/skills/", ".codex/", ".agentbundle/"]
# ---------------------------------------------------------------------------# Cursor adapter — full-parity native adapter (v0.11).## Projects all five catalogue primitives to Cursor's native `.cursor/*`# discovery paths at both repo and user scope. Cursor's CLI and IDE share one# `.cursor/` layout (no kiro-style split). No new projection mode and no# adapter.schema.json change: every primitive reuses an already-enumerated# mode. The agent uses `direct-file` + a frontmatter mapping (the kiro-ide# `.md` shape); because Cursor subagents have no per-agent tool allowlist,# cursor.py drops the source `tools` and derives a `readonly` flag for# non-mutating agents (documented degradation, the Copilot shape).# hook-wiring is `merge-json` to an aggregated `.cursor/hooks.json`; cursor.py's# merge helper remaps source events via `hook-event-map` and adds a `version`# key. `command` is first-class (Cursor is the second adapter after claude-code# to honour commands). `kiro-ide-hook` is dropped (Kiro-only), declared in the# table form since the schema's array `primitive` enum admits only the five# standard primitives. Scope prefix is identical at both scopes (the# claude-code/codex pattern), so the user-scope home is the generic# user-rooting of the repo-relpath — NO Cursor-specific prefix rewrite.# Distribution-only: cursor is NOT in SELF_HOST_ADAPTERS.# ---------------------------------------------------------------------------
[[adapter.cursor.projection]]primitive = "skill"mode = "direct-directory"# Cohort skill home. Cursor reads `.agents/skills/` and# `~/.agents/skills/` alongside `.cursor/skills/`; route skills there to share# one co-owned copy across the cohort.target-path = ".agents/skills/"on-conflict = "prompt-then-preserve"
[[adapter.cursor.projection]]primitive = "agent"mode = "direct-file"target-path = ".cursor/agents/"frontmatter-mapping = "cursor-agent-frontmatter-v0.11"on-conflict = "prompt-then-preserve"
[[adapter.cursor.projection]]primitive = "hook-body"mode = "direct-file"target-path = ".cursor/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.cursor.projection]]primitive = "hook-wiring"mode = "merge-json"target-path = ".cursor/hooks.json"managed-key = "hooks"on-conflict = "merge-managed-key-only"# Hook-event map. Our hook-wiring source uses Claude-# native PascalCase event names (e.g. `[[hooks.SessionStart]]`); Cursor's# hooks.json uses camelCase event keys. cursor.py's merge helper reads this# table to translate each source event; a source event with no entry here is# dropped with a build-time log line (fail-open, the no-silent-caps rule —# deliberately unlike copilot-hooks-json's fail-closed map). The map lives in# the contract, not the adapter code. (The original design spelled the source# vocabulary `agentSpawn`/…; the shipped source is Claude PascalCase, so the# map keys on PascalCase — target events unchanged.)hook-event-map = { SessionStart = "sessionStart", UserPromptSubmit = "beforeSubmitPrompt", PreToolUse = "preToolUse", PostToolUse = "postToolUse", Stop = "stop" }
[[adapter.cursor.projection]]primitive = "command"mode = "direct-file"target-path = ".cursor/commands/"on-conflict = "prompt-then-preserve"
# kiro-ide-hook — dropped (Kiro-only primitive). Declared in the table form,# not the projection array, because the schema's array `primitive` enum admits# only skill/agent/hook-body/hook-wiring/command (kiro-ide / kiro-cli set the# table-form precedent).[adapter.cursor.projections.kiro-ide-hook]mode = "dropped"
# Scope dimension for Cursor. Cursor's `.cursor/` prefix# is identical at repo and user scope (the claude-code/codex shape), so the# divergent user-scope home is produced by the generic user-root resolution,# not a Cursor-specific prefix rewrite. `.agentbundle/` carries the install# state file at both scopes (as every scope-capable adapter lists it).[adapter.cursor.scope]repo = "."user = "~"# Skills route to the shared `.agents/skills/` home (both scopes);# agents/hooks stay under `.cursor/` (moves only the skill primitive).allowed-prefixes.repo = [".agents/skills/", ".cursor/", ".agentbundle/"]allowed-prefixes.user = [".agents/skills/", ".cursor/", ".agentbundle/"]
# ---------------------------------------------------------------------------# Gemini adapter — full-parity native adapter (v0.13).## Projects all five catalogue primitives to Gemini CLI's native `.gemini/*`# discovery paths at both repo and user scope. Unlike Cursor, gemini KEEPS and# name-maps the agent `tools:` allowlist (Gemini has a real per-agent tool# allowlist) and applies a tier-preserving `model` map (`gemini-agent-frontmatter`).# `command` uses the new `gemini-command-toml` mode (markdown body → TOML `prompt`;# fail-closed on multi-injection). `hook-body` lands under `.gemini/hooks/` (the# cursor model — a single `.gemini/` prefix at both scopes, no `tools/hooks/`), and# the carried hook command is path-rewritten `tools/hooks/`→`.gemini/hooks/` by# gemini.py's settings-merge helper. `hook-wiring` is `merge-json` into a shared# `.gemini/settings.json`: gemini.py's bespoke `_project_settings_json` helper (the# cursor `_project_hooks_json` shape, NOT the generic merge_json module) writes the# `hooks` map (event-remapped via `hook-event-map`, FAIL-CLOSED on an unmapped event# — the copilot precedent, unlike cursor's fail-open) AND a static `context.fileName`# bridge sourced from `context-filenames`, in one managed-merge that preserves# adopter keys. SINGLE-WRITER (cursor model): the file is written only when the pack# ships hook-wiring — repo-scope install writes merge-json targets whole-file, so a# settings.json emitted for every pack would clobber another pack's hooks. The# `context` bridge rides in that same write; the base `core` pack ships both the# session-start wiring and `AGENTS.md`, so the bridge lands with it. `kiro-ide-hook` is dropped# (Kiro-only), declared in the table form. Scope prefix is identical at both scopes# (the claude-code/cursor pattern), so the user-scope home is the generic# user-rooting of the repo-relpath — NO gemini-specific prefix rewrite.# Distribution-only: gemini is NOT in SELF_HOST_ADAPTERS.# ---------------------------------------------------------------------------
[[adapter.gemini.projection]]primitive = "skill"mode = "direct-directory"# Cohort skill home. Gemini CLI reads `.agents/skills/`# and `~/.agents/skills/`, and its docs now make the alias precedence-winning# over `.gemini/skills/`; route skills there to share one co-owned copy.target-path = ".agents/skills/"on-conflict = "prompt-then-preserve"
[[adapter.gemini.projection]]primitive = "agent"mode = "direct-file"target-path = ".gemini/agents/"frontmatter-mapping = "gemini-agent-frontmatter"on-conflict = "prompt-then-preserve"
[[adapter.gemini.projection]]primitive = "hook-body"mode = "direct-file"target-path = ".gemini/hooks/"on-conflict = "prompt-then-preserve"
[[adapter.gemini.projection]]primitive = "hook-wiring"mode = "merge-json"target-path = ".gemini/settings.json"managed-key = "hooks"on-conflict = "merge-managed-key-only"# Hook-event map. Our hook-wiring source uses Claude-native# PascalCase event names (e.g. `[[hooks.SessionStart]]`); Gemini's settings.json# uses its own event vocabulary. gemini.py's settings-merge helper reads this table# to translate each source event; a source event with no entry here **fails the# build** (fail-closed — the copilot-hooks-json precedent, deliberately unlike# cursor's fail-open drop-with-log). The map lives in the contract, not the adapter# code.hook-event-map = { SessionStart = "SessionStart", SessionEnd = "SessionEnd", UserPromptSubmit = "BeforeAgent", PreToolUse = "BeforeTool", PostToolUse = "AfterTool", Stop = "AfterAgent" }# Static `context.fileName` bridge. gemini.py's settings-merge# helper emits `context = { fileName = <this list> }` into the same `.gemini/settings.json`# so Gemini CLI reads the canonical `AGENTS.md` (and `GEMINI.md` if present). Data# lives in the contract, not a literal in code; written in the single hook-wiring# settings.json write (core ships both the wiring and AGENTS.md).context-filenames = ["AGENTS.md", "GEMINI.md"]
[[adapter.gemini.projection]]primitive = "command"mode = "gemini-command-toml"target-path = ".gemini/commands/"on-conflict = "prompt-then-preserve"
# kiro-ide-hook — dropped (Kiro-only primitive). Declared in the table form, not the# projection array, because the schema's array `primitive` enum admits only the five# standard primitives (the kiro-ide / kiro-cli / cursor precedent).[adapter.gemini.projections.kiro-ide-hook]mode = "dropped"
# Scope dimension for Gemini. Gemini's `.gemini/` prefix is# identical at repo and user scope (the claude-code/cursor shape), so the divergent# user-scope home is produced by the generic user-root resolution, not a# gemini-specific prefix rewrite. Every projected target (skills, agents, commands,# settings.json, AND hook bodies at `.gemini/hooks/`) is under `.gemini/`, so the# two allowed-prefix lists are identical. `.agentbundle/` carries the install state# file at both scopes.[adapter.gemini.scope]repo = "."user = "~"# Skills route to the shared `.agents/skills/` home (both scopes);# agents/hooks stay under `.gemini/` (moves only the skill primitive).allowed-prefixes.repo = [".agents/skills/", ".gemini/", ".agentbundle/"]allowed-prefixes.user = [".agents/skills/", ".gemini/", ".agentbundle/"]
# ---------------------------------------------------------------------------# Frontmatter mappings — rewrite rules applied when projecting a primitive# ---------------------------------------------------------------------------
# Kiro agent frontmatter v0.9: renames Claude Code agent fields to Kiro's schema.# Adapters consume the RENAME/NORMALIZE/VALUES rules below generically — no# per-field rename dictionary is hardcoded in Python. The emitted field *set* is# a separate concern and is bounded in `adapters/kiro.py` (`_IDE_AGENT_FIELDS` /# `_CLI_AGENT_FIELDS`), because these projectors iterate source frontmatter and# would otherwise pass unmapped keys through verbatim. A rename target added# here must also be a member of that set or the rule is a silent no-op;# `test_adapter_kiro_cli.py` / `test_adapter_kiro_ide.py` assert that coupling.[frontmatter-mapping."kiro-ide-agent-frontmatter-v0.9"]
[frontmatter-mapping."kiro-ide-agent-frontmatter-v0.9".description]rename = "description"
[frontmatter-mapping."kiro-ide-agent-frontmatter-v0.9".tools]normalize = "to-list"# Translate Claude Code tool names to specific Kiro tool *ids*. Kiro matches# an agent's `tools` entries by id or tag (never by Claude Code names); we# prefer ids for a faithful, least-privilege 1:1 mapping rather than the# broad category tags (the `read` tag also grants diagnostics + read_code;# `write` also grants delete_file). `WebSearch` is the exception — the IDE# exposes no granular web-search tool id, so it maps to the `web` tag.# Applied per list element after `to-list`; duplicates collapse and an# unmapped token drops with a build-time warning. These are Kiro IDE ids;# the Kiro CLI's short names land in the kiro-cli adapter.values = { Read = "read_file", Grep = "grep_search", Glob = "file_search", Edit = "str_replace", Write = "fs_write", MultiEdit = "str_replace", Bash = "execute_bash", WebFetch = "web_fetch", WebSearch = "web" }
[frontmatter-mapping."kiro-ide-agent-frontmatter-v0.9".model]rename = "model"# Translate Claude Code's friendly aliases to Kiro's documented model IDs# (https://kiro.dev/docs/cli/custom-agents/configuration-reference/). A# source value not in the map is dropped from the JSON output, leaving# Kiro to fall back to its CLI default with a warning rather than# rejecting the agent for an unknown model.values = { opus = "claude-opus-4.6", sonnet = "claude-sonnet-4.5", haiku = "claude-haiku-4.5" }
# kiro-cli agent frontmatter v1.0: rewrite rules from Claude Code# agent markdown to Kiro CLI JSON. Tools map to CLI short-names (not IDE ids).# The `kiro` CLI matches agents' `tools` entries by short name; the IDE uses# ids — hence the separate table rather than reusing kiro-ide-agent-frontmatter-v0.9.[frontmatter-mapping."kiro-cli-agent-frontmatter-v1.0"]
[frontmatter-mapping."kiro-cli-agent-frontmatter-v1.0".description]rename = "description"
[frontmatter-mapping."kiro-cli-agent-frontmatter-v1.0".tools]normalize = "to-list"# Translate Claude Code tool names to Kiro CLI short-name tool tokens.# Applied per list element after `to-list`; duplicates collapse and an# unmapped token drops with a build-time warning.values = { Read = "read", Grep = "grep", Glob = "glob", Edit = "write", Write = "write", MultiEdit = "write", Bash = "shell", WebFetch = "web_fetch", WebSearch = "web_search" }
[frontmatter-mapping."kiro-cli-agent-frontmatter-v1.0".model]rename = "model"values = { opus = "claude-opus-4.6", sonnet = "claude-sonnet-4.5", haiku = "claude-haiku-4.5" }
# Codex agent frontmatter v0.8:# rewrite rules from claude-code-style markdown agent (YAML frontmatter with# `name`, `description`, optionally `tools`, `model`) to codex's TOML agent# shape. `model` maps Claude-style aliases to OpenAI model IDs. `tools`# maps Claude-style tool names to Codex capability intents; the# `codex-agent-toml` projection reduces duplicate intents to documented# config keys (`sandbox_mode`, `[features].shell_tool`, `web_search`,# `[tools].web_search`) and never emits a generic `tools = [...]` array.# Codex has no named Read/Grep/Glob tool allowlist, so any mapped `read`# intent retains the default shell tool inside `sandbox_mode = "read-only"`;# mapped `write` remains the only route to a writable sandbox.# The markdown body lands in TOML `developer_instructions` via a# mode-level convention in the `codex-agent-toml` projection mode, NOT# via a rename rule (frontmatter mapping is keyed on frontmatter fields;# the body is not one).[frontmatter-mapping."codex-agent-frontmatter-v0.8"]
[frontmatter-mapping."codex-agent-frontmatter-v0.8".name]rename = "name"
[frontmatter-mapping."codex-agent-frontmatter-v0.8".description]rename = "description"
[frontmatter-mapping."codex-agent-frontmatter-v0.8".tools]normalize = "to-list"values = { Read = "read", Grep = "read", Glob = "read", Edit = "write", Write = "write", MultiEdit = "write", Bash = "shell", WebFetch = "web_search", WebSearch = "web_search" }
[frontmatter-mapping."codex-agent-frontmatter-v0.8".model]rename = "model"values = { opus = "gpt-5.5", sonnet = "gpt-5.5", haiku = "gpt-5.4-mini" }related-values = { model_reasoning_effort = { opus = "xhigh", sonnet = "medium", haiku = "medium" } }
# Copilot agent frontmatter v0.10:# rewrite rules from claude-code-style markdown agent (YAML frontmatter with# `name`, `description`, optionally `tools`, `model`) to Copilot's `.agent.md`# shape. `name` / `description` pass through (identity renames). `tools` is# NOT a rename rule — the `copilot-agent-md` mode emits it verbatim after an# allow-list validation (Copilot's parser self-resolves the Claude names), so# a `values` map here would be redundant. `model` is absent from this mapping# and so drops on projection (the CLI ignores it; our values aren't Copilot# model ids — copilot-cli#2133/#1195). `target` is never emitted.[frontmatter-mapping."copilot-agent-frontmatter-v0.10"]
[frontmatter-mapping."copilot-agent-frontmatter-v0.10".name]rename = "name"
[frontmatter-mapping."copilot-agent-frontmatter-v0.10".description]rename = "description"
# Cursor agent frontmatter v0.11:# rewrite rules from claude-code-style markdown agent (YAML frontmatter with# `name`, `description`, optionally `tools`, `model`) to Cursor's subagent `.md`# shape. `name` / `description` / `model` pass through (identity renames; `model`# is emitted verbatim — Cursor resolves a known id or falls back to inherit, no# alias translation). `tools` is **absent** from this mapping: Cursor subagents# have no per-agent tool allowlist, so `cursor.py` drops the source `tools` and# derives a `readonly` flag for non-mutating agents (an agent whose declared# tool set contains none of Edit/Write/MultiEdit/NotebookEdit). `is_background`# is not emitted. This is the documented-degradation shape accepted for# Copilot's tool handling.[frontmatter-mapping."cursor-agent-frontmatter-v0.11"]
[frontmatter-mapping."cursor-agent-frontmatter-v0.11".name]rename = "name"
[frontmatter-mapping."cursor-agent-frontmatter-v0.11".description]rename = "description"
[frontmatter-mapping."cursor-agent-frontmatter-v0.11".model]rename = "model"
# Gemini agent frontmatter (v0.13):# rewrite rules from claude-code-style markdown agent (YAML frontmatter with# `name`, `description`, optionally `tools`, `model`) to Gemini CLI's subagent `.md`# shape. `name` / `description` pass through (identity renames). Unlike Cursor,# Gemini has a real per-agent tool allowlist, so `tools` is KEPT and name-mapped to# Gemini's built-in tool identifiers (`normalize = "to-list"`, applied per element;# duplicates collapse — `Edit`+`MultiEdit`→single `replace` — and an unmapped token# drops with a build-time log line). `model` aliases map to Gemini model IDs; a# source value not in the map drops, leaving Gemini's default.[frontmatter-mapping."gemini-agent-frontmatter"]
[frontmatter-mapping."gemini-agent-frontmatter".name]rename = "name"
[frontmatter-mapping."gemini-agent-frontmatter".description]rename = "description"
[frontmatter-mapping."gemini-agent-frontmatter".tools]normalize = "to-list"# Claude Code tool names → Gemini CLI built-in tool identifiers# (https://google-gemini.github.io/gemini-cli/docs/tools/). `MultiEdit` maps to# `replace` for parity with the codex/claude/kiro maps (collapses onto `Edit`).values = { Read = "read_file", Grep = "grep_search", Glob = "glob", Edit = "replace", MultiEdit = "replace", Write = "write_file", Bash = "run_shell_command", WebFetch = "web_fetch", WebSearch = "google_web_search", LS = "list_directory" }
[frontmatter-mapping."gemini-agent-frontmatter".model]rename = "model"# Tier-preserving map to Gemini's stable 2.5 line. opus→pro (most capable),# sonnet→flash (balanced), haiku→flash-lite (fastest). A source value not in the# map is dropped, leaving Gemini's CLI default.values = { opus = "gemini-2.5-pro", sonnet = "gemini-2.5-flash", haiku = "gemini-2.5-flash-lite" }Download the raw file.
See the overview section for this file.
Its schema is adapter.schema.json.