Skip to content
Back to skills

Update Kit

BSecurity

Update or re-adapt the feature-flow kit in a project that was ALREADY bootstrapped, without clobbering your local adaptations — the safe path for both a newer kit version and a plain re-adapt after structural changes. Does a 3-way merge (manifest baseline vs your files vs the staged version), asks only on real conflicts, never touches project-owned files (PROJECT.md, CONTEXT.md, docs/adr/, backlog), then re-runs the bootstrap re-adapt inline so settings/hooks/rules refresh in the same pass. B...

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 5, 2026
ai-agentsgobashsqlgit

Security analysis

B85/100
  • highPerforms destructive filesystem operations

Pro shows the line behind each finding and how to fix it

Scanned September 24, 2026

npx -y skills add mik2win/foureyes --skill update-kit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Update Kit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Update Kit
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/mik2win-update-kit/badge)](https://www.skillsdirectory.com/skills/mik2win-update-kit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: update-kit
disable-model-invocation: true
description: |
  Update or re-adapt the feature-flow kit in a project that was ALREADY bootstrapped, without
  clobbering your local adaptations — the safe path for both a newer kit version and a plain
  re-adapt after structural changes. Does a 3-way merge (manifest baseline vs your files vs the
  staged version), asks only on real conflicts, never touches project-owned files (PROJECT.md,
  CONTEXT.md, docs/adr/, backlog), then re-runs the bootstrap re-adapt inline so
  settings/hooks/rules refresh in the same pass. Backs up first and asks keep-or-rollback.
  Trigger when the user says "update the kit", "pull a newer kit version", "sync the kit",
  "re-adapt the kit", "/update-kit", "/sync-kit", drops a new kit version into
  .claude/.kit-incoming/, or runs /bootstrap on a project that's already adapted (bootstrap
  hands off here). DO NOT TRIGGER for the FIRST install of the kit (use /bootstrap), or to
  remove it (/teardown).
allowed-tools: Read, Grep, Glob, Bash, Write, Edit, AskUserQuestion
effort: high
---

# /update-kit — pull a newer kit version into an already-bootstrapped project

The kit is a **copy-in bundle**, so a newer version can't auto-update itself, and re-copying it
over `.claude/` would clobber any skill you adapted. This skill is the safe path for **any
post-install adaptation** — a version bump *or* a plain re-adapt after structural changes. It
brings in the staged kit, **3-way merges** it against your local copy, resolves conflicts with
you, then re-adapts — all in **one run**. (`/bootstrap` is the front door: on an already-adapted
project it hands off here; you can also run `/update-kit` directly.) A same-version re-adapt is
just the degenerate case where everything merges quietly and only the re-adapt step does work.

Work top-to-bottom, naming each phase as you enter it — the phase headings below **are** the
checklist, and none is optional. A merge run that loses its place re-litigates a conflict the
user already resolved. New kit source: `$ARGUMENTS` (a path), or
the pre-staged `.claude/.kit-incoming/` if no path is given.

---

## The model: 3-way, not 2-way

A plain "copy and compare" is a 2-way diff (mine vs theirs) — it can't tell *"I adapted this"*
from *"the kit changed this upstream"*; both just look different. To merge safely you need a third
reference, the **BASE**: what the kit file was when it was last installed. The kit records that in
a content-hash manifest, so this works even when `.claude/` is gitignored (git is **not** used).

- **BASE** — `sha256` per file from `.claude/.kit-manifest.json` (last install/update).
- **MINE** — the file currently in `.claude/`.
- **THEIRS** — the same file in the new kit source (staged).

### Which files this merges (class A)

Only the **persistent, pristine-by-default kit files**:
`skills/**` (kit skills), `agents/**`, `hooks/**` **except `guard-bash.sh`**, `rules/_generic/**`,
`output-styles/**`, `schemas/**`, `docs/**` (kit docs — `docs/adr/` is project-owned, see below).

**Carve-out inside class A: a rule's `paths:` frontmatter is project-owned.** `/bootstrap`
narrows each installed rule's globs to the project's real layout (Phase 5.2), so THEIRS carries
the kit's `"**/*"` *template default* while MINE carries the deliberate project scope. Merge the
rule **body** normally and **keep MINE's `paths:` block** — taking THEIRS silently re-widens the
rule to every file touch and quietly inflates the standing context budget, which is the kind of
regression nobody notices until a session is paying for rules it never needed. Surface it instead:
when THEIRS changes a `paths:` block, report it as a **suggested** re-scope with both globs shown,
and let the user apply it. Same treatment for a rule that gains or loses `paths:` entirely — moving
a rule into or out of the always-on set is a context-budget decision, not a file sync.

Everything else falls out naturally and is **not** file-merged:
- **Project-owned (never touched):** `.claude/PROJECT.md`, `CONTEXT.md`, `docs/adr/`, the backlog,
  and any project-specific skill/agent the user created — these aren't in the kit source at all, so
  the sync physically can't reach them. A path under `.claude/` that is **absent from the manifest**
  is project-owned by definition — leave it.
- **Mixed (kit template + project inserts) → regenerated by re-adapt, not merged:**
  `.claude/settings.json`, `.claude/hooks/guard-bash.sh`, `CLAUDE.md`, `.gitignore`. The kit's
  templates for these (`settings.template.json`, `CLAUDE.snippet.md`) and the placeholder
  `guard-bash.sh` come in THEIRS only to feed the re-adapt phase.
- **Build-time (staged for re-adapt, then cleaned):** `_kit/**`, `*.template.*`,
  `CLAUDE.snippet.md`, `settings.stop-gate.example.json`.

---

## Phase 0 — Preconditions & staging the new version

1. Confirm this project is **already bootstrapped**: `.claude/PROJECT.md` exists with
   `profile_status: ACTIVE`. If it's missing or still `TEMPLATE`, STOP — tell the user to run
   `/bootstrap` first (this skill updates an existing install; it is not the first install).
2. Stage **THEIRS**:
   - If `$ARGUMENTS` is a path to a new kit checkout, copy it into the staging dir, excluding the
     kit's own dev dirs:
     ```bash
     mkdir -p .claude/.kit-incoming
     rsync -a --delete --exclude='.git' --exclude='.claude' --exclude='.claude copy' \
       --exclude='_backlog' --exclude='guide' "$ARGUMENTS"/ .claude/.kit-incoming/
     ```
   - Else if `.claude/.kit-incoming/` already exists (the user dropped it there manually), use it.
   - Else STOP and tell the user: drop the new kit version into `.claude/.kit-incoming/` (or pass
     its path), then re-run.
3. Sanity-check the staged source looks like the kit (`.kit-incoming/_kit/KIT.md` and
   `.kit-incoming/skills/` present). If not, stop and report.

## Phase 0.5 — Back up before any writes

Same mechanism as `/bootstrap` Phase 0.5 — reuse it so backup/rollback behave identically:

```bash
TS=$(date +%Y%m%d-%H%M%S); BK=".claude/.bootstrap-backup/$TS"; mkdir -p "$BK"
: > "$BK/created.txt"   # paths this run newly creates — for a clean rollback
```

Back up every class-A file **before** you overwrite it (mkdir its parent under `$BK`, `cp -p`).
Append each **newly created** path to `$BK/created.txt`. On re-run, add a new snapshot — never
delete earlier ones.

## Phase 1 — Load the baseline (BASE)

Read `.claude/.kit-manifest.json`. Define a hash helper:

```bash
hashof() { shasum -a 256 "$1" 2>/dev/null | cut -d' ' -f1 || sha256sum "$1" | cut -d' ' -f1; }
```

- **Manifest present** → full 3-way (the normal, quiet path). Also read its optional `excluded`
  array — kit paths this project removed on purpose (e.g. an agent replaced by a project-owned
  one). Phase 2 skips them entirely.
- **Manifest absent** (a legacy project bootstrapped before manifests existed) → **degraded 2-way
  mode**: there is no BASE, so any class-A file that differs from THEIRS becomes a conflict to ask
  about (can't auto-tell adapted from upstream-changed). **Warn the user** it'll be noisier this
  once, and that Phase 6 writes a manifest so future updates are quiet.

## Phase 2 — Classify

Enumerate the class-A paths present in `.kit-incoming/` (the list above; **exclude
`hooks/guard-bash.sh`** and every path in the manifest's `excluded`). For each, compute BASE (from manifest), MINE (`hashof` the local file if
present), THEIRS (`hashof` the staged file). Bucket by this matrix:

| BASE | MINE | THEIRS | Verdict | Action |
|---|---|---|---|---|
| present | == BASE | any | untouched locally | **take-new** (quiet) |
| present | != BASE | == BASE | you edited, kit didn't | **keep-mine** (quiet) |
| present | != BASE | == MINE | converged | **take-new** (quiet) |
| present | != BASE | != BASE, != MINE | **real conflict** | → Phase 3 |
| — (absent) | absent | present | new kit file | **add** (quiet) |
| present | absent | present | you deleted a kit file | ask: **re-add**, or **exclude** for good (move the path from `files` to `excluded`) |
| present | present | absent (gone upstream) | removed upstream | offer **delete** (confirm) |

A path in `excluded` never reaches this matrix: without the list, a removed kit file whose entry
is dropped from `files` reads as *new kit file* and is silently re-added on the next run, and one
whose entry is kept is asked about on every run.

In **degraded 2-way mode** collapse to: `MINE == THEIRS` → nothing; `MINE` absent → add;
`THEIRS` absent → offer delete; otherwise → **conflict** (Phase 3).

Produce two lists: **quiet actions** and **conflicts**.

## Phase 3 — Resolve conflicts (one batch of focused questions)

For each conflict show a short diff (MINE vs THEIRS; mention if THEIRS also moved from BASE) and
ask via `AskUserQuestion`: **take-new** / **keep-mine** / **merge** (hand-combine — you do the
edit, preserving the user's intent and the new upstream improvement).

For an adapted **skill** specifically, add the kit's guidance so the conflict stops recurring:
- *General improvement* (useful in any project) → **upstream it**: keep your version now, and open a
  change in the kit repo so the next release carries it.
- *Genuinely project-specific* → **rename it** to a project-owned skill (a name not in the manifest);
  then kit updates will never collide with it again, per the kit's design (skills stay invariant;
  project specifics live in `PROJECT.md`/rules or in project-owned skills).

Group related conflicts; don't ask twenty separate questions when a few grouped ones cover it.

## Phase 4 — Apply the file merge

Apply the resolved class-A actions: **take-new** (copy THEIRS over MINE), **merge** (write the
combined file), **add** (copy new file), **delete** (remove, confirmed). Back up each overwritten
file into `$BK` first; log each newly created path to `$BK/created.txt`. **Do not** touch class-B
(project-owned) or class-C (mixed) files here — class C is the next phase's job.

Stage the build-time inputs the re-adapt needs, logging them as created:
`cp -R .claude/.kit-incoming/_kit .claude/_kit` and copy the contracts
(`PROJECT.template.md`, `settings.template.json`, `CLAUDE.snippet.md`,
`settings.stop-gate.example.json`) into `.claude/`.

## Phase 5 — Re-adapt (inline, automatic — no separate command)

Within this same run, perform the bootstrap re-adapt so settings/hooks/rules refresh against the
new version. **Follow `skills/bootstrap/SKILL.md` Phases 3–6** against the freshly staged `.claude/_kit`
and contracts (it sees `profile_status: ACTIVE`, so it updates in place):
- re-select & **reconcile** stack rule packs, **reusing the decisions already recorded in
  `PROJECT.md` → "Rules notes"** (don't re-ask what's already answered);
- regenerate `.claude/settings.json` from the new template + the profile's permissions;
- regenerate `.claude/hooks/guard-bash.sh` (kit regions from the new version + the project's
  `__BOOTSTRAP_PROJECT_BLOCKS__` from the deny decisions; keep `__VCS_PUBLISH_BLOCKS__` per policy);
- replace the `feature-flow-kit:begin…end` block in `CLAUDE.md` and in `.gitignore`.

Do **not** re-interview the domain or re-touch `PROJECT.md` content beyond what reconcile records;
leave `CONTEXT.md`, `docs/adr/`, and the backlog alone (bootstrap already treats them as project-owned).

## Phase 6 — Write the new manifest (new BASE)

Record, for every class-A path this run kept in sync, the hash of **THEIRS — the staged kit file
as the kit shipped it**, not the local copy, and write `.claude/.kit-manifest.json`:

```json
{
  "kit_version": "<from .kit-incoming/_kit/KIT.md version or the date>",
  "installed_at": "<ISO-8601>",
  "files": { "skills/analyst/SKILL.md": "<sha256 of .kit-incoming/skills/analyst/SKILL.md>", "...": "..." },
  "excluded": ["agents/<removed-on-purpose>.md"]
}
```

BASE means *the kit version this project last merged*. For a pristine file THEIRS and the local
copy hash the same, so nothing changes; the difference is every **adapted** file — a merge, a
keep-mine, a narrowed `paths:` block. Hash the local copy there and the next run reads it as
*MINE == BASE → untouched locally → take-new*, and silently overwrites the adaptation this run just
preserved. Hashing THEIRS keeps it reading as *MINE != BASE*, so it stays keep-mine until the kit
changes it, and then it is a conflict you are asked about. Carry `excluded` forward, plus any path
the user chose to exclude in Phase 2. Check before writing: every adapted file must still differ
from its recorded hash — if one does not, you hashed the wrong copy.

## Phase 7 — Report & next step

Output:
- **File merge:** a table per class-A path — `unchanged · updated · merged · added · deleted ·
  kept-yours · excluded · skipped(project-owned)`.
- **New paths-scoped rules:** any rule that arrived new with template `paths:` (`sql.md` is the
  first) needs the narrowing `/bootstrap` does — name it and this project's DB directories.
- **Re-adapt:** what bootstrap regenerated (settings/hooks/CLAUDE.md/.gitignore) and the
  reconciliation summary.
- **To upstream:** the adapted skills you chose to keep that are worth contributing back to the kit repo.
- **Next step:** smoke-test — run `/analyst` (it should read the refreshed profile) and the
  profile's `test` command; if anything looks off, you can still roll back (Phase 8).
- **Re-check hook registration.** This run may have rewritten `settings.json` hook entries, and a
  hook that failed to register is indistinguishable from a silent one. `/hooks` → non-zero count
  per event + source label = your settings file; when `/hooks` is unavailable (TUI-only), provoke
  a hook into output and look for an `attachment` / `hook_success` record carrying `command` in
  the session transcript. Procedure: `_kit/KIT.md` § Hook & permission configuration.

## Phase 8 — Keep or roll back (ask)

Ask via `AskUserQuestion`: **Keep** or **Roll back** (identical to `/bootstrap` Phase 8).
- **Keep** → Phase 9.
- **Roll back** → undo this run from `$BK`: delete every path in `$BK/created.txt`, restore every
  backed-up file, strip any `feature-flow-kit` block this run added to a file it created. Leave
  `.kit-incoming/` so the user can retry. Report exactly what was undone.

## Phase 9 — Clean up (ask first)

After the user keeps the result, remove the build-time material this run staged (same as
`/bootstrap` Phase 9): `rm -rf .claude/_kit`, delete `.claude/*.template.*`,
`.claude/CLAUDE.snippet.md`, `.claude/settings.stop-gate.example.json`, and `.claude/.kit-incoming/`.
Offer to prune old `.claude/.bootstrap-backup/*` snapshots. Leave a clean `.claude/` plus the
updated `.kit-manifest.json`.

---

## Hard rules

- **Single command.** One `/update-kit` run does load-new + merge-old + re-adapt. Never make the
  user run `/bootstrap` separately afterward.
- **3-way, not 2-way.** Use the manifest BASE; only a genuine BASE≠MINE≠THEIRS divergence is a
  conflict worth a question. Quiet on everything else.
- **BASE is the kit's file, never yours.** The new manifest records THEIRS hashes (Phase 6) — a
  local hash there turns every adaptation into a silent overwrite on the next run.
- **Exclusions are durable.** A path in `excluded` is never added, re-added, merged, or asked about.
- **Project-owned is sacred.** Anything not in the manifest is the project's — never overwrite
  `PROJECT.md`, `CONTEXT.md`, `docs/adr/`, the backlog, or user-created skills/agents.
- **Idempotent.** Re-running with the same source yields zero conflicts and zero changes.
- **Reversible.** Back up before writing; confirm keep-or-rollback before cleanup; a rolled-back
  run leaves no trace.
- **No git dependency.** Detection is hash-based so it works when `.claude/` is gitignored.

## See also

- **`/bootstrap`** — first install + the re-adapt phases this skill calls inline.
- **`/teardown`** — remove the kit or its leftovers (also cleans `.kit-incoming/` and the manifest).
- **`/writing-skills`** — when an adapted skill should be upstreamed into the kit.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.