Publish an add-on
Added in 2.1
This guide builds a collection acme with one add-on, release, and installs it from the directory to try it. To install someone else's add-on, see Share chains with add-ons.
Before you start
- A running Kraft server, and
git. - A git repository to publish the collection from, if other machines will install it.
- The instance defines every harness and
agent profile the add-on requires. The
example needs the shipped
claudeharness andstrongprofile.
Publish a collection
1. Lay out the collection
A collection is a directory with .kraft/collection.json at its root and one
directory per add-on:
acme-kraft/
.kraft/collection.json
addons/
release/
.kraft/addon.json
library.yaml
chains/ship.yaml
skills/release-notes/SKILL.md
collection.json names the collection and lists its add-ons:
{
"$schema": "https://kraft-sdlc.dev/schemas/collection.schema.json",
"name": "acme",
"description": "ACME's Kraft chains and review methods",
"owner": { "name": "ACME Platform", "email": "platform@acme.dev" },
"addons": [
{ "name": "release", "source": "./addons/release", "description": "Release chains" }
]
}
addon.json names the add-on, its version, and what it needs from an
instance. "kraft": "2" means any Kraft 2.x:
{
"$schema": "https://kraft-sdlc.dev/schemas/addon.schema.json",
"name": "release",
"version": "1.0.0",
"description": "Release chains",
"requires": { "kraft": "2", "harnesses": ["claude"], "profiles": ["strong"] }
}
2. Write the add-on's files
The files have the same shape as the ones in your own config/. Inside an
add-on, a bare extends:, steering: or skill: names the add-on's own
declaration. A bare harness: or profile: names the instance's.
An add-on holds only the files it uses. One chain, or one skill, is a valid
add-on. An add-on with none of library.yaml, chains/, skills/ and
profiles.yaml is refused.
A chain is named by its id, or by its file name when it has none, so the chain
below is release:ship once installed. A file name is a lowercase letter, then
lowercase letters, digits, _ or -. Kraft ignores chains/Ship.yaml, and
validate warns about it.
tasks:
implementer:
kind: agent
harness: claude
profile: strong
prompt: Implement the approved plan, then write the release notes.
skill: release-notes
policy:
time_cap_minutes: 90
test_changed_scopes:
kind: builtin
ref: kraft.verify_changed_test_scopes
scope: each_repository
id: ship
description: Implement, write release notes, run the tests for what changed.
nodes:
- id: implementation
kind: exec
tasks:
- id: implement
extends: implementer
- id: verify
kind: exec
tasks:
- id: test_changed_scopes
extends: test_changed_scopes
---
name: release-notes
description: Write release notes for the change in this worktree.
---
Add one line to `RELEASES.md` for each user-visible change, newest first.
An add-on cannot use a component of your local library or of another add-on: it
carries everything its chains extend. It also cannot carry a subprocess task
or a permission. What an add-on may set
has the list.
3. Validate it
kraft admin addon validate ./acme-kraft
release 1.0.0 sha256:f915f6266d319e9c0bc2bb2483683d74f307839138258901919e7d071fd37da4
validate runs the checks an install runs and installs nothing. It prints one
line per add-on, then:
- each problem as
error: <file>: <message>. Any problem makes it exit 1. - each file or manifest key Kraft ignores as
warning: <message>. A warning does not change the exit code. - when the config directory has no
library.yaml, as in CI, askipped:line that names the checks it left out.
When the config directory has a library.yaml, validate also checks the
add-on against that instance: its requires, its namespace, and your own
chains.
Add-on problems lists each message and its fix.
4. Try it from the directory
Add the directory itself as a collection, and install from it:
kraft admin addon collection add ./acme-kraft
kraft admin addon install release@acme
An add-on from a directory collection has an update whenever its files change,
whatever its version. Edit a file, then run kraft admin addon update and
accept the review. Before you add the published collection
(Install an add-on), remove this one:
kraft admin addon uninstall release@acme, then
kraft admin addon collection remove acme.
A directory collection exists on one machine only, and never auto-updates.
5. Publish it
Commit the collection and push it to a git host. To release a change later,
raise version in addon.json and push. An installed instance sees an update
only when the version changes, so a push without a new version changes nothing
for the people who installed it.
Tell people the collection's address and the add-on's id. They add and install it with the commands under Install an add-on.
A private repository works when git on the machine that runs the Kraft server can clone it without a prompt: an SSH key, or a credential helper, that a server run as a service can reach. Put no credentials in the URL; Kraft refuses one that has them.
Run validate in CI
Run validate on every push to the collection. validate needs no server and
no config directory, so CI only installs Kraft and runs it. The job fails when
validate exits 1. For GitHub Actions:
name: validate
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pipx run --spec kraft-sdlc kraft admin addon validate .
Or share from the repository itself
A collection suits chains many repositories share. For one repository's own
chains, commit them in the repository and skip the collection: a
repo library is read from the repository's
.kraft/ folder on its default branch, with no manifest and no install.
- Put
library.yaml,profiles.yaml,chains/<name>.yamlorskills/<name>/SKILL.mddirectly in.kraft/, commit them, and merge to the default branch. - A teammate runs
kraft repo connectin a terminal. Connect prints a review of the library and asks whether to take it. - Items filed in that repo run
.kraft/chains/default.yamlwith no further setup, and--chain my-repo:hotfixselects another. A later change waits until someone runskraft repo libraryand takes it. The first accept asks whether later text changes may be taken automatically; see Automatic updates.
The repository's id in repos.yaml is the namespace, and an instance checks
every change against the same rules as an add-on. Unlike an add-on, the library
is visible only to that repository's items: another repository, your own chains
and harnesses.yaml cannot name it.
Repo libraries has the facts.
Serve Claude Code from the same directory
An add-on directory can also be a Claude Code plugin. Kraft reads .kraft/,
library.yaml, profiles.yaml, chains/ and skills/, and nothing else in
the directory:
addons/release/
.kraft/addon.json
.claude-plugin/plugin.json
library.yaml
chains/ship.yaml
skills/release-notes/SKILL.md
hooks/hooks.json
.mcp.json
skills/has the layout Claude Code uses, so both tools read the sameSKILL.md. The skill isrelease:release-notesin both..claude-plugin/,hooks/and.mcp.jsonnever reach Kraft. It does not install the Claude Code half: do that in Claude Code.- An alias changes the name on Kraft's side only. An add-on installed with
--as acme-releasehasacme-release:release-notesin Kraft andrelease:release-notesin Claude Code. - A chain of yours that already says
skill: release:<name>hands that skill to the agent. Once an add-on holds the namespacerelease, Kraft reads the skill from the add-on, and the install review lists each such reference.
Next steps
- Share chains with add-ons: install an add-on from a collection, use what it ships, and keep it current.
- Add-ons and collections: every manifest field,
addons.yaml, the lock, and when an add-on loads. - Admin verbs: every
kraft admin addonverb and flag. - Add-on problems: the message
validateprinted, and its fix. - Write your own chain: the chain and library keys an add-on's files use.