Migrate
Use when the plugin has a new version and a project (groundrules, or a legacy starter-kit one) needs to catch up — per-file diff, never overwrites without explicit confirmation.From its SKILL.md
npx -y skills add lozit/groundrules --skill migrateAssembled 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
9.7 KB, as published. Nobody here has run it
/groundrules:migrate
You will update a groundrules-bootstrapped project (including projects bootstrapped by the pre-1.0 plugin, named starter-kit) to the current plugin version.
If $ARGUMENTS contains --dry-run (or dry-run), run all analysis phases but write no file: end with a report only.
Phase 0 — Plugin update check (best-effort, never blocking)
- Read
versionfrom${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json→ INSTALLED. - Run via Bash with a short timeout (~3s):
git ls-remote --tags --refs --sort=-v:refname https://github.com/lozit/groundrules.git 'v*' | head -1 - Extract the tag (
refs/tags/vX.Y.Z). If semver-greater than INSTALLED, warn (informational, don't stop):📦 groundrules vX.Y.Z is available but vINSTALLED is installed — migrating now will only bring the project up to vINSTALLED. Recommended: update the plugin first —
/plugin marketplace update claude-code-groundrules(catalog only), then reinstall (/plugin install groundrules@claude-code-groundrules) and restart Claude Code — then re-run/groundrules:migrate. (Marketplace update alone doesn't update the installed plugin.) Continuing with vINSTALLED is fine too. - Fail silent: on timeout, no network, or any error, continue without mentioning the check. This is the only network access in this skill and it is best-effort (cf. ADR 0015).
Phase 1 — Version detection
.groundrules.jsonmust exist in the cwd. Legacy: if only a pre-1.0.starter-kit.jsonexists, read that instead (version keystarterKitVersion) — the V1.0 rename below will move it. If neither exists: "This project was not bootstrapped with groundrules, nothing to migrate." Stop.- Read the state file → extract:
groundrulesVersion(legacy file:starterKitVersion) → OLDanswers→ original interview answersgeneratedFiles→ list of files generated at bootstrap
- Read
${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json→ extractversion→ NEW. - Compare:
- OLD == NEW → "Project up to date at version NEW. Nothing to migrate." Stop.
- OLD > NEW (semver) → "Project version (OLD) is newer than the installed plugin (NEW). Refusing to downgrade." Stop.
- OLD < NEW → continue.
Phase 2 — Show what changes
Read ${CLAUDE_PLUGIN_ROOT}/CHANGELOG.md. Extract and show the entries between OLD and NEW so the user understands what they'll get.
Migrations that rename/move files: some versions rename or relocate generated files — e.g. V0.7 renamed
docs/00-VISION.md→docs/VISION.mdandbrief/00-INTENT.md→brief/INTENT.md; V0.9 movedmedia/→docs/media/; V0.11 renamed thebrief/folder →intake/(ADR 0014). When migrating across such a version, detect the old path on disk and offer togit mvit to the new path (never duplicate; never delete without confirmation). Renames chain: a pre-V0.7brief/00-INTENT.mdmigrating past V0.11 lands directly atintake/INTENT.md(onegit mvto the final path). Formedia/→docs/media/: if a top-levelmedia/exists and was the starter-kit one, offer the move; if the project has its own unrelatedmedia//public/, leave it and just createdocs/media/. Forbrief/→intake/: offergit mv brief intake(whole folder), then fix the paths ingeneratedFilesand anyintent.source-style references in.groundrules.json; also flag stalebrief/references in the project's ownCLAUDE.md/README.md/docs for the user to update (or offer to update them if they carry the starter-kit signature).
V1.0 — the plugin was renamed starter-kit → groundrules (ADR 0017)
When OLD < 1.0.0, the project was generated by the plugin under its former name starter-kit. After the regular per-file arbitration (phases 3-6), apply the rename pass (each step after confirmation, git mv for tracked files):
- State file:
git mv .starter-kit.json .groundrules.json, then rename the keystarterKitVersion→groundrulesVersioninside it (all other keys unchanged). - Signatures: in every file of
generatedFilesthat carries<!-- generated-by: starter-kit vX.Y.Z -->, rewrite the signature line to<!-- generated-by: groundrules vNEW -->. One grouped confirmation for all files ("rewrite N legacy signatures?"), not one question per file. Files the user chose to keep untouched in phase 5 still get the signature rewrite offer — it's a one-line change, not a content overwrite. - Command prefix: inform the user that all slash commands changed:
/starter-kit:<skill>→/groundrules:<skill>. If the project's ownCLAUDE.md/docs mention/starter-kit:commands (grep them), offer to update those mentions. - Stale name references: flag remaining "starter-kit" mentions in the project's generated docs (
README.mdstructure section, notes "bootstrapped with starter-kit"...) and offer to update them to groundrules + the new repo URL (https://github.com/lozit/groundrules). - Plugin reinstall reminder (recap): the marketplace is now
claude-code-groundrulesand the plugingroundrules— the user should/plugin marketplace add https://github.com/lozit/groundrules+/plugin install groundrules, and may remove the oldstarter-kitplugin/marketplace entries.
Phase 3 — Analyze tracked files
For each file listed in generatedFiles:
- Identify the template that produced it. Use the same mapping as
skills/bootstrap/SKILL.mdPhase 5. Templates are English-only with a single.tplper file (no language variants). - Read
${CLAUDE_PLUGIN_ROOT}/skills/bootstrap/templates/<template>(current version). - Substitute the placeholders with
answers(same logic as bootstrap Phase 5). - Read the file on disk.
- Compare via Bash
diff -u <(printf '%s' "$current") <(printf '%s' "$generated"):- Identical → note "already up to date"
- Differs → note "to arbitrate" + keep the diff handy
Legacy projects (pre-V0.8, bilingual): if
answers.langis present (fr/mix), the project was generated by an older bilingual plugin. The current plugin is English-only — there is no FR template to diff against. Do NOT overwrite French content with English; report these files as "language change — manual review" and, if the user wants to switch to English, offer to write the new English template as<file>.newfor manual merge.
Phase 4 — Detect newly available files
For each template in the current plugin that would produce a file given answers (respecting the HAS_* flags) but that is NOT in generatedFiles:
- It's a new template introduced in a recent version → offer to create it.
Phase 5 — Recap and arbitration
Show a recap table:
| File | State | Proposed action |
|------------------------|-------------------|----------------------|
| CLAUDE.md | content modified | show diff then choose |
| docs/LEARNINGS.md | identical | skip |
| docs/ARCHITECTURE.md | identical | skip |
| (new) docs/... | template added | create? (optional) |
For each "content modified" file, ask via AskUserQuestion (group by 3-4):
See the diff— show the diff via Bashdiff -u, then re-askOverwrite with the new template— destructive, but updatesKeep my file— skipSave the new one as .new— write the new content to<file>.newfor manual merge by the user
For each "new template available": Create / Skip.
Phase 6 — Apply decisions
If
--dry-run: skip this phase, go straight to phase 8 with a "would have done:" report.
For each "Overwrite" → Write the new content.
For each "Save as .new" → Write to <file>.new.
For each "Create" → Write the new file.
For each "rename" (legacy file name) → git mv to the new name (after confirmation).
Phase 7 — Update .groundrules.json
Update:
groundrulesVersion→ NEWgeneratedFiles→ updated list (add newly created files, fix renamed paths)- If
answers.langexists and the user migrated content to English, drop the obsoletelangkey (the plugin no longer uses it). - Append (or create) a
migrationsarray entry:{"from": "OLD", "to": "NEW", "at": "YYYY-MM-DD"}
Phase 8 — Final recap
Show:
- ✅ Files updated (overwrite)
- ✅ Files created (new templates)
- 🔁 Files renamed (legacy → new name)
- 📁
.newfiles to merge manually - ⏭️ Files left as-is (user choice)
- 📋 Next steps:
- Review the
.newfiles and merge in what you like - Flesh out the project-specific sections (Setup/Build/Test commands, stack, etc.) in CLAUDE.md if they were regenerated
- Commit when ready — if
.groundrules.jsonhaspolicies.noAiAttribution = true(or a project/global CLAUDE.md forbids AI attribution), the suggested commit message must contain no AI attribution marker (Co-Authored-By, "Generated with Claude Code"…), even if a default agent guideline would add it.
- Review the
NEVER commit automatically.
Important rules
- No overwrite without an explicit user choice (phase 5).
- Refuse downgrade: if the project version is newer than the installed plugin, stop.
- If a template referenced in
generatedFilesno longer exists in the current plugin (file removed in a recent version), don't touch the file; report it in the recap. - If
--dry-run: no writes, just a report — useful to understand before committing. - The diff must be readable: prefer
diff -u(unified) over rawdiff.