Home/Machine-setup runbook
Runbook · owner & forkers

Copy your setup · machine → machine

One command rebuilds a full Claude Code environment on a fresh box: every marketplace, plugin, npx skill, dotfile, and behavioral guideline — from a single versioned TOML. This is the operator's guide to that round trip: capture a machine, restore it elsewhere, and contribute changes back. Commands are real; paths use $HOME and placeholders so you can run it on your own fork.

00 The round trip

State lives in the repo, not on any one machine. A snapshot on Machine A becomes the source of truth that Machine B restores from.

01 Quick start — restore a machine

On the new machine: clone, then run the installer. It is idempotent — safe to re-run any time.

# clone your fork, then bootstrap
git clone https://github.com/<you>/claude-skills ~/projects/claude-skills
bash ~/projects/claude-skills/setup/setup.sh
Preview firstRun setup.sh --dry-run to print every action with zero mutations. Add --verbose to trace each shell command.

Prerequisites the preflight step checks (and installs where it can): Python ≥ 3.11, git, and — via Homebrew if missing — bats, shellcheck, and uv. gh is only needed to contribute back.

02 What setup.sh does — 9 steps

Each step is reached in order; any one can be isolated with --only <step> or excluded with --skip-<step>.

preflight re-checks

Ensure $HOME/.local/bin is on PATH, verify Python ≥ 3.11, install bats/shellcheck/uv via brew if absent.

claude re-checks

Install the Claude Code CLI from the official installer if not present, then run claude update.

marketplaces idempotent

Read [[marketplaces]] from the TOML; for each, add if new or update if already registered (checked against claude plugin marketplace list).

plugins idempotent

Read [[plugins]]; install or update each name@marketplace. Version pins are surfaced as warnings — claude plugin install has no version flag.

skills idempotent

Run npx -y skills add <source>@<name> -g -y for each [[skills]] entry, then a global update pass.

dotfiles writes + backup

Copy templates/home-CLAUDE.md → $HOME/CLAUDE.md and templates/user-settings.json → $HOME/.claude/settings.json. Existing files are backed up to *.bak.<timestamp> first.

guidelines additive merge

Run merge_guidelines.py: extract the region between the claude-skills:guidelines markers in CLAUDE.md, and append only the ## sections not already present (heading match is case- and number-insensitive). Targets $HOME/CLAUDE.md, the current repo, and any --merge-claude-md <path>.

local_plugins idempotent

Register this repo itself as a marketplace (.claude-plugin/marketplace.json) and install/update its own four plugins.

symlinks safe

Remove stale links into the repo from the pre-plugin layout, then link $HOME/.local/bin/claude-skills-contribute and $HOME/.local/bin/wind. Never clobbers a foreign symlink or real file.

Exit codes0 clean · 1 a step failed · 2 warnings only (e.g. an unsupported version pin). The loop never aborts early — failures are tallied and reported by the summary step.

03 The manifest — claude-setup.toml

One declarative file is the source of truth. parse_toml.py reads it for setup.sh; write_toml.py rewrites it (preserving comments) for capture.sh.

[[marketplaces]]×5 — git sources to register (official + community)
[[plugins]]×19 — user-scope plugin installs, each name + marketplace
[[skills]]×7 — npx-installed skills, each source + name
[dotfiles]tablehome_claude_md, user_settings → template paths
[meta]v1schema_version — rejected if unknown, future-proofs the format
Make it yoursTo diverge from the author's stack, edit these arrays — add/remove a plugin row, swap a marketplace — and re-run setup.sh. The installer reconciles the machine to whatever the TOML says.

04 Flags

FlagEffect
--dry-runPrint every action, mutate nothing. Always your first run on a new box.
--verboseset -x — echo each shell command as it runs.
--only <step>Run a single step in isolation (e.g. --only guidelines).
--skip-<step>Exclude one step; stackable (e.g. --skip-skills --skip-symlinks).
--merge-claude-md <path>In the guidelines step, also merge the behavioral guidelines into any other CLAUDE.md.

05 Snapshot a machine — capture.sh

The reverse direction. Run it on a well-configured machine to fold its live state back into the TOML and templates, then commit.

Four sources it reads

  • 1 $HOME/CLAUDE.md + settings.jsontemplates/ & [dotfiles]
  • 2 ~/.claude/plugins/known_marketplaces.json[[marketplaces]]
  • 3 ~/.claude/plugins/installed_plugins.json[[plugins]]
  • 4 ~/.agents/.skill-lock.json[[skills]]

How it writes

  • Each source is piped as JSON into write_toml.py
  • tomlkit preserves comments & formatting — no destructive rewrite
  • Only the named section is replaced; the rest of the file is untouched
  • Result: a clean diff you can review before committing

06 Round-trip a change — contribute.sh

Available anywhere as claude-skills-contribute after setup. It branches, validates, commits, and opens a PR so a change made on one machine reaches the others.

# snapshot live state + open a PR
claude-skills-contribute --message 'capture: new plugin + guidelines'
# or scaffold a brand-new skill:
claude-skills-contribute --skill my-skill --message 'add my-skill'

Under the hood: gh auth check → switch to main and pull → new contrib/<slug> branch → capture state or scaffold a skill → run bats + pytest + shellcheck → commit → push → gh pr create. If validation fails, the branch is left for you to fix.

07 Long runs survive the move — Second Wind

setup.sh links wind onto your PATH. It orchestrates long Claude Code runs across the 5-hour usage limit so a relocated workflow keeps going.

bash tools/second-wind/install.sh
wind init # pick repos, set permissions
wind up # start a tmux session per repo
wind watch # auto-resume when the limit resets
wind dash # live dashboard on 127.0.0.1:8787

Config search order: ./second-wind.json~/.wind/config.json. State (paused sessions, reset time) lives in ~/.wind/state.json. The dashboard is localhost-only and gates writes behind a per-run token.

Read the Second Wind feature guide → · full CLI reference →

08 Troubleshooting

SymptomFix
python3 too oldNeed ≥ 3.11. On macOS: brew install python@3.11, then re-run.
symlink target is "foreign"A link at $HOME/.local/bin/* or ~/.claude/* points elsewhere. Setup refuses to clobber it — remove/rename it, re-run.
bats: command not foundRe-run setup.sh (preflight installs it) or brew install bats-core manually.
uv: command not foundRe-run setup, or curl -LsSf https://astral.sh/uv/install.sh | sh.
a plugin "version pin" warningExpected — claude plugin install can't pin versions, so latest is installed and the pin is surfaced as a warning.