Cairn init
Adopt the cairn system in a repo - scaffold the cairn/ tracking files, CLAUDE.md section, and ignore entries; or migrate an existing precursor tracking system. Use when the user wants to set up, initialize, adopt, repair, or migrate to cairn in a repository.From its SKILL.md
npx -y skills add jmgirard/cairn --skill cairn-initAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
15.8 KB, as published. Nobody here has run it
/cairn-init — scaffold, repair, or migrate
Read ${CLAUDE_PLUGIN_ROOT}/skills/shared/tracking-rules.md first.
Idempotent: safe to re-run any time; re-runs report and repair missing or
damaged pieces and never overwrite user content without asking.
Phase header: # cairn-init → ## Scaffold / ## Repair / ## Migration §n.
Chapter markers: mark a chapter at each phase transition (session start implicit).
0. Detect the situation
- Environment check (RR01 §10.2). Before anything else, probe the
external tools cairn leans on (
command -v git python3 gh;git remote) and report each gap with its degradation path — one line per gap, then proceed (only a missinggitis fatal):gitabsent → stop: cairn is git-based; there is nothing to adopt.python3absent → the plugin's hooks and thescripts/health checks (cairn_validate,cairn_next) are unavailable; skills degrade to by-hand file reads against the tracking files. On Windows (nopython3on PATH by default) the registered hooks fall back to thepylauncher (hooks.json) — best-effort, unverified on Windows (DESIGN Known issues); install Python 3 for the scripts either way.ghabsent or unauthenticated → PR creation,gh pr checks, and the mechanical merge gate are unavailable: PRs and merges happen in the GitHub UI and the approval model becomes honor-system (the merge-guard hook sees only agent Bash). Recommendgh auth login.- no git remote → local-only mode: PR flows degrade to local branch merges and push steps no-op; recommend adding a remote before the first milestone.
- Default branch. Detect the repo's default branch per the canonical
recipe in the tracking-rules git model:
git symbolic-ref --short refs/remotes/origin/HEAD(strip theorigin/prefix); if that fails but a remote exists —origin/HEADunset locally (a shallow clone, a freshgit remote add, a CI checkout that never ranset-head) — query the remote withgit ls-remote --symref origin HEADand read theref: refs/heads/<name>line. Only with no remote at all ask the user — never guess the local current branch. cairn does not assumemain; use the detected name wherever the steps below (and the tracking-rules git model) say "the default branch". - Toolchain profile. Select the repo's profile in this order:
DESCRIPTIONpresent → r-package; elsepyproject.toml(primary) /setup.py/setup.cfgpresent → python; else aDockerfileas the only toolchain marker → docker-image; otherwise → generic.DESCRIPTIONoutranks apyproject.tomlin a hybrid repo. A repo carrying both aDockerfileand a language marker (DESCRIPTION/pyproject.toml/setup.py/setup.cfg) is a hybrid image+package repo. Run a disambiguation gate (AskUserQuestion) asking which is the primary deliverable — the language package or the container image — and select the chosen profile rather than guessing (the language markers keep their order above on the language side). cairn-init instantiates the chosen reference (${CLAUDE_PLUGIN_ROOT}/skills/shared/profiles/<name>.md) intocairn/PROFILE.mdat §1; the operational skills read its slots for language-specific commands (tracking-rules "Toolchain profiles"). Confirm the recommended profile with the user before writing. - Greenfield? A new/empty repo — no source to read and no toolchain marker
(
DESCRIPTION/pyproject.toml/setup.py/setup.cfg/Dockerfile) — has no profile to infer. When this fires, present a project-type chip (AskUserQuestion: R package / Python package / Docker image / generic; recommend per any weak signal, else generic) to select the profile explicitly rather than silently defaulting to generic, then run the greenfield opener flow (§1). A repo that has a marker or existing source is not greenfield: infer the profile as above and skip the openers. - No existing tracking → fresh scaffold (§1).
- Existing tracking footprint → migration (§2). Recognize precursors by
footprint: root-level
MILESTONES.md/DESIGN.md/ROADMAP.mdwith status inside CLAUDE.md — or a forward-onlyROADMAP.mdplus an explicit status /Current focusslot (as the ackwards pilot had), which maps to cairn more cleanly than status-in-CLAUDE ("Lineage B"); an olderproject/layout withSTATUS.md/LOG.md/PRINCIPLES.mdor per-milestone files ("Lineage A" — precursors usedproject/, notcairn/); repo-local milestone skills in.claude/skills/. Unrecognized footprints get an interview, not a guess. - Already on cairn → repair mode (§3).
1. Fresh scaffold
Create (from ${CLAUDE_PLUGIN_ROOT}/skills/shared/templates/ where a
template exists):
cairn/
├── DESIGN.md # skeleton: Purpose & Scope / Function Families /
│ # Conventions / Design Principles — IP<n> = Inviolable
│ # (hard constraint) block first, then GP<n> = Guiding
│ # (tradeable with justification); numbers never
│ # reused / Architecture / Known issues
├── ROADMAP.md # empty index (below)
├── DECISIONS.md # header + append-only note (see decision.md template)
├── LESSONS.md # header + correct-in-place note; repo lessons, capped 50 lines / 20,000 bytes (D-015; byte budget D-119)
├── PROFILE.md # toolchain profile (r-package | python | docker-image | generic), instantiated
│ # from skills/shared/profiles/<name>.md; capped 120 lines
├── milestones/archive/
├── reviews/archive/
└── references/sources/ # plus empty INDEX.md
ROADMAP.md skeleton (keep under 60 lines and 24,000 bytes forever):
# Roadmap
_The only authority on milestone status. Grouped by status, not ID._
_Last hygiene check: YYYY-MM-DD (one short line, replaced each pass — never appended to)_
## Milestones
| ID | Title | Status | Depends on | Priority | File/Archive |
|---|---|---|---|---|---|
<!-- rows grouped by status, not sorted by ID; keep only the 5 most recent
terminal (done or dropped) rows — older ones live in milestones/archive/ + git -->
## Candidates
<!-- unnumbered ideas; one line each: idea — added YYYY-MM-DD — links -->
Then:
- Append
templates/claude-md-section.mdto CLAUDE.md (create CLAUDE.md if absent). If a conflicting section exists, show the diff and ask. .Rbuildignore: add^cairn$(packages only). On a migration, also prune stale per-file entries for tracking files that moved intocairn/(§2 step 6)..gitignore: addcairn/references/sources/,cairn/.merge-approved, andcairn/.merge-approved.pending(the single-use merge-approval marker written at review gates and its consumed-but-unresolved state — the plugin's merge-guard hooks manage both; never committed).- Instantiate
cairn/PROFILE.mdfrom the selected reference profile (${CLAUDE_PLUGIN_ROOT}/skills/shared/profiles/<name>.md—r-package.md,python.md,docker-image.md, orgeneric.mdper the selection order above) — copy it verbatim; the repo edits its slots (notablyverify) afterward as needed. - Greenfield openers (new/empty repos only). When §0 flagged the repo
greenfield, after instantiating
PROFILE.mdask the opener set in batched AskUserQuestion rounds (question-gate rules) — each option marked with its consequence and a recommended reversible default:- Universal layer (every profile): distribution ambition — rendered per the selected profile (r-package → CRAN vs GitHub-only; python → PyPI vs private; generic → tagged public release vs internal-only), landing in DESIGN Purpose & Scope; and numeric-work-needs-oracle-verification (universal — D-024/D-025), landing in DESIGN Conventions (a line committing numeric results to the oracle doctrine's ≥2-types bar).
- Profile layer: the selected profile's
greenfield-openersslot questions, each landing in the durable home the slot names (a PROFILE slot, DESIGN Conventions). The generic profile adds none. - Undecided on any opener ⇒ take that opener's marked reversible default
now and bank one ROADMAP
candidaterow recording the deferred choice (IP3 — nothing silently locked in). Stay tracking-only: record answers incairn/files; never scaffold a package skeleton (DESCRIPTION/pyproject.toml/R//src/) — that is the repo's obvious first milestone, surfaced in the close block below. The openers are toolchain-config only; DESIGN principle elicitation stays/design-interview(offered in the close block), never duplicated here.
- Fill DESIGN.md's Purpose & Scope from DESCRIPTION and a quick read of
R/— 5–10 honest lines, marked for the user to refine; never invent principles. In a greenfield repo there is no DESCRIPTION or source to read and the greenfield openers above already seeded Purpose & Scope (distribution ambition) and Conventions (oracle-on) — extend those honestly, never overwrite them. The deep version — eliciting the contract boundary, conventions, and IP/GP principles the code can't show — is/design-interview, offered in the close block below; this step only seeds the file. - Commit (docs-only, on the default branch):
cairn-init: scaffold tracking system; push if a remote exists (the remote's default branch is authoritative — see tracking-rules git model). - Close block (tracking-rules "Question gates and phase closes"), composed
from what the scaffold found — recap, status line, fenced next command(s)
with plain labels — e.g.
/design-interviewto turn the seeded DESIGN.md into an elicited one (the natural first move in a fresh repo), or/milestone-planwhere the package skeleton is the obvious first milestone — and the adjust-or-/clearsafety line; no chip.
2. Migration protocol
The protocol lives in
${CLAUDE_PLUGIN_ROOT}/skills/shared/migration-protocol.md — read it in
full when (and only when) §0 detects an existing tracking footprint
(progressive disclosure, M59: a greenfield scaffold or repair run never
loads it). Follow its steps 1–8 as this skill's §2; the ## Migration §n
phase headers and the chapter-marker directive apply to them unchanged.
3. Repair
Reached from §0 when the repo is already on cairn. Repair restores §1 pieces that are missing and migrates scaffold names cairn itself renamed; it never rewrites content the repo authored.
-
Missing §1 pieces. Verify every §1 piece exists and is intact; create what is missing; report each fix. A missing
cairn/PROFILE.md(a repo that adopted cairn before profiles) is backfilled by inference —DESCRIPTIONpresent → r-package, elsepyproject.toml/setup.py/setup.cfg→ python, else aDockerfile(sole marker) → docker-image, else generic — restoring the explicit declaration without changing behavior (the inference is exactly what the skills fall back to when the file is absent; with no user present, a hybridDockerfile+language-marker repo keeps the language marker — the disambiguation gate is a cairn-init-time step only). -
Scaffold deprecations. cairn is post-1.0, so a scaffold name cairn renames follows the deprecation cycle rather than breaking adopters (D-047): the old
.gitignoreentry keeps satisfyingscaffold present, and a non-failingscaffold deprecationsadvisory names its successor. The advisory names the fix; this step is the only place cairn performs it —/milestone's audit surfaces advisories and never auto-fixes them, so an un-migrated repo carries the WARN until repair runs.Run
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/cairn_validate.py"and read itsscaffold deprecationsadvisory. The advisory prints two kinds of line: an entry line —'<old>' is superseded by '<new>'— naming a superseded.gitignoreentry, and a directory line —directory '<old>' still holds files— naming a superseded shelf directory still occupied on disk, whatever.gitignoresays (M129: this arm is what lets a repair re-run resume a declined or deferred move). Act on every line the advisory prints, never on a pair named in this text — the advisory is generated from the plugin's own map of superseded entries, so a rename added after this was written migrates with no edit here. Per line, by kind:On an entry line: Add the successor entry, no ask.
<new>joins.gitignoreand<old>stays for now. It is cairn's own scaffold line, not one the repo authored, and adding touches nothing git tracks — so it needs no gate. Both entries present is silent to the entry arm, so the shelf stays ignored for as long as the old directory still holds files.On a directory line: it is only the trigger for the directory-state cases below — the case is still chosen by what is on disk. A directory line with both
.gitignoreentries already present is exactly the declined-or-deferred move this arm exists to resume; re-entering the cases below is how repair resumes it.Then take exactly one of the cases below, chosen by what is on disk before anything moves. They are mutually exclusive states, not a sequence — reading them in order would put a move ahead of the check meant to stop it.
- Both directories present: surface, never clobber. Never merge or overwrite one shelf with the other unasked. Report what each holds and let the user choose — merge old into new, keep both and skip, or stop.
- Only the old directory present: move it only after an explicit ask. The shelf is gitignored, so its contents are untracked and git cannot restore them. Show what is on the shelf (file count, and the names when few) and ask via AskUserQuestion before moving anything. A declined move is reported and the directory left alone.
- Old directory absent: the entry change is the migration. Nothing to move, nothing to ask.
Remove
<old>from.gitignoreonly once the old directory is gone from disk. A declined or deferred move keeps both entries, and both entries keep the shelf ignored; dropping the superseded entry while its directory still holds files would un-ignore untracked contents the repo may be keeping out of git deliberately.Close by re-running
cairn_validate.py. A quiet advisory now confirms the entries and the directory both — the directory arm reads the filesystem, so a superseded shelf still holding files keeps its line firing whatever.gitignoresays. A declined or deferred move therefore stays visible to the next repair run by design; the line is silenced only by the directory being moved or removed (an empty leftover is the one residue it stays silent on — nothing is lost by that). Report the directory outcome on its own: what moved, what was declined, and what still awaits a choice. -
Commit (docs-only, on the default branch): stage the files repair touched by path, never
git add -Aor.— a mid-migration shelf is untracked by design, and a blanket stage would commit the very files the entry above keeps ignored. Thencairn-init: repair scaffold; push if a remote exists. Nothing to fix → report that and skip the commit. -
Close block (tracking-rules "Question gates and phase closes"), composed from what repair found — e.g.
/milestonefenced first (re-audit a repo that just changed),/milestone-planbeside it, and the safety line.