Share chains with add-ons
Publish your chains as a Kraft add-on in a collection, and install it on every instance that should run them. Each instance pins the add-on to a commit and changes it only through an update you review.
This guide builds a collection acme with one add-on, release, then installs
it. To write an add-on, start at Publish a collection.
To install someone else's, check Before you start first,
then go to Install an add-on.
| To | Run |
|---|---|
| Check an add-on you wrote | kraft admin addon validate PATH |
| Add a collection | kraft admin addon collection add SOURCE |
| Install an add-on from it | kraft admin addon install ADDON@COLLECTION |
| See what is installed | kraft admin addon list |
| Take a new version | kraft admin addon update |
| Remove an add-on | kraft admin addon uninstall ADDON@COLLECTION |
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.
Repo libraries has the facts.
Install an add-on
Bound what an add-on's agents may do
An add-on's prompts, steering and skills are instructions an agent follows with whatever tools your instance allows it. An add-on cannot widen what a run may do, but with nothing set on the instance an agent may already run any command. Before you install an add-on from people you do not fully trust, set these on the instance:
maxima:inpolicy.yaml, so no add-on sets a time, attempt or budget limit above yours;allowed_tools, so the permission gate answers from your list;- a sandbox on each repo the add-on's chains run in.
Security explains why an add-on carries limits and never permissions.
1. Add the collection
Kraft ships no collection and has no directory of them, so ask whoever published the add-on for its address. Then add it:
kraft admin addon collection add acme/kraft-addons
added collection acme
owner/repo is a GitHub repository. An https URL, an ssh:// URL, a
user@host:path address or a file:/// URL works too, and --ref picks a
branch, tag or commit. This installs nothing.
kraft admin addon collection list shows what the collection publishes.
2. Install the add-on
kraft admin addon install release@acme
Kraft fetches the add-on, checks it against your instance, and prints a review. Reach lists what decides how far a run goes: gates, merge steps, harnesses, models and limits. Content lists the rest, with every prompt and skill in full. On a first install everything is new:
release@acme new, 1.0.0
Reach:
- release:ship.nodes[implementation].tasks[implement]: harness set to 'claude'
- release:ship.nodes[implementation].tasks[implement].policy: limit time_cap_minutes set, set to 90
...
Content:
- release:ship.nodes[implementation].tasks[implement]: prompt new:
Implement the approved plan, then write the release notes.
...
Apply release@acme? [y/N]
Read it, then answer y. Nothing is installed until you do. -y accepts without
asking. A running server loads the add-on at once.
Add-on problems
covers a refused or declined install.
If another add-on or a repository already uses the name release, install it
under another namespace with --as:
kraft admin addon install release@acme --as acme-release
3. Check it worked
kraft admin addon list
release@acme release 1.0.0 f4a11422cc83
The columns are the add-on id, its namespace, its version and the commit it is pinned to. Then read a chain it ships:
kraft admin templates show release:ship
The output starts with # release:ship (release@acme 1.0.0).
kraft admin doctor shows 1 add-on(s) load on its add-ons row.
Use what an add-on ships
Everything an add-on declares is named <namespace>:<name> from outside it.
- Run its chain. Name it when you file an item, or make it a repo's
default_chaininrepos.yaml:kraft item create "Cut the 4.2 release" --chain release:ship - Extend a component. A chain or library component of your own takes an
add-on's with
extends:and changes what it needs. An update to the add-on reaches your chain too, and its review shows the change:config/chains/mine.yamlid: mine nodes: - id: implementation kind: exec tasks: - id: implement extends: release:implementer policy: time_cap_minutes: 60 - Select its skill, steering profile or agent profile. Set
skill: release:release-notes,steering: [release:house-style]orprofile: release:carefulon a task in your own chain or library. The add-on must ship the one you name. The example add-on ships only the skill. An add-on declares a steering profile in thesteering:section of itslibrary.yaml, and an agent profile in itsprofiles.yaml. Names lists every qualified reference. - Copy it. An add-on's chains and components are read-only. In the web UI, open the chain under Templates › Chains, or the component under Templates › Library, and click Copy to my library. Kraft asks for a name and opens a draft that is yours to edit. A copy no longer follows the add-on's updates. The phone layout has no copy button.
Keep it current
Update by hand
kraft admin addon update --check
release@acme 1.0.0 -> 1.1.0
Reach:
- release:ship.nodes[implementation].tasks[implement].policy: limit time_cap_minutes raised, 90 -> 120
Content:
- component tasks.implementer changed
release@acme: update waiting
--check prints each review and changes nothing. It exits 0 when every add-on
is current, 1 when an update is waiting, 2 when one would be refused and 3
when a collection could not be checked, so a scheduled job can watch for one. kraft admin addon update shows the same reviews and asks about each
add-on in turn.
An item that is already running keeps the add-on version it started with.
Let an add-on update itself
kraft admin addon auto-update release@acme on
After each server start, and once a day while the server runs, Kraft checks
the add-on and applies an update whose
review has no Reach line: a new version that changes only content, such as
a prompt or a skill. It holds every other update for you and keeps the
installed version running. kraft admin doctor shows a held or failed update
on its add-on updates row.
kraft admin addon collection auto-update acme on does the same for every
add-on installed from the collection.
Auto-update lists what is held.
Put the same add-ons on another machine
Copy addons.yaml and addons.lock from config/ to the other machine's
config/, then start Kraft or run kraft admin reload. Kraft fetches each
add-on at its locked commit in the background, so both machines run the same
files. The other machine needs git access to the collection's URL.
Remove an add-on
kraft admin addon uninstall release@acme
Kraft refuses while one of your own files still names the add-on's namespace,
and lists each reference. Change those first.
kraft admin addon disable release@acme keeps the add-on installed and stops
loading it, under the same rule. Items that are running finish on the version
they started with.
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
- 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. - Security: what an add-on can and cannot change.
- Add-on problems: the message
validateor akraft admin addoncommand printed when you add a collection, install, update or remove an add-on, and its fix. - Write your own chain: the chain and library keys an add-on's files use.