kitbash / docs / adapters

Adapters & targets

Nine compile targets, each with its own output path, detection rule, loading mode, and capability set. This page is the reference for what every one of them actually writes.

An adapter turns one installed skill into the native format of one coding agent. kitbash compile runs every detected adapter over every installed skill and writes the result. Two emission styles exist: per-skill files (Claude Code, Cursor, the vendor-neutral .agents/skills/ path, Copilot, Cline, Devin, Gemini CLI) and marker-merged sections in a shared file (AGENTS.md, CONVENTIONS.md).

Lazy vs eager

The single most important property of a target is how it loads a compiled skill.

  • lazy The target keeps the skill out of the context window until it is invoked. Only a standing stub โ€” a name and description the agent can match against โ€” is resident. Claude Code SKILL.md files, Cursor agent-requested rules, the vendor-neutral .agents/skills/ path, the .github/skills/ and .gemini/skills/ directories Copilot and Gemini CLI read, and Devin rules with a model_decision trigger all work this way.
  • eager The target has no mechanism for deferred loading, so the entire compiled body sits in context every session. AGENTS.md, CONVENTIONS.md, and Cline rule files all work this way.

Kitbash compiles to the cheapest loading mode each target actually supports, so six of the nine carry only a stub. The standing token tax is what a skill costs on the targets whose only mode is eager: a skill authored with disclosure = "lazy" cannot lazy-load there โ€” the target simply has nowhere to put a stub. Kitbash refuses to let that happen quietly: on every eager target, a lazy-authored skill produces a warning that names the real cost and the limit the author declared.

โš  prereview โ†’ agentsmd: agentsmd is eager and cannot lazy-load;
  this skill costs ~517 tokens standing every session (declared limit: 60)

Multiply that by the number of installed skills and it is the whole context budget problem. The benchmark page quantifies the gap between lazy and eager delivery of the same skill.

Capability matrix

Detection is a filesystem check against the repo root โ€” presence of the directory or file the agent already uses. Capabilities are matched against a skill's targets.requires; an empty set means the adapter can only carry instructions. Every adapter's set is empty today โ€” emit() writes a SKILL.md (and, for Claude Code, slash-command shims) and nothing else, so none of them yet delivers scripts, hooks, subagents, or network.

AdapterOutput pathDetectionLoadingCapabilities
claude-code .claude/skills/<name>/SKILL.md
.claude/commands/<cmd>.md
.claude/ exists lazy none
cursor .cursor/rules/<name>.mdc .cursor/ exists lazy none
agents .agents/skills/<name>/SKILL.md .agents/ or .codex/ exists lazy none
copilot .github/skills/<name>/SKILL.md .github/ exists lazy none
cline .clinerules/<name>.md .clinerules/ exists eager none
windsurf .devin/rules/<name>.md
fallback .windsurf/rules/<name>.md
.devin/ or .windsurf/ exists lazy none
gemini .gemini/skills/<name>/SKILL.md GEMINI.md or .gemini/ exists lazy none
aider CONVENTIONS.md (merged) CONVENTIONS.md or .aider.conf.yml exists eager none
agentsmd AGENTS.md (merged) always eager none

No adapter declares any capability today. emit() writes a SKILL.md โ€” plus slash-command shims on Claude Code โ€” but it does not copy a skill's scripts/, install a hook, or wire a subagent, so claiming any of those would report full support for output referencing files the compiler never produced. Because every capability set is empty, any non-empty targets.requires โ€” scripts, hooks, subagents, or network โ€” degrades on every one of the nine targets, Claude Code included. A capability will be re-added only alongside the emit code that delivers its primitive.

Detection can be overridden. Set targets under [project] in kitbash.toml and Kitbash compiles exactly that list instead of probing the filesystem; an unknown id there is a hard error. See project config.

The nine adapters

Every emitted file carries a generated-file header naming the source skill and version, which is what makes stale output safe to delete on the next compile:

<!-- generated by kitbash โ€” do not edit; source: .kitbash/skills/prereview @ 0.1.0 -->

claude-code

Writes .claude/skills/<name>/SKILL.md with YAML frontmatter carrying name and a quoted description. The description is emitted as a double-quoted YAML scalar, so descriptions containing :, ", [, or { stay valid frontmatter.

It is the only adapter that emits native slash commands. Each entry in the manifest's triggers.commands becomes a thin shim at .claude/commands/<cmd>.md that tells the agent to read the SKILL.md and follow it exactly, passing along $ARGUMENTS. The body is not duplicated; the shim only points at it.

It declares no capabilities. The slash-command shims and SKILL.md are the whole of what it emits โ€” it does not copy a skill's scripts/, install a hook, or wire a subagent โ€” so a skill that requires any of those degrades here exactly as it does everywhere else.

cursor

Writes .cursor/rules/<name>.mdc with frontmatter alwaysApply: false plus the skill description. That combination makes the rule agent-requested rather than always-on, which is why Cursor counts as lazy despite having no capabilities.

agents

Writes .agents/skills/<name>/SKILL.md with the same name and quoted description frontmatter as Claude Code. This is the vendor-neutral Agent Skills path: Codex reads it as its only repo path, and Cursor, Copilot, Gemini CLI, Roo, Amp, OpenCode, Zed and Antigravity read it too โ€” one target instead of nine adapters. Only the metadata is preloaded, so it is lazy.

Detection is deliberately narrow: .agents/ or .codex/. Agents that also have a native path already get their own adapter, and emitting both would duplicate the skill for no benefit. It writes only SKILL.md and declares no capabilities.

copilot

Writes .github/skills/<name>/SKILL.md with name and a quoted description in frontmatter โ€” the agentskills.io layout. Copilot loads only that frontmatter until the skill is needed, so the target is lazy. Detection is the presence of .github/, which most repositories have โ€” expect this target to fire more often than the others.

Before 0.8.0 this adapter wrote .github/instructions/<name>.instructions.md with applyTo: "**", a glob that matched every single request and made the target eager.

cline

Writes .clinerules/<name>.md with no frontmatter at all โ€” header and body only. Cline rule files are always loaded, so this is eager.

windsurf

Windsurf became Devin Desktop on 2026-06-02. .devin/rules/ is the preferred path and .windsurf/rules/ remains a supported fallback, so the adapter emits to whichever the repo actually has: .devin/rules/<name>.md when .devin/ exists, .windsurf/rules/<name>.md otherwise.

Directory rules carry an activation trigger in their frontmatter. Kitbash emits trigger: model_decision alongside the description, which means the description is what sits in context and the body loads only when the model judges it relevant โ€” lazy, not eager.

gemini

Writes .gemini/skills/<name>/SKILL.md with name and a quoted description in frontmatter. Gemini CLI injects only that name and description into the system prompt at session start and pulls the body in through its activate_skill tool, so the target is lazy. Detection is unchanged: either an existing GEMINI.md or a .gemini/ directory.

Before 0.8.0 this adapter merged into GEMINI.md, which Google's own docs describe as persistent workspace-wide background โ€” the whole body, every session.

aider

Merges into CONVENTIONS.md, the file Aider reads when it is wired up through .aider.conf.yml or --read. Detection fires on an existing CONVENTIONS.md or an .aider.conf.yml. Always in context, so eager.

agentsmd

Merges into AGENTS.md. This is the floor: its detect function returns true unconditionally, so every repo gets AGENTS.md output whether or not any agent-specific directory exists. Codex and a long tail of other tools read it.

How marker merging works

The two shared-file adapters โ€” agentsmd and aider โ€” do not own their file. Each skill gets a marker-delimited section:

<!-- kitbash:begin prereview -->
<!-- generated by kitbash โ€” do not edit; source: .kitbash/skills/prereview @ 0.1.0 -->

## Skill: prereview

...body...
<!-- kitbash:end prereview -->

On compile, a section whose markers already exist is replaced in place; a new one is appended. Anything you wrote by hand outside the markers is preserved untouched โ€” your AGENTS.md stays yours. Sections belonging to skills that are no longer installed are pruned whenever the file is rebuilt. If nothing wrote to a managed shared file during a compile โ€” AGENTS.md, GEMINI.md or CONVENTIONS.md โ€” then none of its generated sections are current, and all of them are pruned.

Upgrading from before 0.8.0. copilot and gemini used to write .github/instructions/<name>.instructions.md and a merged section in GEMINI.md. The next kitbash compile after upgrading prunes both: the stale .github/instructions/ files carrying the generated-file header are removed, and the generated sections in GEMINI.md are stripped. Anything you wrote in those files yourself is preserved โ€” only Kitbash's own marked output is touched.

Degradation

A skill declares what it needs from a target in its manifest:

[targets]
requires = ["scripts"]   # "scripts" | "hooks" | "subagents" | "network"
mode = "skill"

At compile time each required capability is checked against the adapter's capability list. Anything missing produces a warning naming the skill, the target, and the capability:

โš  verify โ†’ cursor: target lacks "scripts"; compiled instruction-only (degraded)

Because every adapter's capability set is currently empty, this is not a per-target quirk: any value in requires degrades on every one of the nine targets. Kitbash's emit() writes instructions โ€” a SKILL.md, and slash-command shims on Claude Code โ€” but it does not yet deliver the underlying primitives: it does not copy a skill's scripts/, install a hook, or wire a subagent, and no adapter reaches the network. Declaring the capability the compiler cannot honor would report full support for output that references files it never produced, which is exactly the silent capability loss the spec forbids.

Degraded means the instructions still compile and still ship โ€” the agent gets the prose, it just does not get the deterministic helper the skill wanted to run. That is often acceptable. What is never acceptable is not being told.

Spec ยง2. A compiler lacking a required capability MUST either emit a degraded variant with a visible warning, or fail under --strict. Silent degradation is a conformance violation.

kitbash explain

To interrogate one pairing, ask directly. explain takes a skill โ€” installed name, local path, or a remote source you have not installed โ€” and an adapter id:

kitbash explain prereview agentsmd
prereview โ†’ agentsmd: no capability degradation
  โš  loading: agentsmd is eager โ€” skill costs ~464 tokens standing every session (declared limit: 60)

When capabilities are missing it itemizes them:

verify โ†’ cursor: degraded
  โœ— requires "scripts" โ€” not supported by cursor; compiled instruction-only

The two findings are independent. A skill can be capability-clean and still carry a loading warning, as the first example shows.

Failing the build

Warnings are informational by default. Pass --strict to kitbash compile and any warning โ€” degradation, eager loading, output-path conflicts โ€” turns into a non-zero exit:

kitbash compile --strict
--strict: failing on 2 warning(s)

That is the flag for CI. It converts "we quietly lost the script hook on three of five agents" into a red build.

The matrix is derived

A skill's compatibility across agents is computed, never hand-written: targets.requires crossed with each adapter's capability set yields full, degraded, or unsupported per assistant. kitbash preview walks every adapter for one skill and prints each target's loading mode, standing cost, and degradation warnings; kitbash doctor reports which targets your repo detects and what the installed set costs standing. Publishing a hand-claimed compatibility matrix is not a thing here โ€” the compiler is the source of truth.

Related