/cairn-adopt-components
Backfill the Cairn component store into a project that was adopted before the component store shipped.
$ npx -y skills add isaacriehm/cairn --skill cairn-adopt-components --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/cairn-adopt-components
Context preview
The summary Claude sees to decide when to auto-load this skill.
Backfill the Cairn component store into a project that was adopted before the component store shipped.
SKILL.md
cairn-adopt-components.SKILL.mdname: cairn-adopt-components
description: Backfill the Cairn component store into a project that was adopted before the component store shipped.
when_to_use: |
Use when the operator wants the component store on a repo that already
has `.cairn/` but no `components:` config (adopted before v0.18.0), or
asks to "adopt components", "backfill the component registry", or "add
`@cairn` headers". Drives detect → annotate → emit inline. Skip when
the repo isn't adopted at all (send to cairn-adopt first) or already
carries a built component store.
allowed-tools: Skill(cairn:cairn-attention), Task(component-annotator), Task(component-registrar), AskUserQuestion
Skill: cairn-adopt-components
Host portability
This skill is shared by Claude Code, Cursor, and Codex. Use the host's structured question UI when available; otherwise ask the same concise A/B/C question in chat and pause. Named subagent briefs live under `../../agents/`: dispatch by name where supported, or read the brief and pass it to the host's native subagent tool. Execute inline only when no subagent tool exists.
You are backfilling Cairn's **component store** into an already-adopted project — the one-time work the adoption pipeline does for fresh repos, applied to a repo that predates the store. The goal: every component file carries a `@cairn` registry header, the derived index is built, and `@singleton` headers become §INVs. Spec: `docs/PLUGIN_ARCHITECTURE.md` §6 (the component trio 9d→9e→9f) and `docs/COMPONENT_STORE_PLAN.md`.
This skill drives the bundled `dist/cli.mjs` internally using the plugin-root variable supplied by the active host (`PLUGIN_ROOT`, `CURSOR_PLUGIN_ROOT`, or `CLAUDE_PLUGIN_ROOT`). **Never surface a CLI subcommand to the operator** (Plugin spec §11) — the chat shows progress + consent gates, not commands.
Step 0 — classify the repo
Run this single probe to decide whether backfill applies. It is **ghost-aware**: a ghost-adopted repo has no in-repo `.cairn/` — its state lives out-of-repo at `~/.cairn/state/<root-commit>/`. The probe resolves the effective state home (in-repo when present, else the out-of-repo ghost dir keyed on the repo's root-commit) and prints `<mode> <verdict> <home>`:
node -e '
const fs=require("node:fs");
const os=require("node:os");
const path=require("node:path");
const cp=require("node:child_process");
const root=process.cwd();
let mode="committed";
let home=path.join(root,".cairn");
if(!fs.existsSync(home)){
// No in-repo .cairn — may be ghost-adopted. Ghost state lives at
// ~/.cairn/state/<repo-id>; repo-id is the move-stable root-commit SHA
// (matches registerGhostRepo). Resolve it and probe there instead.
let rc="";
try{rc=cp.execFileSync("git",["-C",root,"rev-list","--max-parents=0","HEAD"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim().split(/\s+/)[0]||"";}catch{}
if(rc){const g=path.join(os.homedir(),".cairn","state",rc);if(fs.existsSync(g)){home=g;mode="ghost";}}
}
const cfg=path.join(home,"config.yaml");
const idx=path.join(home,"ground","components");
if(!fs.existsSync(home)||!fs.existsSync(cfg)){console.log(mode+" not-adopted "+home);process.exit(0);}
let hasBlock=false;
try{hasBlock=/^components:/m.test(fs.readFileSync(cfg,"utf8"));}catch{}
const hasIndex=fs.existsSync(idx)&&fs.readdirSync(idx).length>0;
console.log(mode+" "+(hasBlock&&hasIndex?"has-store":"backfill")+" "+home);'The first token is the **mode** (`committed` | `ghost`); the third is the resolved **state home**. In **ghost mode**, every `.cairn/…` path in the steps below resolves under that home (the out-of-repo dir), NOT the repo root — the `node … cli.mjs components …` commands already resolve it automatically, so only the raw `cat` / in-place-edit snippets need `$CAIRN_HOME` substituted for `.cairn`. Export it: `CAIRN_HOME="<home>"`.
Branch on the verdict (second token):
- **`not-adopted`** → the repo has no Cairn state. Surface one line:
"This project isn't adopted yet — run `/cairn:cairn-adopt` first; it builds the component store as part of adoption." End the turn.
- **`has-store`** → a `components:` block and a built index already
exist. This is a **refresh**, not a first backfill — skip Step 1's detect (the config is already there), run **Step 2.5 (config-gap check)** first, then go to Step 3 (walk for newly-added un-headered files). Surface: "Component store already present — re-checking for un-headered components."
- **`backfill`** → the normal path. Continue to Step 1.
Step 1 — detect + write the `components:` config
Run detection (LLM-driven + convention-agnostic — the same one adoption uses; it reasons over the repo's structure rather than probing a fixed list of conventional dir names, so any layout / monorepo tooling works):
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.mjs" components detectRead the stdout and branch:
- **"No recognizable component layout found"** → the model found no
reusable UI components (e.g. a backend-only repo). Surface one line and end — there is nothing to backfill.
- **"already carries a components: block"** → fall through to Step 2.
- **"Wrote a components: block"** → the config now has a `components:`
block. If the output also says **"Monorepo detected"**, run Step 1.5. Otherwise continue to Step 2.
Step 1.5 — monorepo sharing (only when monorepo detected)
Every workspace is **isolated by default** — components in one workspace are OFF-LIMITS to the others. A shared UI library workspace (e.g. a `packages/ui` design system) should usually be `shared: true` so the whole repo may use it. Detection never guesses this (isolation invariant 3).
Read the workspace names from `.cairn/config.yaml` (`components.workspaces`). Render an `AskUserQuestion` (multi-select) listing the workspaces:
> Which workspaces expose their components repo-wide (a shared UI/design > library)? Leave all unchecked to keep every workspace iso
Read more
name: cairn-adopt-components description: Backfill the Cairn component store into a project that was adopted before the component store shipped. when_to_use: | Use when the operator wants the component store on a repo that already has `.cairn/` but no `components:` config (adopted before v0.18.0), or asks to "adopt components", "backfill the component registry", or "add `@cairn` headers". Drives detect → annotate → emit inline. Skip when the repo isn't adopted at all (send to cairn-adopt first) or already carries a built component store. allowed-tools: Skill(cairn:cairn-attention), Task(component-annotator), Task(component-registrar), AskUserQuestion
Skill: cairn-adopt-components
Host portability
This skill is shared by Claude Code, Cursor, and Codex. Use the host's structured question UI when available; otherwise ask the same concise A/B/C question in chat and pause. Named subagent briefs live under `../../agents/`: dispatch by name where supported, or read the brief and pass it to the host's native subagent tool. Execute inline only when no subagent tool exists.
You are backfilling Cairn's **component store** into an already-adopted project — the one-time work the adoption pipeline does for fresh repos, applied to a repo that predates the store. The goal: every component file carries a `@cairn` registry header, the derived index is built, and `@singleton` headers become §INVs. Spec: `docs/PLUGIN_ARCHITECTURE.md` §6 (the component trio 9d→9e→9f) and `docs/COMPONENT_STORE_PLAN.md`.
This skill drives the bundled `dist/cli.mjs` internally using the plugin-root variable supplied by the active host (`PLUGIN_ROOT`, `CURSOR_PLUGIN_ROOT`, or `CLAUDE_PLUGIN_ROOT`). **Never surface a CLI subcommand to the operator** (Plugin spec §11) — the chat shows progress + consent gates, not commands.
Step 0 — classify the repo
Run this single probe to decide whether backfill applies. It is **ghost-aware**: a ghost-adopted repo has no in-repo `.cairn/` — its state lives out-of-repo at `~/.cairn/state/<root-commit>/`. The probe resolves the effective state home (in-repo when present, else the out-of-repo ghost dir keyed on the repo's root-commit) and prints `<mode> <verdict> <home>`:
node -e '
const fs=require("node:fs");
const os=require("node:os");
const path=require("node:path");
const cp=require("node:child_process");
const root=process.cwd();
let mode="committed";
let home=path.join(root,".cairn");
if(!fs.existsSync(home)){
// No in-repo .cairn — may be ghost-adopted. Ghost state lives at
// ~/.cairn/state/<repo-id>; repo-id is the move-stable root-commit SHA
// (matches registerGhostRepo). Resolve it and probe there instead.
let rc="";
try{rc=cp.execFileSync("git",["-C",root,"rev-list","--max-parents=0","HEAD"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim().split(/\s+/)[0]||"";}catch{}
if(rc){const g=path.join(os.homedir(),".cairn","state",rc);if(fs.existsSync(g)){home=g;mode="ghost";}}
}
const cfg=path.join(home,"config.yaml");
const idx=path.join(home,"ground","components");
if(!fs.existsSync(home)||!fs.existsSync(cfg)){console.log(mode+" not-adopted "+home);process.exit(0);}
let hasBlock=false;
try{hasBlock=/^components:/m.test(fs.readFileSync(cfg,"utf8"));}catch{}
const hasIndex=fs.existsSync(idx)&&fs.readdirSync(idx).length>0;
console.log(mode+" "+(hasBlock&&hasIndex?"has-store":"backfill")+" "+home);'The first token is the **mode** (`committed` | `ghost`); the third is the resolved **state home**. In **ghost mode**, every `.cairn/…` path in the steps below resolves under that home (the out-of-repo dir), NOT the repo root — the `node … cli.mjs components …` commands already resolve it automatically, so only the raw `cat` / in-place-edit snippets need `$CAIRN_HOME` substituted for `.cairn`. Export it: `CAIRN_HOME="<home>"`.
Branch on the verdict (second token):
- **`not-adopted`** → the repo has no Cairn state. Surface one line:
"This project isn't adopted yet — run `/cairn:cairn-adopt` first; it builds the component store as part of adoption." End the turn.
- **`has-store`** → a `components:` block and a built index already
exist. This is a **refresh**, not a first backfill — skip Step 1's detect (the config is already there), run **Step 2.5 (config-gap check)** first, then go to Step 3 (walk for newly-added un-headered files). Surface: "Component store already present — re-checking for un-headered components."
- **`backfill`** → the normal path. Continue to Step 1.
Step 1 — detect + write the `components:` config
Run detection (LLM-driven + convention-agnostic — the same one adoption uses; it reasons over the repo's structure rather than probing a fixed list of conventional dir names, so any layout / monorepo tooling works):
node "${CLAUDE_PLUGIN_ROOT}/dist/cli.mjs" components detectRead the stdout and branch:
- **"No recognizable component layout found"** → the model found no
reusable UI components (e.g. a backend-only repo). Surface one line and end — there is nothing to backfill.
- **"already carries a components: block"** → fall through to Step 2.
- **"Wrote a components: block"** → the config now has a `components:`
block. If the output also says **"Monorepo detected"**, run Step 1.5. Otherwise continue to Step 2.
Step 1.5 — monorepo sharing (only when monorepo detected)
Every workspace is **isolated by default** — components in one workspace are OFF-LIMITS to the others. A shared UI library workspace (e.g. a `packages/ui` design system) should usually be `shared: true` so the whole repo may use it. Detection never guesses this (isolation invariant 3).
Read the workspace names from `.cairn/config.yaml` (`components.workspaces`). Render an `AskUserQuestion` (multi-select) listing the workspaces:
> Which workspaces expose their components repo-wide (a shared UI/design > library)? Leave all unchecked to keep every workspace iso
Showing the first part of this file.
Persistent ground truth for AI coding agents. First-class support for Claude Code, Cursor, and Codex. Stop agents from drifting.
Repo: isaacriehm/cairn
Other skills on cairn.
- /cairn-adopt
One-time Cairn adoption pipeline for a new project.
Open skill - /cairn-attention
Resolve Cairn's pending-attention queue inline (DEC drafts, baseline findings, drift events).
Open skill - /cairn-direction
Spec-tightener + subagent dispatcher. Engage on code-change asks — verbs, bug reports, observations. Pivot-aware on active tasks.
Open skill - /cairn-resync
Operator-initiated re-discovery — resolve config drift, re-cluster topics, re-curate grown areas into DEC/INV drafts.
Open skill

