Skip to content
GitHub

adapter.toml

The adapter.toml member of the catalogue specification.

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.