Upgrade your configuration
See what a Kraft upgrade shipped that your config lacks, and take what you want
without losing your edits. Kraft seeds $KRAFT_HOME/config/ from its shipped
defaults on first run and never overwrites it after, so an upgrade cannot
clobber your edits. It also never brings you new shipped nodes, tasks or policy
keys.
Before you start
- You have upgraded Kraft (
kraft admin update). See Upgrade Kraft. - Your config directory is
~/.kraft/config/, or$KRAFT_HOME/config/if you setKRAFT_HOME. The directoryKRAFT_CONFIG_DIRnames wins over both, and so does a 1.xKRAFT_TEMPLATES_DIRwhenKRAFT_CONFIG_DIRis unset. The commands below follow the same order. gitis installed.
Keep your config in git
Do this once. If you have not yet, do it now, before you merge anything, so that
git diff shows your merge. A git history lets you see your own edits apart from
Kraft's, and undo a bad merge.
Skip this block if the config directory is already in git: with config set as in
its first line, git -C "$config" status answers instead of failing.
Each command runs only if the one before it worked, and every git command names
the directory with -C. So a config directory that does not exist stops the
block before it touches anything, and nothing runs in the folder you are in. The
block adds access.yaml and notify.yaml to a .gitignore that is already
there, and leaves its other lines alone:
config="${KRAFT_CONFIG_DIR:-${KRAFT_TEMPLATES_DIR:-${KRAFT_HOME:-$HOME/.kraft}/config}}"
git -C "$config" init &&
for f in access.yaml notify.yaml; do
grep -qxs "$f" "$config/.gitignore" || printf '\n%s\n' "$f" >> "$config/.gitignore"
done &&
git -C "$config" add -A &&
git -C "$config" commit -m "My Kraft config"
Leave access.yaml and notify.yaml out. One holds your password hash, the
other a webhook URL that usually carries a token. Keep the repository on this
machine: repos.yaml can hold secrets in a repo's env:.
See what is new
Run the checks that know what shipped:
kraft admin doctor
Two rows say whether your config has fallen behind. When it has not, they read:
ok capabilities seeded at 2.0.0; up to date
ok chain_templates 2 template(s), no missing nodes
- The
capabilitiesrow lists what this Kraft can do that your config, seeded earlier, does not use yet, each with the edit that adopts it. Your config records the version it was seeded from inconfig/.seeded-version. - The
chain_templatesrow names nodes a shipped chain has that your copy lacks. It compares node ids only, so a clean row does not prove the chains match.
With the Claude Code plugin, /kraft:check runs these checks and templates lint, and explains each row. It also flags a plugin:skill in your chains that
your agent does not have. It only reports until you ask it to change something.
Diff against the shipped defaults
The shipped defaults sit inside the installed Kraft. The next command finds them
through the Python that runs your kraft command. It works for a uv, script or
Homebrew install:
KRAFT_PY="$(head -1 "$(command -v kraft)" | cut -c3-)"
SHIPPED="$("$KRAFT_PY" -c 'import kraft.paths; print(kraft.paths.BUNDLED / "config")')"
echo "$SHIPPED"
echo prints a path that ends in kraft/_bundled/config. If it prints an empty
line, the line above it failed and the shell printed that error: the first line
of your kraft command is not an interpreter path. Use the repo's copy instead: the
config/ directory at
the tag for your version, such as v2.0.0, not main. For a tag before 2.0, the
directory is templates/.
Download it, and set SHIPPED to that folder with SHIPPED=/path/to/config. The
next two blocks stop with a message while it is unset.
Then compare it with yours:
config="${KRAFT_CONFIG_DIR:-${KRAFT_TEMPLATES_DIR:-${KRAFT_HOME:-$HOME/.kraft}/config}}"
diff -ru "${SHIPPED:?set SHIPPED first, as the block above does}" "$config"
A line starting - is in the shipped file and not in yours. That is either
something new or something you removed on purpose. Your git log tells the two
apart. Only in lines name whole files, such as a chain you added.
A task that ships and that your library.yaml lacks, such as mr_rebase, shows
like the hunk below. A real diff also prints a file header, an @@ line, the six
comment lines above mr_rebase in the shipped file (as - lines), and a few
lines of context on each side. They are left out here:
- mr_rebase:
- kind: builtin
- ref: kraft.mr_rebase
-
open_draft_mr:
kind: forge
target: mr.open_draft
Merge what you want
Copy changes into your files by hand. Do not copy the shipped files over yours:
your library holds your own model, effort and harness choices, and a copy
destroys them.
- A new library task or node. Copy its entry into your
library.yaml. For the hunk above, add the threemr_rebaselines, without the-, undertasks:. - A new node in a shipped chain. Add it to your copy of that chain in
chains/. If your chain is a copy of a shipped one with your own nodes added, as in Write your own chain, add the new node there too. - A new
policy.yamlkey. Add it only if you want to change its default. A key your file leaves out takes the default. See policy.yaml.
Then check and load the result:
kraft admin templates lint
kraft admin reload
lint checks every chain against your library and exits 1 on any error. reload
makes the running server read the files, with no restart. It exits 1 and names
any chain that does not resolve. A pass prints these lines (the counts are your
own):
3 chain(s), no errors
reloaded 3 template(s) and policy.yaml and intake.yaml
To check the shipped set on its own, without a server, with SHIPPED set as
above:
kraft admin templates lint --dir "${SHIPPED:?set SHIPPED first, as the block above does}"
Check it worked
config="${KRAFT_CONFIG_DIR:-${KRAFT_TEMPLATES_DIR:-${KRAFT_HOME:-$HOME/.kraft}/config}}"
kraft admin templates show default --resolved
kraft admin doctor
git -C "$config" diff
show --resolvedprints the chain as it will run, with each library entry expanded. Check that the new node or task is there.- In
doctor, thecapabilitiesrow no longer lists a capability you adopted. The row clears a capability once your files contain the key its edit adds: the agent profiles (profiles:) inharnesses.yaml, or thenever-signal-processes-you-didnt-startsteering profile inlibrary.yaml. It does not check that a chain uses it, somr_rebaseclears as soon aslibrary.yamldefines it. git diffshows exactly what the merge changed. Commit it.
Next steps
- Migrating a home from 0.x: the one upgrade that replaces the config directory, with a backup.
- Library and chains: every field in the files you merged.
- Write your own chain: a chain of your own, which this guide keeps up to date.
Upgrade Kraft
Back up, upgrade and check an installed Kraft, pin it to a version, and roll back to an earlier 2.x release, with pointers to the 1.4 upgrade and rollback.
Upgrade from 1.4
What 2.0 changes from 1.4 and what to do about each change, and the steps to update from 1.4 without losing the database.