kitbash / docs / cli

CLI reference

Every command the kitbash binary exposes, the flags it actually parses, the lines it actually prints, and the exit code it returns.

Synopsis

kitbash <command> [args]

The binary is a single dispatch table. It takes the first argument as the command name, hands the rest to that command, and exits with whatever number the command returns. There is no global option parser: flags are matched literally by each command, so --strict and --yes only mean something to the commands documented as accepting them.

Three cases are handled before dispatch:

  • help / --help / -h, or no argument at all โ€” print the usage listing and exit 0.
  • --version / -v โ€” print the version and exit 0.
  • Anything else that isn't a known command โ€” print kitbash: unknown command "โ€ฆ" to stderr, print the usage listing, exit 1.
$ kitbash --version
0.6.0

The usage listing is the command table itself:

$ kitbash help
kitbash 0.6.0 โ€” write a skill once, run it in every coding agent

Usage: kitbash <command> [args]

  init     Set up kitbash in this repository (kitbash.toml)
  install  Install a skill with pre-install review: gh:owner/repo[/path][@ref], owner/repo, or file:path (--yes; [policy] enforced)
  remove   Remove an installed skill
  list     List installed skills with versions and context cost
  compile  Emit native formats for every detected assistant (--strict)
  doctor   Detect assistants, report total standing context cost
  update   Update skills, showing instruction diffs before applying
  diff     Instruction/permission/budget diff between two skill versions
  lint     Schema, context budgets, dead references, injection heuristics (--strict; accepts a name, path, or uninstalled source)
  audit    Scan installed skills: permission drift, unsigned sources, injection heuristics
  preview  Render each adapter's output with per-agent token counts โ€” works on uninstalled sources (gh:owner/repo)
  explain  Why a compilation degraded on a given adapter (accepts a name, path, or uninstalled source)
  test     Run a skill's static evals: schema, budgets, dead refs, injection heuristics (--strict)
  gate     Run a gate-mode skill with a deterministic exit code
  search   Search the community index
  publish  Validate and publish a skill to the index
  lore     Build, query, and curate repo intelligence
  run      Run a declared pipeline (e.g. kitbash run ship)

Docs: https://github.com/singhharsh1708/kitbash

Every command runs against the current working directory as the project root. Skills live in .kitbash/skills/, config in kitbash.toml, pins in kitbash.lock.

Command summary

Ten commands are implemented. The other eight are wired to a placeholder that prints kitbash <name>: not implemented yet (lands in <milestone> โ€” see docs/roadmap.md) on stderr and returns exit code 2. They occupy the command surface deliberately, so docs, spec, and implementation grow against one interface.

CommandSummaryStatus
initSet up kitbash in this repository (kitbash.toml)Available
installInstall a skill with pre-install reviewAvailable
removeRemove an installed skillAvailable
listList installed skills with versions and context costAvailable
compileEmit native formats for every detected assistantAvailable
doctorDetect assistants, report total standing context costAvailable
updateUpdate skills, showing instruction diffs before applyingPlanned โ€” v0.2
diffInstruction/permission/budget diff between two skill versionsPlanned โ€” v0.2
lintSchema, context budgets, dead references, injection heuristicsAvailable
auditScan installed skills: permission drift, unsigned sources, injection heuristicsPlanned โ€” v0.2
previewRender each adapter's output with per-agent token countsAvailable
explainWhy a compilation degraded on a given adapterAvailable
testRun a skill's static evalsAvailable
gateRun a gate-mode skill with a deterministic exit codePlanned โ€” v0.3
searchSearch the community indexPlanned โ€” v0.4
publishValidate and publish a skill to the indexPlanned โ€” v0.4
loreBuild, query, and curate repo intelligencePlanned โ€” v0.5
runRun a declared pipeline (e.g. kitbash run ship)Planned โ€” v0.5

Do not script against planned commands. They exit 2, not 127 and not 1. If your CI treats any non-zero code as a failure, calling kitbash audit today will fail the build with a message pointing at the roadmap.

Commands

init

kitbash init

Writes kitbash.toml at the current working directory and creates .kitbash/skills/. The generated config carries a [project] table with every setting commented out: an example targets line, and a commented [policy] block covering allow_sources, deny_network, deny_write, and max_budget. Nothing is enabled until you uncomment it.

The command is idempotent in the safest possible way โ€” if kitbash.toml already exists it touches nothing, including the skills directory.

  • (none) Takes no arguments and no flags.
$ kitbash init
created kitbash.toml and .kitbash/skills/
next: kitbash install <gh:owner/repo | owner/repo | file:path>, then kitbash compile

On a repo that already has one:

$ kitbash init
kitbash.toml already exists โ€” nothing to do

Exit codes: 0 always โ€” both the created and the already-exists path succeed.

install

kitbash install <gh:owner/repo[/path][@ref] | owner/repo | file:path> [--yes]

Fetches a skill, shows a review block, asks for confirmation, copies it into .kitbash/skills/<name>/, and records a content hash in kitbash.lock.

The source argument is the first argument that does not start with -. Five forms resolve:

FormResolves to
gh:owner/repoShallow git clone of https://github.com/owner/repo.git
gh:owner/repo/path/to/skillThe same clone, then a subdirectory
gh:owner/repo@refFull clone, then git checkout <ref> โ€” branch, tag, or commit
owner/repoSame as gh:, but only if no local path of that name exists
file:./path or ./pathA directory on disk, resolved against the project root

Resolution order matters: a bare owner/repo is checked as a local path first, so a directory named foo/bar in your repo wins over the GitHub repo foo/bar. Subpaths are confined to the clone โ€” a subpath that resolves outside it is rejected as escaping the repository. GitHub sources require git on PATH; without it the command tells you to install git or use file:. The temporary clone is removed whether the install succeeds or fails.

Before anything is written, the review block prints what the skill declares: budget, standing limit, disclosure mode, target mode, tool permissions, network and write flags, and any required capabilities. A skill with only a SKILL.md and no skill.toml is flagged as unmanifested. Every failing or warning static check is printed too, so you see budget overruns and injection heuristics before you consent.

A failed safety lint โ€” visible-text, dynamic-context, or remote-exec โ€” blocks the install outright: the command prints the reason, exits 1, and writes nothing. This holds with no kitbash.toml present and is not bypassable by --yes. (Through 0.8.0 these were printed but did not stop the install; 0.8.1 fixed that.) Schema and quality checks do not block install โ€” they surface at test.

Because install copies the whole skill directory, the three safety lints scan every non-binary file in it, not just SKILL.md: a curl โ€ฆ | sh in scripts/setup.sh or hidden text in a sibling .md is caught the same way it is in the body. A symlink is itself flagged โ€” it can point anywhere and the copy follows it verbatim. Each hit reports the offending file, and the block is enforced exactly like a body hit.

  • --yes, -y Skip the confirmation prompt. Does not skip the review block, the safety lints, or [policy].

The prompt only appears when both stdin and stdout are TTYs. In a pipe, a CI runner, or a non-interactive shell, install proceeds without asking โ€” --yes is for readability, not for unblocking automation.

$ kitbash install gh:singhharsh1708/kitbash/examples/skills/prereview
review: prereview@0.1.0 โ€” Reviews a diff against the team's actual standards
  budget 1500 tokens ยท standing 60 ยท lazy disclosure ยท mode skill
  permissions: tools [read, grep, bash:git *] ยท network no ยท write no
install prereview@0.1.0? [y/N] y
installed prereview@0.1.0
  pinned in kitbash.lock
next: kitbash compile

Only y or yes, case-insensitive, count as consent. Anything else โ€” including an empty line โ€” aborts:

aborted โ€” nothing installed.

[policy] is a hard gate. If kitbash.toml declares a [policy] table, the source is matched against allow_sources and the manifest against deny_network, deny_write, and max_budget. Violations are printed and the install is refused โ€” --yes does not bypass it, because the flag suppresses a question and policy is not a question.

$ kitbash install gh:stranger/skills/exfil --yes
review: exfil@1.0.0 โ€” Summarizes your codebase
  budget 4000 tokens ยท standing 90 ยท lazy disclosure ยท mode skill
  permissions: tools [read, bash] ยท network YES ยท write no
  โš  lint: injection โ€” heuristic match โ€” review: data-exfiltration shape
  โœ— policy: source "gh:stranger/skills/exfil" is not in allow_sources (gh:your-org/*)
  โœ— policy: exfil declares network permission and deny_network = true
blocked by [policy] in kitbash.toml.

Reinstalling is explicit. If the skill name is already present the command refuses rather than overwriting, and tells you the two-step sequence.

prereview is already installed. To reinstall: kitbash remove prereview && kitbash install gh:singhharsh1708/kitbash/examples/skills/prereview

Exit codes: 0 when installed. 1 when no source was given, the source string is malformed, git is missing, the clone or ref checkout fails, the subpath is missing or escapes the repo, the folder has no SKILL.md, the skill is already installed, a policy rule is violated, or the prompt is declined.

remove

kitbash remove <skill-name>

Deletes .kitbash/skills/<name>/ and drops the skill's entry from kitbash.lock. It takes an installed skill name only โ€” not a path and not a source โ€” and the name must match the KSF name rule (lowercase, starting with a letter, 2โ€“41 characters).

Removing does not clean up compiled output. The next kitbash compile prunes the generated files this skill owned, which is why the command says so.

  • <skill-name> Required. The installed name, as shown by kitbash list.
$ kitbash remove prereview
removed prereview
next: kitbash compile (prunes this skill's generated outputs)

If the name isn't installed, the error lists what is:

$ kitbash remove prereviw
prereviw is not installed.
  installed: prereview, release-notes

Exit codes: 0 when removed. 1 when the name is missing, malformed, or not installed.

list

kitbash list

Prints one line per installed skill, sorted by name: name and version, declared context budget, declared standing limit, target mode, an [unmanifested] marker for SKILL.md-only skills, and the description. Directories under .kitbash/skills/ without a SKILL.md are skipped, so aborted installs and stray folders don't break the listing.

  • (none) Takes no arguments and no flags.
$ kitbash list
prereview@0.1.0  budget=1500  standing=60  mode=skill  โ€” Reviews a diff against the team's actual standards
release-notes@0.2.0  budget=2200  standing=80  mode=skill  โ€” Turns merged PRs into a release note
scratch@0.0.0  budget=6000  standing=250  mode=skill  [unmanifested]  โ€” Imported skill (no manifest)

An empty project says so instead of printing nothing:

no skills installed โ€” kitbash install <source>

Exit codes: 0 always, including when nothing is installed.

compile

kitbash compile [--strict]

Compiles every installed skill for every configured target and writes the results to disk. Targets come from [project] targets in kitbash.toml; with no config or no targets key, Kitbash autodetects by looking for each adapter's marker file or directory. An unknown target name in the config is a hard error before anything is written.

Per-skill files (.claude/skills/, .claude/commands/, .cursor/rules/, .agents/skills/, .github/skills/, .gemini/skills/, .clinerules/, .devin/rules/ or .windsurf/rules/) are written whole. Shared files (AGENTS.md, CONVENTIONS.md) are marker-merged: each skill owns a <!-- kitbash:begin <name> --> โ€ฆ <!-- kitbash:end <name> --> section and your own prose around it is preserved.

Compile is also the garbage collector. Files in managed directories that carry the generated header but were not written by this run are deleted, including the .github/instructions/ files older versions emitted. A managed shared file that nothing wrote to this run โ€” AGENTS.md, GEMINI.md or CONVENTIONS.md โ€” has all of its generated sections pruned, since none of them are current. That is how removing a skill eventually removes its output.

Budgets are enforced here, not merely reported. A manifested skill whose compiled body exceeds context.budget, or whose standing stub exceeds context.standing, fails the whole compile before any file is written. Unmanifested skills never declared those numbers, so their overruns are measured and warned about instead.

  • --strict Exit 1 if the run produced any warnings. Files are still written; the failure is reported after.
$ kitbash compile
โ†’ .claude/skills/prereview/SKILL.md
โ†’ .claude/commands/prereview.md
โ†’ .cursor/rules/prereview.mdc
โ†’ AGENTS.md
โœ‚ removed .claude/commands/old-review.md (stale)
โš  prereview โ†’ agentsmd: agentsmd is eager and cannot lazy-load; this skill costs ~517 tokens standing every session (declared limit: 60)
compiled 1 skill(s) for 3 agent target(s)

Warnings cover eager-target standing cost, capability degradation, unmanifested skills, and output-path conflicts where two skills write the same file. With --strict those same warnings end the run:

$ kitbash compile --strict
โ†’ .claude/skills/prereview/SKILL.md
โ†’ AGENTS.md
โš  prereview โ†’ agentsmd: agentsmd is eager and cannot lazy-load; this skill costs ~517 tokens standing every session (declared limit: 60)
compiled 1 skill(s) for 2 agent target(s)
--strict: failing on 1 warning(s)

A budget violation stops the run outright:

โœ— prereview: compiled body is ~1712 tokens, over its budget of 1500

With nothing installed, compile still prunes stale output, then reports and succeeds:

no skills installed โ€” kitbash install <source> to add one

Exit codes: 0 on a clean compile. 1 on an unknown target in kitbash.toml, a manifested skill over its budget or standing limit, or --strict with at least one warning.

doctor

kitbash doctor

The repo health check. It reports which of the nine adapters were detected, how many skills are installed, the total standing context cost of their stubs, the worst-case active cost if every skill fired in one session, and then verifies the lockfile.

Three integrity conditions are checked: skills installed with no kitbash.lock at all, a locked skill whose files on disk no longer match their recorded hash, and a skill present on disk but absent from the lockfile. All three mean the code your agents load is not the code somebody reviewed.

If [policy] is configured, doctor rechecks it against everything already installed โ€” using each skill's recorded source from the lockfile. That catches skills that predate the policy or were copied in without going through kitbash install.

  • (none) Takes no arguments and no flags.
$ kitbash doctor
detected targets:
  โœ“ claude-code
  โœ“ cursor
  โœ— agents
  โœ— copilot
  โœ— cline
  โœ— windsurf
  โœ— gemini
  โœ— aider
  โœ“ agentsmd (floor: Codex, Gemini CLI, anything reading AGENTS.md)
installed skills: 2
standing context cost: ~98 tokens (stubs); worst-case active: 3700 tokens (budgets)
lock integrity: ok
policy: ok

The agentsmd target is always detected โ€” AGENTS.md is the floor every repo compiles to. A failing run looks like this:

installed skills: 2
standing context cost: ~98 tokens (stubs); worst-case active: 3700 tokens (budgets)
  โœ— prereview: integrity drift โ€” installed files differ from kitbash.lock
  โœ— scratch: installed but not pinned in kitbash.lock โ€” reinstall to pin it.
2 integrity problem(s) โ€” reinstall or investigate

A lockfile entry for a skill that is not installed is a warning, not a failure โ€” that is the ordinary state after removing a skill from disk by hand.

Exit codes: 0 when detection, integrity, and policy all pass. 1 on a missing lockfile with skills installed, integrity drift, an unpinned installed skill, or a policy violation.

test

kitbash test [skill-name] [--strict]

Runs the static-tier evals over installed skills: manifest presence, template and reference resolution, measured body tokens against context.budget, measured stub tokens against context.standing, artifact reference shape (name@version), slash-prefixed trigger commands, a gate-verdict check, schema-conformance lints, prompt-injection heuristics, and three safety checks on the body itself โ€” visible-text, dynamic-context, and remote-exec.

The gate-verdict check applies only to mode = "gate" skills: a gate has to be able to produce a deterministic verdict, so it must carry a scripts/ directory to run or a declared artifacts.produces. A gate with neither fails lint and test โ€” nothing there can ground a pass/fail.

No eval file is required โ€” these checks always run. Failures print โœ—, warnings print โš , passing checks print ยท. Injection heuristics and schema lints only ever warn: a security skill may legitimately quote the phrases it defends against, so the tool refuses to hard-fail on a pattern match.

The three safety checks are the exception โ€” each is a hard failure that exits 1 with or without --strict, and each also blocks install:

  • visible-text rejects a body containing zero-width characters, bidi overrides, or codepoints from the Unicode Tags block (U+E0000โ€“U+E007F), which encodes plain ASCII invisibly. Those are instructions a human reviewer cannot see while the agent still reads them. The report names each run of hidden characters by codepoint.
  • dynamic-context rejects ! followed by a backtick command substitution in the body. That construct executes at load time, before the model reads anything, so it never gets a pass.
  • remote-exec rejects a download-and-execute pipeline in the body prose โ€” curl โ€ฆ | sh, eval "$(curl โ€ฆ)", base64 -d | sh, PowerShell iex/iwr, save-then-chmod +x-then-run, remote-archive extract-run. This is the payload that hides in a "Prerequisites" section rather than the manifest. A [policy] may exempt it with deny_remote_exec = false; the other two are never exemptible.

Kitbash fans one skill out to nine files, several of them always in context, which is why none of these is a warning.

The optional positional argument filters to a single installed skill. Unlike lint, test does not accept a path or a remote source.

  • [skill-name] Optional. Restrict the run to one installed skill.
  • --strict Exit 1 if any check warned, not just if one failed.
$ kitbash test
โœ“ prereview
    ยท manifest โ€” prereview@0.1.0
    ยท references
    ยท budget โ€” body ~464 tok / budget 1500
    ยท standing โ€” stub ~38 tok / limit 60
    ยท artifacts โ€” produces 1, consumes 0
    ยท visible-text โ€” no hidden characters
โš  scratch
    โš  manifest โ€” unmanifested (SKILL.md only) โ€” defaults applied
    ยท references
    ยท budget โ€” body ~1204 tok / budget 6000
    ยท standing โ€” stub ~61 tok / limit 250
    โš  injection โ€” heuristic match โ€” review: role reassignment
    ยท visible-text โ€” no hidden characters

tested 2 skill(s) ยท 0 failure(s) ยท 2 warning(s) (static tier)

If any skill has an evals/ directory, test says plainly that only the static tier ran:

note: evals/ present โ€” audit & behavioral tiers need a runner (not in this build); static tier ran

A real failure โ€” here a dead template reference and a malformed artifact id:

โœ— release-notes
    ยท manifest โ€” release-notes@0.2.0
    โœ— references โ€” release-notes: template references missing /repo/.kitbash/skills/release-notes/prompts/summarize.md
    โœ— artifacts โ€” malformed: changelog (want name@version)

tested 1 skill(s) ยท 2 failure(s) ยท 0 warning(s) (static tier)

Exit codes: 0 when nothing failed (and, under --strict, nothing warned). 1 when the named skill is not installed, no skills are installed, any check failed, or --strict saw a warning.

lint

kitbash lint [skill-name | path | source] [--strict]

Runs the same static checks as test and prints the same report, but resolves its target far more broadly. The argument is tried in this order: an existing path relative to the project root, then an installed skill name, then a fetchable source (gh:owner/repo[/path][@ref], owner/repo, file:path). With no argument it lints everything installed.

That last case is the point: you can lint a stranger's skill before it ever touches your repo. The remote is cloned to a temporary directory, checked, and deleted.

Two of those checks are hard failures rather than warnings, and they apply to an uninstalled source exactly as they do to an installed skill: visible-text, which rejects hidden characters โ€” zero-width, bidi overrides, the Unicode Tags block โ€” and dynamic-context, which rejects ! plus backtick command substitution in the body. Either one exits 1 without --strict:

$ kitbash lint gh:stranger/skills/helper
โœ— helper
    ยท manifest โ€” helper@1.0.0
    ยท references
    ยท budget โ€” body ~318 tok / budget 1500
    ยท standing โ€” stub ~24 tok / limit 60
    โœ— visible-text โ€” 2 run(s) of invisible characters (U+200B, U+E0041) โ€” instructions a reviewer cannot see
    โœ— dynamic-context โ€” command substitution in the skill body executes before the model sees it: !`cat ~/.ssh/id_rsa`

linted 1 skill(s) ยท 2 failure(s) ยท 0 warning(s)
  • [target] Optional. A path, an installed name, or an uninstalled source. Omit to lint every installed skill.
  • --strict Exit 1 if any check warned, not just if one failed.
$ kitbash lint gh:stranger/skills/summarize
โš  summarize
    โš  manifest โ€” unmanifested (SKILL.md only) โ€” defaults applied
    ยท references
    โš  budget โ€” body ~7310 tok / budget 6000
    ยท standing โ€” stub ~112 tok / limit 250
    โš  injection โ€” heuristic match โ€” review: override of prior instructions, data-exfiltration shape
    ยท visible-text โ€” no hidden characters

linted 1 skill(s) ยท 0 failure(s) ยท 3 warning(s)

The same run under --strict adds one line and fails:

--strict: failing on 3 warning(s)

An unresolvable argument tells you what it tried:

$ kitbash lint prereviw
prereviw: not found as a path or installed skill name (or pass a source: gh:owner/repo, file:path)
  installed: prereview, release-notes

Exit codes: 0 when nothing failed (and, under --strict, nothing warned). 1 when the target cannot be resolved or loaded, no argument was given and nothing is installed, any check failed, or --strict saw a warning.

explain

kitbash explain <skill-name | path | source> <adapter>

Answers one question: what does this skill lose on that agent? Both arguments are positional and both are required. The first resolves the same way as lint's target โ€” path, installed name, or fetchable source. The second names an adapter: claude-code, cursor, agents, copilot, cline, windsurf, gemini, aider, or agentsmd.

Explain reports two independent kinds of loss. Capability degradation: each entry in the skill's targets.requires that the adapter does not support, which compiles down to instruction-only. And loading degradation: a skill authored for lazy disclosure on an eager adapter, which pays its full body as standing context in every session.

  • <target> Required, first positional. Path, installed name, or uninstalled source.
  • <adapter> Required, second positional. One of the nine adapter ids.
$ kitbash explain prereview agentsmd
prereview โ†’ agentsmd: no capability degradation
  โš  loading: agentsmd is eager โ€” skill costs ~464 tokens standing every session (declared limit: 60)

No adapter declares a capability today โ€” emit() writes instructions but does not copy scripts/, install a hook, or wire a subagent โ€” so a skill that requires any of scripts, hooks, subagents, or network reports degraded on every one of the nine targets, Claude Code included:

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

Missing arguments print the usage line and the adapter list:

$ kitbash explain prereview
usage: kitbash explain <skill-name-or-path-or-source> <adapter>
  adapters: claude-code, cursor, agents, copilot, cline, windsurf, gemini, aider, agentsmd

Exit codes: 0 whether or not degradation was found โ€” explain reports, it does not judge. 1 when an argument is missing, the adapter name is unknown, the target cannot be resolved or loaded, or the body's template references cannot be resolved.

preview

kitbash preview <skill-name | path | source>

Renders, without writing anything, exactly what each adapter would emit โ€” every output path, every byte of frontmatter, the full compiled body โ€” plus that adapter's loading mode and what it costs in standing context. Target resolution is identical to lint and explain, so remote sources work.

Adapters come from [project] targets or autodetection, the same as compile. If kitbash.toml names an unknown target, preview errors out with the same message compile prints rather than falling back to every adapter โ€” previewing output the repo will never generate would be its own kind of lie.

Lazy adapters are labelled lazy (0 tok standing). Eager adapters show the measured token count of their emitted files, because on those targets that is what every session carries.

  • <target> Required. Path, installed name, or uninstalled source. The first argument not starting with -.
$ kitbash preview gh:singhharsh1708/kitbash/examples/skills/prereview
preview: prereview@0.1.0

โ”€โ”€โ”€ claude-code [lazy] lazy (0 tok standing) โ”€โ”€โ”€

  โ†’ .claude/skills/prereview/SKILL.md

---
name: prereview
description: "Reviews a diff against the team's actual standards"
---
<!-- generated by kitbash โ€” do not edit; source: .kitbash/skills/prereview @ 0.1.0 -->

# prereview
โ€ฆ

  โ†’ .claude/commands/prereview.md

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

Load the "prereview" skill: read .claude/skills/prereview/SKILL.md and follow it exactly.

Arguments: $ARGUMENTS

โ”€โ”€โ”€ agentsmd [eager] ~507 tok standing โ”€โ”€โ”€
โš  prereview โ†’ agentsmd: agentsmd is eager and cannot lazy-load; this skill costs ~464 tokens standing every session (declared limit: 60)

  โ†’ AGENTS.md

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

## Skill: prereview

โ€ฆ
<!-- kitbash:end prereview -->

The shared-file adapters print only the skill's own marker-delimited section, not the merged file โ€” that section is what compile would splice in.

Exit codes: 0 on a successful render, warnings included. 1 when no target was given, the target cannot be resolved or loaded, [project].targets in kitbash.toml names an unknown adapter, or the body's template references cannot be resolved.

Exit codes

Three codes, used consistently. Wire CI against these rather than parsing output.

CodeMeaning
0Success. Also returned by --version, help, and by read-only commands that found problems worth reporting but not failing on โ€” explain on a degraded target, list with nothing installed.
1Failure. Bad or missing arguments, an unresolvable target, a failed fetch, a validation or integrity failure, a policy violation, a declined prompt, a budget overrun, or --strict tripping on warnings. Unknown commands and uncaught errors also land here.
2The command exists but is not implemented in this build. Reserved for the roadmap commands listed in the summary table.

A useful CI shape: doctor to prove the lockfile still matches disk and policy still holds, test --strict to fail on any warning in your own skills, then compile --strict to fail if the generated output would carry a degradation warning nobody has acknowledged.

kitbash doctor
kitbash test --strict
kitbash compile --strict

One caveat for CI. install only prompts when stdin and stdout are both TTYs, so in a pipeline it installs without asking whether or not you passed --yes. The thing that actually stops an unwanted skill in automation is [policy] in kitbash.toml โ€” see trust & review.