nextv1.4.0
Guides

Upgrading your templates

Pick up new shipped chains, library tasks and policy after a Kraft upgrade, without losing your own edits.

Kraft seeds $KRAFT_HOME/templates/ from its packaged defaults on first run and never overwrites it after. An upgrade cannot clobber your edits. It also never brings you new shipped nodes, tasks or policy keys. This guide shows how to see what changed and take what you want.

Before you start

  • You have upgraded Kraft (kraft admin update). See Upgrading.
  • Your templates live in ~/.kraft/templates/, or $KRAFT_HOME/templates/.

Keep your templates in git

Do this once, before your next upgrade. A git history lets you see your own edits apart from Kraft's, and undo a bad merge.

cd ~/.kraft/templates
git init
printf 'access.yaml\nnotify.yaml\n' > .gitignore
git add -A
git commit -m "My Kraft templates"

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

Start with the checks that know what shipped:

kraft admin doctor
  • The capabilities row lists what this Kraft can do that your templates predate, each with the edit that adopts it. Your templates record the version they were seeded from in templates/.seeded-version.
  • The chain_templates row 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 packaged defaults

The packaged defaults sit inside the installed Kraft. This finds them through the Python that runs your kraft command, 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 / "templates")')"
diff -ru "$SHIPPED" ~/.kraft/templates

The same files are in the repo's templates/ directory. Use the tag for your version, such as v1.1.0, not main.

In the diff, 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.

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.
  • 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.yaml key. Add it only if you want to change its default. A key your file leaves out takes the default. See Policy.

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.

To check the packaged set on its own, without a server:

kraft admin templates lint --dir "$SHIPPED"

Verify

kraft admin templates show default --resolved
kraft admin doctor
cd ~/.kraft/templates && git diff

show --resolved prints the chain as it will run, with each library entry expanded, so you can check the new node or task is there. git diff shows exactly what the merge changed. Commit it.

The capabilities row drops a capability once your files hold the key its edit adds: profiles: in harnesses.yaml, or the task or steering profile in library.yaml. It checks that the key exists, not that you wired it into a chain, so mr_rebase clears as soon as library.yaml defines it.

Copyright © 2026