From 93dccbcbd4410fd33e391456a7ae064cf2ce2cb8 Mon Sep 17 00:00:00 2001 From: Marlin Forbes Date: Fri, 1 May 2026 09:19:37 +0200 Subject: [PATCH 01/13] Add /bootstrap-harness skill Packages the harness-engineering setup as a reusable, idempotent skill: operating-contract CLAUDE.md template, 4 guardrail hooks (block-force-push, format-on-edit, post-compact-reinject, verify-before-stop), /verify + /plan slash commands, auto-memory seeds (MEMORY.md index + concise / plan-first / verification-gate feedback + user_role template), and helper scripts for snapshotting ~/.claude/ to a private git repo plus a prompt template for a monthly remote-audit routine. The installer (scripts/install.sh) supports --dry-run / --force / --skip-memory / --skip-settings and never clobbers existing files unless asked. settings.json is patched additively (env + 4 hook entries only), preserving permissions/marketplaces/statusLine/etc. Smoke-tested in an isolated $HOME: dry-run writes nothing, real run installs 12 files + patches settings, second run is fully idempotent (skip-exists everywhere, "settings.json already current"), and the installed block-force-push.sh correctly blocks 'git push --force origin main' while allowing --force-with-lease and echoed strings. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 11 ++ skills/bootstrap-harness/README.md | 61 +++++++ skills/bootstrap-harness/SKILL.md | 146 +++++++++++++++++ .../bootstrap-harness/assets/CLAUDE.md.tmpl | 37 +++++ .../bootstrap-harness/assets/commands/plan.md | 34 ++++ .../assets/commands/verify.md | 22 +++ .../assets/hooks/block-force-push.sh | 97 +++++++++++ .../assets/hooks/format-on-edit.sh | 39 +++++ .../assets/hooks/post-compact-reinject.sh | 19 +++ .../assets/hooks/verify-before-stop.sh | 27 +++ .../assets/memory/MEMORY.md.tmpl | 8 + .../assets/memory/feedback_concise.md.tmpl | 11 ++ .../assets/memory/feedback_plan_first.md.tmpl | 11 ++ .../memory/feedback_verification.md.tmpl | 11 ++ .../assets/memory/user_role.md.tmpl | 17 ++ .../bootstrap-harness/scripts/audit-prompt.md | 72 ++++++++ skills/bootstrap-harness/scripts/install.sh | 154 ++++++++++++++++++ skills/bootstrap-harness/scripts/snapshot.sh | 91 +++++++++++ 18 files changed, 868 insertions(+) create mode 100644 skills/bootstrap-harness/README.md create mode 100644 skills/bootstrap-harness/SKILL.md create mode 100644 skills/bootstrap-harness/assets/CLAUDE.md.tmpl create mode 100644 skills/bootstrap-harness/assets/commands/plan.md create mode 100644 skills/bootstrap-harness/assets/commands/verify.md create mode 100644 skills/bootstrap-harness/assets/hooks/block-force-push.sh create mode 100644 skills/bootstrap-harness/assets/hooks/format-on-edit.sh create mode 100644 skills/bootstrap-harness/assets/hooks/post-compact-reinject.sh create mode 100644 skills/bootstrap-harness/assets/hooks/verify-before-stop.sh create mode 100644 skills/bootstrap-harness/assets/memory/MEMORY.md.tmpl create mode 100644 skills/bootstrap-harness/assets/memory/feedback_concise.md.tmpl create mode 100644 skills/bootstrap-harness/assets/memory/feedback_plan_first.md.tmpl create mode 100644 skills/bootstrap-harness/assets/memory/feedback_verification.md.tmpl create mode 100644 skills/bootstrap-harness/assets/memory/user_role.md.tmpl create mode 100644 skills/bootstrap-harness/scripts/audit-prompt.md create mode 100755 skills/bootstrap-harness/scripts/install.sh create mode 100755 skills/bootstrap-harness/scripts/snapshot.sh diff --git a/README.md b/README.md index 6bd9f15..811ebad 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,17 @@ Trello board health audit. Queries five dimensions of card data to identify thro /audit-trello board="My Board" since=2024-07-01 ``` +### `/bootstrap-harness` + +Bootstraps a "harness-engineering" Claude Code setup at user scope (`~/.claude/`): dense operating-contract CLAUDE.md, four deterministic guardrail hooks (block force-push, format on edit, re-inject CLAUDE.md after compact, refuse to stop with broken build), `/verify` and `/plan` slash commands, an auto-memory seed (concise / plan-first / verification-gate feedback memories + a user_role template), and an optional monthly remote-audit pipeline (snapshot the setup to a private git repo, schedule a routine that PRs deltas against the latest Anthropic releases and Claude Code community patterns). Idempotent installer with `--dry-run` / `--force` / `--skip-memory` / `--skip-settings`; never clobbers existing files unless asked. + +**Arguments:** None. + +**Usage:** +``` +/bootstrap-harness +``` + ## Other skills I like ### [`agent-ready-codebase`](https://skills.sh/casper-studios/casper-marketplace/agent-ready-codebase) diff --git a/skills/bootstrap-harness/README.md b/skills/bootstrap-harness/README.md new file mode 100644 index 0000000..3c2520c --- /dev/null +++ b/skills/bootstrap-harness/README.md @@ -0,0 +1,61 @@ +# bootstrap-harness + +Bootstraps a "harness-engineering" Claude Code setup at user scope (`~/.claude/`): operating contract, deterministic guardrail hooks, verify/plan slash commands, auto-memory seed, and an optional monthly drift-detection audit. Idempotent. + +## When to use + +Good prompts: *bootstrap my Claude Code*, *install harness*, *wire hooks*, *set up auto-memory*, *harden my setup*, *make my setup agent-ready*. + +## What it installs + +| Surface | What lands | +| ------------------------ | --------------------------------------------------------------------------------------------------- | +| `~/.claude/CLAUDE.md` | Operating contract template (default stance, editing rules, expected tools) | +| `~/.claude/hooks/` | `block-force-push.sh`, `format-on-edit.sh`, `post-compact-reinject.sh`, `verify-before-stop.sh` | +| `~/.claude/commands/` | `/verify` (run project's pass/fail check), `/plan` (Goal/Constraints/Acceptance template) | +| `~/.claude/projects//memory/` | `MEMORY.md` index + `user_role`, `feedback_concise`, `feedback_plan_first`, `feedback_verification` | +| `~/.claude/settings.json`| Adds `CLAUDE_CODE_AUTO_COMPACT_WINDOW=400000` + 4 hook entries (only if missing — no clobber) | + +## How it works + +Three stages: + +1. **Bootstrap** (`scripts/install.sh`) — copies templates from `assets/` into `~/.claude/`, never overwrites without `--force`. Patches `settings.json` to wire hooks. `--dry-run` shows what would happen. +2. **Snapshot** (`scripts/snapshot.sh`, optional) — mirrors `~/.claude/` into a private git repo, scrubs caches and secret patterns, commits + pushes only on diff. Run after material config changes. +3. **Audit** (optional) — schedule a monthly remote routine using the prompt at `scripts/audit-prompt.md`. The agent clones your snapshot repo, researches the last ~30 days of Anthropic releases and canonical Claude Code voices (Cherny / Willison / Vincent / Huntley / Husain / Yegge), and PRs `audits/YYYY-MM-DD-setup-audit.md` with prioritised deltas. + +The hooks: +- **block-force-push.sh** (PreToolUse:Bash) — segment-aware matcher. Blocks force-push to main/master, hard reset to remote, `rm -rf ~`, `--no-verify`, world-writable chmod, branch -D. Allows `--force-with-lease`. Doesn't false-trigger on echoed strings. +- **format-on-edit.sh** (PostToolUse:Write|Edit) — runs Pint / `bun run format` / `npm run format` / ruff / gofmt / cargo fmt if config exists. Silent on success. +- **post-compact-reinject.sh** (PostCompact) — re-cats `./CLAUDE.md`, `./AGENTS.md`, `~/.claude/CLAUDE.md` after autocompact, so the operating contract survives. +- **verify-before-stop.sh** (Stop) — blocks Stop if `./scripts/harness-check.sh` fails. `CLAUDE_SKIP_VERIFY=1` to override mid-investigation. + +## Arguments + +None. The skill detects what's already in `~/.claude/`, reports gaps, runs the installer, and walks the user through the two hand-edit steps (CLAUDE.md stack signals, memory user_role). + +## Files in this folder + +| File | Role | +| --------------------------------- | --------------------------------------------------------------------- | +| `SKILL.md` | Full agent instructions | +| `README.md` | This overview | +| `assets/CLAUDE.md.tmpl` | Operating-contract template | +| `assets/hooks/*.sh` | Four hook scripts | +| `assets/commands/*.md` | `/verify`, `/plan` | +| `assets/memory/*.tmpl` | MEMORY.md index + 3 feedback memories + user_role template | +| `scripts/install.sh` | Idempotent installer (`--dry-run` / `--force` / `--skip-memory` / `--skip-settings`) | +| `scripts/snapshot.sh` | Sanitised mirror of `~/.claude/` → target git repo | +| `scripts/audit-prompt.md` | Prompt template for the monthly remote-audit routine | + +## Requirements + +- macOS or Linux with `python3` and `grep -E` on PATH +- For the snapshot phase: `git`, plus `gh` if you want the installer to help create the private repo +- For the audit routine: a Claude Code account with `/schedule` available + +## Inspiration + +Convergent patterns from Boris Cherny (howborisusesclaudecode.com), Simon Willison (Auto Mode safety analysis), Jesse Vincent (Superpowers framework), Geoffrey Huntley (Ralph loop), Hamel Husain (eval skills), and Steve Yegge (Gas Town orchestration). The harness vocabulary (feedforward / sensors / GC) follows OpenAI's harness-engineering article and Martin Fowler's writeup. + +For the full step-by-step workflow, open **`SKILL.md`**. diff --git a/skills/bootstrap-harness/SKILL.md b/skills/bootstrap-harness/SKILL.md new file mode 100644 index 0000000..de04de3 --- /dev/null +++ b/skills/bootstrap-harness/SKILL.md @@ -0,0 +1,146 @@ +--- +name: bootstrap-harness +description: > + Bootstraps a "harness-engineering" Claude Code setup at user scope (~/.claude/): + a dense operating-contract CLAUDE.md, deterministic guardrail hooks (block force-push, + format on edit, re-inject CLAUDE.md after compact, refuse to stop with broken build), + /verify and /plan slash commands, an auto-memory seed (concise / plan-first / + verification-gate feedback memories + a user_role template), and an optional + monthly remote-audit pipeline (snapshot the setup to a git repo, schedule a routine + that PRs deltas against the latest Anthropic releases and Claude Code community + patterns). Idempotent. Use when asked to "set up my Claude Code", "bootstrap a + harness", "install hooks", "wire verification", or "harden my setup". +user-invocable: true +--- + +# Bootstrap harness + +Sets up the user-scope Claude Code "harness" — feedforward guides (CLAUDE.md, memory), feedback sensors (hooks), and an optional drift-detection loop (snapshot + monthly audit routine). Idempotent: never clobbers files unless `--force`. + +## When to use + +Good prompts: *bootstrap my Claude Code setup*, *install harness*, *wire up hooks and verification*, *set up auto-memory*, *make my setup agent-ready*. + +## What it installs + +| Surface | What lands | +| ------------------------ | --------------------------------------------------------------------------------------------------- | +| `~/.claude/CLAUDE.md` | Operating contract (default stance, editing rules, expected tools, stack-signals placeholder) | +| `~/.claude/hooks/` | `block-force-push.sh`, `format-on-edit.sh`, `post-compact-reinject.sh`, `verify-before-stop.sh` | +| `~/.claude/commands/` | `/verify` (run pass/fail check), `/plan` (Goal/Constraints/Acceptance template) | +| `~/.claude/projects//memory/` | `MEMORY.md` index + `user_role`, `feedback_concise`, `feedback_plan_first`, `feedback_verification` | +| `~/.claude/settings.json`| Adds `CLAUDE_CODE_AUTO_COMPACT_WINDOW=400000` env + 4 hook entries (only if missing — no clobber) | + +Optional second phase: +- Mirror `~/.claude/` into a private git repo via `scripts/snapshot.sh`. +- Schedule a monthly remote-audit routine that PRs deltas against the latest releases (prompt template at `scripts/audit-prompt.md`). + +## Step 1: Inventory what's already there + +Before installing anything, look at the current state and report what you'll do: + +```bash +ls -la ~/.claude/CLAUDE.md ~/.claude/settings.json 2>/dev/null +ls ~/.claude/hooks/ ~/.claude/commands/ ~/.claude/agents/ 2>/dev/null +ls ~/.claude/projects/$(printf '%s' "$HOME" | tr '/' '-')/memory/ 2>/dev/null +``` + +For each surface, classify as: +- **absent** → install template +- **present, untouched template** → safe to overwrite (rare — usually only on a re-run) +- **present, customised** → skip; tell user to pass `--force` if they want to overwrite + +## Step 2: Run the installer + +```bash +bash "$SKILL_DIR/scripts/install.sh" +``` + +(Substitute the skill's absolute base directory for `$SKILL_DIR` — it's announced at the top of this invocation.) + +Useful flags: +- `--dry-run` — show what would happen, change nothing. +- `--force` — overwrite existing CLAUDE.md / hooks / commands / memory templates. +- `--skip-memory` — leave memory alone (recommended if it's already populated). +- `--skip-settings` — leave `settings.json` alone (recommended if user has a complex existing config). + +The installer: +1. Creates `~/.claude/{hooks,commands,agents,projects//memory}` if missing. +2. Copies `assets/CLAUDE.md.tmpl` → `~/.claude/CLAUDE.md` (skip if exists). +3. Copies each `assets/hooks/*.sh` → `~/.claude/hooks/` and `chmod +x`. +4. Copies each `assets/commands/*.md` → `~/.claude/commands/`. +5. Copies each `assets/memory/*.tmpl` → `~/.claude/projects//memory/` (strips `.tmpl`). +6. Patches `~/.claude/settings.json` via Python: adds `env.CLAUDE_CODE_AUTO_COMPACT_WINDOW` and 4 hook entries, only if not already present. Never overwrites unrelated settings. + +## Step 3: Hand-edit two files + +The installer prints these as next-steps. Walk the user through them or do it yourself if context allows: + +1. **`~/.claude/CLAUDE.md`** — fill in the `## Stack signals` section with their actual default stack (look at `~/.claude/projects/` slugs and `installed_plugins.json` for hints; ask if unclear). +2. **`~/.claude/projects//memory/user_role.md`** — replace the placeholder with their actual role/projects/stack. + +Do NOT auto-fill these from guesswork. Ask. + +## Step 4 (optional): Wire the snapshot + audit loop + +If the user wants version-controlled config + a monthly audit: + +1. **Create the snapshot repo.** Suggest a name like `/claude-setup`. + ```bash + mkdir -p ~/Projects//claude-setup + cd ~/Projects//claude-setup + git init -b main + gh repo create /claude-setup --private --source=. --remote=origin + ``` + +2. **First snapshot.** + ```bash + SNAPSHOT_REPO=~/Projects//claude-setup bash "$SKILL_DIR/scripts/snapshot.sh" + ``` + This sanitises and pushes. The script is idempotent — second run is a no-op if nothing changed. + +3. **Schedule the audit routine.** Use `RemoteTrigger` (the user must invoke `/schedule` themselves; this skill can prepare the prompt). The prompt template is at `$SKILL_DIR/scripts/audit-prompt.md`. Recommended config: + - cron: `0 6 1 * *` (1st of month, 06:00 UTC) + - model: `claude-opus-4-7` (audit quality matters) + - tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, Agent + - sources: the snapshot repo URL + +4. **Tell the user to re-run `snapshot.sh`** whenever they materially change `~/.claude/`. Or schedule it locally via `/loop` or a launchd plist. + +## Step 5: Verify the install + +```bash +# Hook smoke-test — should print BLOCKED. +echo '{"tool_name":"Bash","tool_input":{"command":"git push --force origin main"}}' \ + | ~/.claude/hooks/block-force-push.sh; echo "exit=$?" + +# Memory index loaded. +test -f ~/.claude/projects/$(printf '%s' "$HOME" | tr '/' '-')/memory/MEMORY.md && echo "memory OK" + +# Settings JSON valid. +python3 -m json.tool ~/.claude/settings.json > /dev/null && echo "settings OK" +``` + +Tell the user to **restart Claude Code (or open a new session)** — hooks load on session start, not mid-session. + +## Constraints + +- **Never auto-fill stack signals or user_role.** Templates have placeholders; ask the user to fill them. +- **Never modify `settings.json` outside `env` and `hooks`.** Specifically: don't touch their permissions, marketplaces, statusLine, advisorModel, theme — those are personal. +- **Memory is sensitive.** If `MEMORY.md` already exists with their entries, leave it alone unless they say otherwise. +- **Don't push to a public GitHub repo by default.** The snapshot contains personal config — `--private` is mandatory. +- The hooks assume `python3` and `grep -E` are on PATH. They are on macOS and most Linux. If not, the hook scripts will silently no-op (graceful by design). + +## Files in this skill + +| File | Role | +| --------------------------------- | --------------------------------------------------------------------- | +| `SKILL.md` | This file — agent instructions | +| `README.md` | Human-facing overview | +| `assets/CLAUDE.md.tmpl` | Operating-contract template | +| `assets/hooks/*.sh` | Four hook scripts | +| `assets/commands/*.md` | `/verify`, `/plan` slash commands | +| `assets/memory/*.tmpl` | MEMORY.md index + 3 feedback memories + user_role template | +| `scripts/install.sh` | Idempotent installer with `--dry-run` / `--force` / `--skip-*` flags | +| `scripts/snapshot.sh` | Sanitised mirror of `~/.claude/` → target git repo | +| `scripts/audit-prompt.md` | Prompt template for the monthly remote-audit routine | diff --git a/skills/bootstrap-harness/assets/CLAUDE.md.tmpl b/skills/bootstrap-harness/assets/CLAUDE.md.tmpl new file mode 100644 index 0000000..0047177 --- /dev/null +++ b/skills/bootstrap-harness/assets/CLAUDE.md.tmpl @@ -0,0 +1,37 @@ +# Operating contract + + + +## Default stance +- Treat me as an engineer reviewing your output, not a pair programmer. Take Goal/Constraints/Acceptance and run; don't narrate every line. +- For any non-trivial task: enter plan mode (shift-tab twice) before writing code. One task per conversation — `/clear` between tasks. +- Don't claim done until verification passes. If `./scripts/harness-check.sh` exists, run it. Otherwise run the project's lint + types + tests. +- Concise replies. No trailing summaries — I read diffs. + +## Editing +- `Edit`/`Write` for files. No `sed`/`awk` from Bash. +- ripgrep for search; `Explore` agent only for broad sweeps across many files. +- Don't add comments unless the WHY is non-obvious. Don't add docs unless asked. +- Don't add error handling for impossible cases. Don't introduce abstractions for hypothetical futures. + +## Tools I expect you to use +- `advisor()` before committing to an approach on tasks longer than a few steps, and before declaring done. +- Sub-agents (`Explore`, `general-purpose`) for parallel research — don't duplicate their searches in the main thread. +- `/verify` to run the project's pass/fail check. +- `/plan` to draft Goal/Constraints/Acceptance for a non-trivial task. + +## Stack signals + + + +## When the harness disagrees with you +The harness is authoritative. If a sensor (lint/types/tests) is wrong, fix the rule in the same change with a one-line rationale. Silently disabling a check is worse than the original bug. + +## Memory +The auto-memory system at `~/.claude/projects//memory/` is active — read `MEMORY.md` and update it when you learn durable facts about me, my projects, or my preferences. diff --git a/skills/bootstrap-harness/assets/commands/plan.md b/skills/bootstrap-harness/assets/commands/plan.md new file mode 100644 index 0000000..d69aa66 --- /dev/null +++ b/skills/bootstrap-harness/assets/commands/plan.md @@ -0,0 +1,34 @@ +--- +description: Draft a Goal/Constraints/Acceptance plan for the task in $ARGUMENTS, then enter plan mode +--- + +Before writing any code, produce a plan in this exact shape: + +## Goal +One sentence. The outcome, not the activity. + +## Constraints +Bulleted. Include: +- Files / modules in scope (and explicitly out of scope). +- Stack/version constraints (look at CLAUDE.md, AGENTS.md, composer.json, package.json). +- Backwards-compat or migration concerns. +- Performance, security, or ergonomic budgets the user has flagged. + +## Acceptance criteria +Bulleted, testable. Each one should be checkable by running a command or reading a diff. Examples: +- `composer lint:check` passes. +- `php artisan test --filter=Foo` passes with new cases X and Y. +- `/route X` returns 200 with payload shape Z. +- No new TODOs left behind. + +## Approach +3–7 bullets, ordered. The smallest plan that covers the goal. Identify the *risky* step and how you'll de-risk (spike, isolated test, advisor() call). + +## Open questions +Anything that would change the approach materially. If the answer is guessable from the codebase, go look — don't ask. + +--- + +Task: $ARGUMENTS + +After producing the plan, ask the user to approve or amend before any edits. If they say "go", enter plan-mode-style execution: small steps, run the verification check after each material change, and use `advisor()` once before declaring done. diff --git a/skills/bootstrap-harness/assets/commands/verify.md b/skills/bootstrap-harness/assets/commands/verify.md new file mode 100644 index 0000000..44d19f4 --- /dev/null +++ b/skills/bootstrap-harness/assets/commands/verify.md @@ -0,0 +1,22 @@ +--- +description: Run the project's pass/fail verification check (harness-check.sh, or stack-default lint+types+tests) +--- + +Run the project's verification check and report pass/fail with concise output. Use this before declaring any task done. + +Order of preference: + +1. If `./scripts/harness-check.sh` exists and is executable → run it. +2. Else if `composer.json` exists with a `lint:check` script → run `composer lint:check && composer test` (and `npm run types:check` if `package.json` exists). +3. Else if `package.json` exists → run `npm run lint:check && npm run types:check && npm test` (skip any that aren't defined). +4. Else if `pyproject.toml` exists → `ruff check . && mypy . && pytest -q`. +5. Else if `Cargo.toml` → `cargo check && cargo test`. +6. Else if `go.mod` → `go vet ./... && go test ./...`. + +Report: +- ✅ pass → one line. +- ❌ fail → the failing command, the salient lines from output (not full log), and the smallest fix proposal. + +Do NOT propose a fix that disables a rule unless the user explicitly asks. Never use `--no-verify`. + +If `$ARGUMENTS` is provided, treat it as a path filter — run checks scoped to that path where the tool supports it. diff --git a/skills/bootstrap-harness/assets/hooks/block-force-push.sh b/skills/bootstrap-harness/assets/hooks/block-force-push.sh new file mode 100644 index 0000000..48d11c0 --- /dev/null +++ b/skills/bootstrap-harness/assets/hooks/block-force-push.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# PreToolUse hook for Bash. Block destructive operations. +# Reads JSON on stdin: {tool_name, tool_input: {command, ...}, ...} +# Exit 2 = block + tell model why. Exit 0 = allow. +# +# Matching strategy: split the command on shell separators (; && || | newline), +# then for each *segment*, look at its leading words. This avoids false positives +# from strings inside `echo`, `printf`, comments, heredocs, etc. + +set -u +input="$(cat)" +cmd="$(printf '%s' "$input" | python3 -c 'import json,sys +try: + d=json.load(sys.stdin); print(d.get("tool_input",{}).get("command","")) +except Exception: pass' 2>/dev/null || true)" + +[ -z "$cmd" ] && exit 0 + +block() { + printf 'BLOCKED by ~/.claude/hooks/block-force-push.sh\nSegment: %s\nReason: %s\nIf you genuinely need this, ask the user first.\n' "$1" "$2" >&2 + exit 2 +} + +# Split into segments on ; && || | & and newlines. Crude but enough. +segments="$(CLAUDE_HOOK_CMD="$cmd" python3 - <<'PY' +import os, re +src = os.environ.get("CLAUDE_HOOK_CMD","") +out = [] +buf = [] +i = 0 +in_s = None +while i < len(src): + c = src[i] + if in_s: + buf.append(c) + if c == in_s and (i == 0 or src[i-1] != "\\"): + in_s = None + elif c in ("'", '"'): + in_s = c + buf.append(c) + elif c in (";", "\n"): + out.append("".join(buf)); buf = [] + elif c == "&" and i+1 < len(src) and src[i+1] == "&": + out.append("".join(buf)); buf = []; i += 1 + elif c == "|" and i+1 < len(src) and src[i+1] == "|": + out.append("".join(buf)); buf = []; i += 1 + elif c == "|": + out.append("".join(buf)); buf = [] + else: + buf.append(c) + i += 1 +out.append("".join(buf)) +for seg in out: + s = seg.strip() + if s: print(s) +PY +)" + +while IFS= read -r seg; do + [ -z "$seg" ] && continue + + case "$seg" in + echo*|printf*|cat*|"# "*|"#"*) continue ;; + esac + + case "$seg" in *"--force-with-lease"*) continue ;; esac + + if echo "$seg" | grep -Eq '^[[:space:]]*git[[:space:]]+push[[:space:]].*(--force|[[:space:]]-f[[:space:]]).*[[:space:]](main|master|HEAD:main|HEAD:master)([[:space:]]|$)'; then + block "$seg" "force-push to main/master" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*git[[:space:]]+push[[:space:]].*(--force|[[:space:]]-f[[:space:]])'; then + block "$seg" "force-push without --force-with-lease" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*git[[:space:]]+reset[[:space:]]+--hard[[:space:]]+(origin|upstream)/'; then + block "$seg" "hard reset to remote" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*git[[:space:]]+(checkout|restore)[[:space:]]+\.[[:space:]]*$'; then + block "$seg" "wholesale discard of working tree" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*git[[:space:]]+branch[[:space:]]+-D[[:space:]]'; then + block "$seg" "force-delete branch" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*git[[:space:]]+(commit|merge|push|rebase)[[:space:]].*--no-verify'; then + block "$seg" "skipping git hooks (--no-verify)" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*rm[[:space:]]+(-[rRf]+[[:space:]]+)+(/|~|\$HOME|/\*|~/?\*)([[:space:]]|$)'; then + block "$seg" "rm -rf on \$HOME or /" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*git[[:space:]]+clean[[:space:]].*-f[dx]*[[:space:]]*$'; then + block "$seg" "git clean -fd (destroys untracked work)" + fi + if echo "$seg" | grep -Eq '^[[:space:]]*chmod[[:space:]]+-R[[:space:]]+777'; then + block "$seg" "chmod -R 777" + fi +done <<< "$segments" + +exit 0 diff --git a/skills/bootstrap-harness/assets/hooks/format-on-edit.sh b/skills/bootstrap-harness/assets/hooks/format-on-edit.sh new file mode 100644 index 0000000..221d32e --- /dev/null +++ b/skills/bootstrap-harness/assets/hooks/format-on-edit.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# PostToolUse hook for Write|Edit. Run the project's formatter if available. +# Silent on success, prints output on failure (advisory — non-blocking). + +set -u +cd "$(pwd)" || exit 0 + +run() { "$@" >/dev/null 2>&1 || { echo "format hint: '$*' had non-zero exit"; return 0; }; } + +# Laravel/PHP +if [ -f vendor/bin/pint ]; then + run vendor/bin/pint --quiet +fi + +# Node — only if a format script exists +if [ -f package.json ] && grep -q '"format"' package.json 2>/dev/null; then + if command -v bun >/dev/null 2>&1; then + run bun run format + elif command -v npm >/dev/null 2>&1; then + run npm run format --silent + fi +fi + +# Python — ruff if config exists +if [ -f pyproject.toml ] && grep -q 'ruff' pyproject.toml 2>/dev/null && command -v ruff >/dev/null 2>&1; then + run ruff format . +fi + +# Go +if [ -f go.mod ] && command -v gofmt >/dev/null 2>&1; then + run gofmt -w . +fi + +# Rust +if [ -f Cargo.toml ] && command -v cargo >/dev/null 2>&1; then + run cargo fmt --quiet +fi + +exit 0 diff --git a/skills/bootstrap-harness/assets/hooks/post-compact-reinject.sh b/skills/bootstrap-harness/assets/hooks/post-compact-reinject.sh new file mode 100644 index 0000000..9563009 --- /dev/null +++ b/skills/bootstrap-harness/assets/hooks/post-compact-reinject.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# PostCompact hook. Re-inject the project's CLAUDE.md (and AGENTS.md if present) +# so context-compression doesn't strip the operating contract. +# Output goes to the model. + +set -u + +emit() { + [ -f "$1" ] || return 0 + echo + echo "--- $1 (re-injected after compact) ---" + cat "$1" +} + +emit ./CLAUDE.md +emit ./AGENTS.md +emit ~/.claude/CLAUDE.md + +exit 0 diff --git a/skills/bootstrap-harness/assets/hooks/verify-before-stop.sh b/skills/bootstrap-harness/assets/hooks/verify-before-stop.sh new file mode 100644 index 0000000..6b1a46a --- /dev/null +++ b/skills/bootstrap-harness/assets/hooks/verify-before-stop.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Stop hook. Refuse to stop if the project's verification check fails. +# Exit 2 = block stop and feed stderr back to the model. +# Only runs if a verification script exists — silent otherwise. + +set -u + +# Honour an explicit opt-out for sessions where you're mid-investigation. +[ "${CLAUDE_SKIP_VERIFY:-}" = "1" ] && exit 0 + +if [ -x ./scripts/harness-check.sh ]; then + if ! out="$(./scripts/harness-check.sh 2>&1)"; then + printf 'Stop blocked by ~/.claude/hooks/verify-before-stop.sh: harness-check.sh failed.\n\n%s\n\nFix or amend the rule (with rationale) before declaring done. Set CLAUDE_SKIP_VERIFY=1 to override.\n' "$out" >&2 + exit 2 + fi + exit 0 +fi + +# Fallback — only run if there's a clear, fast check. +if [ -f composer.json ] && grep -q '"lint:check"' composer.json 2>/dev/null; then + if ! out="$(composer lint:check 2>&1)"; then + printf 'Stop blocked: composer lint:check failed.\n\n%s\n' "$out" >&2 + exit 2 + fi +fi + +exit 0 diff --git a/skills/bootstrap-harness/assets/memory/MEMORY.md.tmpl b/skills/bootstrap-harness/assets/memory/MEMORY.md.tmpl new file mode 100644 index 0000000..800aef3 --- /dev/null +++ b/skills/bootstrap-harness/assets/memory/MEMORY.md.tmpl @@ -0,0 +1,8 @@ + +- [User role](user_role.md) — who you are, your stack, how you like to work +- [Concise output](feedback_concise.md) — terse responses, no trailing summaries +- [Plan-first for non-trivial tasks](feedback_plan_first.md) — plan mode before code; one task per conversation +- [Verification gate](feedback_verification.md) — never declare done without running the project's pass/fail check diff --git a/skills/bootstrap-harness/assets/memory/feedback_concise.md.tmpl b/skills/bootstrap-harness/assets/memory/feedback_concise.md.tmpl new file mode 100644 index 0000000..da904a5 --- /dev/null +++ b/skills/bootstrap-harness/assets/memory/feedback_concise.md.tmpl @@ -0,0 +1,11 @@ +--- +name: Concise output +description: Terse responses, no trailing summaries, no narration of internal deliberation +type: feedback +--- + +I want terse output. No trailing "summary of what I just did" — I read the diff. No narration of internal thought process. State results and decisions directly. + +**Why:** Reading dense, well-edited responses is faster than skimming verbose ones. The information density per token is what matters. + +**How to apply:** End-of-turn = one or two sentences max. No headers/sections for simple tasks. No emojis unless asked. When in doubt, cut it. diff --git a/skills/bootstrap-harness/assets/memory/feedback_plan_first.md.tmpl b/skills/bootstrap-harness/assets/memory/feedback_plan_first.md.tmpl new file mode 100644 index 0000000..b70970e --- /dev/null +++ b/skills/bootstrap-harness/assets/memory/feedback_plan_first.md.tmpl @@ -0,0 +1,11 @@ +--- +name: Plan-first for non-trivial tasks +description: Enter plan mode and structure Goal/Constraints/Acceptance before coding; one task per conversation +type: feedback +--- + +For any non-trivial task, enter plan mode before writing code. Structure the plan as Goal / Constraints / Acceptance Criteria. One task per conversation — use `/clear` between tasks rather than continuing in a degraded session. + +**Why:** Plan-first is the convergent recommendation across Cherny, Vincent, Schluntz, Wu. "One task per conversation — starting fresh costs ~20k tokens, trivial vs. quality loss from a degraded session." + +**How to apply:** If a task involves more than one file or has any ambiguity, plan first. For one-line bug fixes, just fix it. The signal is the task's *blast radius*, not its line count. diff --git a/skills/bootstrap-harness/assets/memory/feedback_verification.md.tmpl b/skills/bootstrap-harness/assets/memory/feedback_verification.md.tmpl new file mode 100644 index 0000000..0e7e84a --- /dev/null +++ b/skills/bootstrap-harness/assets/memory/feedback_verification.md.tmpl @@ -0,0 +1,11 @@ +--- +name: Verification gate +description: Never declare done without running the project's pass/fail check; harness is authoritative +type: feedback +--- + +Never claim a task is done without running the project's verification command. If `./scripts/harness-check.sh` exists, that's the canonical check. Otherwise run lint + types + tests for the stack. + +**Why:** The harness is authoritative — Cherny: "the #1 thing to give Claude is a way to verify its work — 2-3× quality." Disabling a check silently is worse than the original bug. + +**How to apply:** Before saying "done" or proposing a commit, run the check. If it fails, fix the code OR amend the rule with a one-line rationale in the same change. Never `--no-verify` to make it pass. diff --git a/skills/bootstrap-harness/assets/memory/user_role.md.tmpl b/skills/bootstrap-harness/assets/memory/user_role.md.tmpl new file mode 100644 index 0000000..ea8c3a9 --- /dev/null +++ b/skills/bootstrap-harness/assets/memory/user_role.md.tmpl @@ -0,0 +1,17 @@ +--- +name: User role +description: My professional context, stack, and preferred working style +type: user +--- + + + +I'm a senior engineer working primarily in . Active projects: +- `/` — +- `/` — + +Default stack: . + +Tooling I use heavily: . + +**Implications:** Frame architectural suggestions in vocabulary I'd recognize. Don't suggest dependency changes without explicit ask. I value token economics — assume I run multiple sessions and care about cache-hit rates. diff --git a/skills/bootstrap-harness/scripts/audit-prompt.md b/skills/bootstrap-harness/scripts/audit-prompt.md new file mode 100644 index 0000000..c4da502 --- /dev/null +++ b/skills/bootstrap-harness/scripts/audit-prompt.md @@ -0,0 +1,72 @@ +# Monthly Claude Code setup audit — agent prompt + +Use this prompt as the `events[].data.message.content` when creating a remote routine +that audits a snapshot repo against the latest Anthropic releases and Claude Code +community best practice. The routine should clone the snapshot repo (e.g. `/claude-setup`) +and have access to Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, Agent. + +--- + +You are auditing my Claude Code setup against the latest Anthropic releases and Claude Code community best practice. The repo you are running in is a sanitized mirror of `~/.claude/` — see README.md for the layout. + +## Your task + +1. **Inventory the current setup.** Read CLAUDE.md, settings.json, hooks/*.sh, commands/*.md, agents/*.md (if any), memory/MEMORY.md and the linked memory files, plugins/installed_plugins.json, and skills-installed.txt. + +2. **Research what shipped in the last ~30 days.** Use WebFetch + WebSearch on: + - https://platform.claude.com/docs/en/release-notes/overview + - https://code.claude.com/docs/en/changelog + - https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md + - https://www.anthropic.com/news + - https://www.anthropic.com/engineering + +3. **Read the canonical Claude Code voices for new posts.** WebFetch each: + - https://howborisusesclaudecode.com/ + - https://simonwillison.net/tags/claude-code/ + - https://blog.fsck.com/ + - https://ghuntley.com/ + - https://hamel.dev/blog/ + - https://steve-yegge.medium.com/ + +4. **Compare and propose deltas.** For each finding, decide: does this suggest a change to CLAUDE.md / settings.json / hooks / commands / agents / memory / plugins? + +5. **Write the report** to `audits/YYYY-MM-DD-setup-audit.md`. Structure: + + ```markdown + # Setup audit — {{date}} + + ## TL;DR + 3-5 bullets of the highest-leverage changes. + + ## What shipped (last ~30 days) + ### Anthropic + - Dated bullets, source-linked. + ### Community + - Dated bullets, source-linked. + + ## Gaps in current setup + Per surface: what's missing or stale, with the specific change. + + ## Proposed deltas (ordered by leverage) + - **What:** one-line summary + - **Where:** exact file path + - **Diff:** old → new + - **Why:** which release/post motivates this, with link + + ## Skip-list + Things that looked relevant but aren't worth doing. + + ## Sources + ``` + +6. **Open a PR.** Branch `audit/YYYY-MM-DD`, commit `audit: monthly setup audit YYYY-MM-DD`, PR title `Setup audit — {{date}}`, PR body = TL;DR. Use `gh pr create`. + +## Constraints + +- DO NOT modify any tracked file outside `audits/`. +- If a recommendation involves a model migration (deprecation), call it URGENT in TL;DR. +- Prefer specific over comprehensive. 5 items I'll act on > 50 I won't. +- If nothing material shipped, write a short report saying so. Don't pad. +- Concise output — frame in feedforward / sensors / GC vocabulary if I use it. + +Report length target: 800-1500 words. diff --git a/skills/bootstrap-harness/scripts/install.sh b/skills/bootstrap-harness/scripts/install.sh new file mode 100755 index 0000000..d324b0c --- /dev/null +++ b/skills/bootstrap-harness/scripts/install.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +# Idempotent installer for the bootstrap-harness skill. +# Copies templates from $SKILL_DIR/assets/ into ~/.claude/, never clobbers existing files +# unless --force is passed. Patches settings.json to wire up hooks. +# +# Usage: +# bash install.sh # interactive, never clobber +# bash install.sh --force # overwrite existing hooks/commands/CLAUDE.md +# bash install.sh --dry-run # show what would be done +# bash install.sh --skip-memory # leave memory/ alone (recommended if already populated) +# bash install.sh --skip-settings # leave settings.json alone +# +# Reads SKILL_DIR from env if set, otherwise computes from script location. + +set -euo pipefail + +FORCE=0 +DRY=0 +SKIP_MEMORY=0 +SKIP_SETTINGS=0 +for arg in "$@"; do + case "$arg" in + --force) FORCE=1 ;; + --dry-run) DRY=1 ;; + --skip-memory) SKIP_MEMORY=1 ;; + --skip-settings) SKIP_SETTINGS=1 ;; + *) echo "unknown flag: $arg" >&2; exit 2 ;; + esac +done + +SKILL_DIR="${SKILL_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +ASSETS="$SKILL_DIR/assets" +HOME_CLAUDE="${HOME_CLAUDE:-$HOME/.claude}" +USER_PROJECT_KEY="${USER_PROJECT_KEY:-$(printf '%s' "$HOME" | tr '/' '-')}" # e.g. -Users-marlinf +MEMORY_DIR="$HOME_CLAUDE/projects/$USER_PROJECT_KEY/memory" + +[ -d "$ASSETS" ] || { echo "error: $ASSETS not found" >&2; exit 1; } + +say() { echo "→ $*"; } +do_or_dry() { if [ $DRY -eq 1 ]; then echo " [dry-run] $*"; else eval "$@"; fi; } + +# 1. Ensure target dirs. +say "ensuring directories" +do_or_dry "mkdir -p '$HOME_CLAUDE/hooks' '$HOME_CLAUDE/commands' '$HOME_CLAUDE/agents' '$MEMORY_DIR'" + +# Helper: copy with overwrite policy. +copy_safe() { + local src="$1" dst="$2" + if [ -e "$dst" ] && [ $FORCE -eq 0 ]; then + echo " skip (exists): $dst" + return 0 + fi + if [ $DRY -eq 1 ]; then + echo " [dry-run] would install: $dst" + else + cp "$src" "$dst" + echo " installed: $dst" + fi +} + +# 2. CLAUDE.md (template — never clobber by default; users edit this heavily). +say "installing global CLAUDE.md" +copy_safe "$ASSETS/CLAUDE.md.tmpl" "$HOME_CLAUDE/CLAUDE.md" + +# 3. Hooks. +say "installing hooks" +for f in "$ASSETS/hooks/"*.sh; do + name="$(basename "$f")" + copy_safe "$f" "$HOME_CLAUDE/hooks/$name" + do_or_dry "chmod +x '$HOME_CLAUDE/hooks/$name'" +done + +# 4. Commands. +say "installing slash commands" +for f in "$ASSETS/commands/"*.md; do + name="$(basename "$f")" + copy_safe "$f" "$HOME_CLAUDE/commands/$name" +done + +# 5. Memory templates. +if [ $SKIP_MEMORY -eq 1 ]; then + say "skipping memory (--skip-memory)" +else + say "installing memory templates" + for f in "$ASSETS/memory/"*.tmpl; do + name="$(basename "$f" .tmpl)" + copy_safe "$f" "$MEMORY_DIR/$name" + done +fi + +# 6. Patch settings.json — add env vars + hooks blocks if missing. +if [ $SKIP_SETTINGS -eq 1 ]; then + say "skipping settings.json (--skip-settings)" +else + say "patching settings.json" + SETTINGS="$HOME_CLAUDE/settings.json" + if [ ! -f "$SETTINGS" ]; then + do_or_dry "echo '{}' > '$SETTINGS'" + fi + if [ $DRY -eq 0 ]; then + python3 - </claude-setup bash snapshot.sh +# +# Override sources with env: +# CLAUDE_DIR=/some/path bash snapshot.sh +# USER_PROJECT_KEY=-Users-foo bash snapshot.sh + +set -euo pipefail + +CLAUDE_DIR="${CLAUDE_DIR:-$HOME/.claude}" +USER_PROJECT_KEY="${USER_PROJECT_KEY:-$(printf '%s' "$HOME" | tr '/' '-')}" +MEMORY_SRC="$CLAUDE_DIR/projects/$USER_PROJECT_KEY/memory" + +if [ -z "${SNAPSHOT_REPO:-}" ]; then + echo "error: SNAPSHOT_REPO must be set to the target repo path" >&2 + echo " e.g. SNAPSHOT_REPO=~/Projects/you/claude-setup bash snapshot.sh" >&2 + exit 2 +fi + +REPO_ROOT="$(cd "$SNAPSHOT_REPO" 2>/dev/null && pwd)" || { + echo "error: $SNAPSHOT_REPO does not exist (mkdir + git init it first)" >&2 + exit 2 +} + +cd "$REPO_ROOT" +[ -d .git ] || { echo "error: $REPO_ROOT is not a git repo" >&2; exit 2; } + +# 1. Wipe sync targets (preserves audits/, .git/, README.md, .gitignore, scripts/). +for d in hooks commands agents memory plugins; do + rm -rf "${REPO_ROOT:?}/$d" + mkdir -p "$d" +done +rm -f CLAUDE.md settings.json skills-installed.txt + +# 2. Mirror. +[ -f "$CLAUDE_DIR/CLAUDE.md" ] && cp "$CLAUDE_DIR/CLAUDE.md" ./CLAUDE.md +[ -f "$CLAUDE_DIR/settings.json" ] && cp "$CLAUDE_DIR/settings.json" ./settings.json + +shopt -s nullglob +for f in "$CLAUDE_DIR/hooks/"*.sh; do cp "$f" ./hooks/; done +for f in "$CLAUDE_DIR/commands/"*.md; do cp "$f" ./commands/; done +for f in "$CLAUDE_DIR/agents/"*.md; do cp "$f" ./agents/; done +shopt -u nullglob + +if [ -d "$MEMORY_SRC" ]; then + find "$MEMORY_SRC" -maxdepth 1 -type f \( -name '*.md' -o -name 'MEMORY.md' \) \ + -exec cp {} ./memory/ \; +fi +if [ -f "$CLAUDE_DIR/plugins/installed_plugins.json" ]; then + cp "$CLAUDE_DIR/plugins/installed_plugins.json" ./plugins/installed_plugins.json +fi +if [ -d "$CLAUDE_DIR/skills" ]; then + ls "$CLAUDE_DIR/skills" > ./skills-installed.txt +fi + +# 3. Drop empty dirs. +for d in hooks commands agents memory plugins; do + if [ -d "$d" ] && [ -z "$(ls -A "$d" 2>/dev/null)" ]; then + rmdir "$d" + fi +done + +# 4. Secret scan. +SECRET_PATTERNS='(sk-ant-|ghp_|gho_|ghu_|AIza[0-9A-Za-z_-]{35}|AKIA[0-9A-Z]{16}|xox[baprs]-[0-9A-Za-z-]{10,}|-----BEGIN [A-Z ]*PRIVATE KEY-----)' +if grep -rEln "$SECRET_PATTERNS" . 2>/dev/null \ + | grep -vE '^(\./)?(\.git/|scripts/snapshot\.sh)'; then + echo "abort: potential secret detected — review above and re-run after scrubbing" >&2 + exit 1 +fi + +# 5. Commit + push. +git add -A +if git diff --cached --quiet; then + echo "snapshot: no changes" + exit 0 +fi +ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +summary="$(git diff --cached --stat | tail -n 1)" +git -c commit.gpgsign=false commit -q -m "snapshot: refresh ~/.claude/ — $ts + +$summary" +if git remote get-url origin >/dev/null 2>&1; then + git push -q origin HEAD + echo "snapshot: pushed" +else + echo "snapshot: committed locally (no remote)" +fi From f14c654129f18e8d825c20d9323eadb1ec1f2a67 Mon Sep 17 00:00:00 2001 From: Marlin Forbes Date: Fri, 1 May 2026 09:21:50 +0200 Subject: [PATCH 02/13] Add uninstall.sh for bootstrap-harness skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symmetric reversal of install.sh, conservative by default: - Removes hooks and slash commands only if their sha256 still matches the installed template; user-modified files are kept and reported. - Strips the 4 hook entries from settings.json; drops empty hook arrays. Leaves permissions, marketplaces, statusLine, etc. untouched. - Keeps CLAUDE.md, memory entries, and CLAUDE_CODE_AUTO_COMPACT_WINDOW by default — those tend to be customised. Opt in with --remove-claude-md, --remove-memory, --remove-env, or --all. - Flags: --dry-run, --force (skip content-match), --all. Smoke-tested: - Default uninstall after fresh install removes 6 files + cleans settings, keeps CLAUDE.md/memory/env. Empty parent dirs rmdir'd. - Modified hook is reported "keep (modified)" without --force; removed with. - --all on a fresh install reduces 13 files → empty settings.json + 0 files. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 2 +- skills/bootstrap-harness/README.md | 5 +- skills/bootstrap-harness/SKILL.md | 18 ++ skills/bootstrap-harness/scripts/uninstall.sh | 194 ++++++++++++++++++ 4 files changed, 217 insertions(+), 2 deletions(-) create mode 100755 skills/bootstrap-harness/scripts/uninstall.sh diff --git a/README.md b/README.md index 811ebad..1367d40 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Trello board health audit. Queries five dimensions of card data to identify thro ### `/bootstrap-harness` -Bootstraps a "harness-engineering" Claude Code setup at user scope (`~/.claude/`): dense operating-contract CLAUDE.md, four deterministic guardrail hooks (block force-push, format on edit, re-inject CLAUDE.md after compact, refuse to stop with broken build), `/verify` and `/plan` slash commands, an auto-memory seed (concise / plan-first / verification-gate feedback memories + a user_role template), and an optional monthly remote-audit pipeline (snapshot the setup to a private git repo, schedule a routine that PRs deltas against the latest Anthropic releases and Claude Code community patterns). Idempotent installer with `--dry-run` / `--force` / `--skip-memory` / `--skip-settings`; never clobbers existing files unless asked. +Bootstraps a "harness-engineering" Claude Code setup at user scope (`~/.claude/`): dense operating-contract CLAUDE.md, four deterministic guardrail hooks (block force-push, format on edit, re-inject CLAUDE.md after compact, refuse to stop with broken build), `/verify` and `/plan` slash commands, an auto-memory seed (concise / plan-first / verification-gate feedback memories + a user_role template), and an optional monthly remote-audit pipeline (snapshot the setup to a private git repo, schedule a routine that PRs deltas against the latest Anthropic releases and Claude Code community patterns). Idempotent installer with `--dry-run` / `--force` / `--skip-memory` / `--skip-settings`; never clobbers existing files unless asked. Ships with a symmetric `uninstall.sh` (content-match check keeps user-modified files; `--all` for full sweep). **Arguments:** None. diff --git a/skills/bootstrap-harness/README.md b/skills/bootstrap-harness/README.md index 3c2520c..3038294 100644 --- a/skills/bootstrap-harness/README.md +++ b/skills/bootstrap-harness/README.md @@ -22,7 +22,9 @@ Three stages: 1. **Bootstrap** (`scripts/install.sh`) — copies templates from `assets/` into `~/.claude/`, never overwrites without `--force`. Patches `settings.json` to wire hooks. `--dry-run` shows what would happen. 2. **Snapshot** (`scripts/snapshot.sh`, optional) — mirrors `~/.claude/` into a private git repo, scrubs caches and secret patterns, commits + pushes only on diff. Run after material config changes. -3. **Audit** (optional) — schedule a monthly remote routine using the prompt at `scripts/audit-prompt.md`. The agent clones your snapshot repo, researches the last ~30 days of Anthropic releases and canonical Claude Code voices (Cherny / Willison / Vincent / Huntley / Husain / Yegge), and PRs `audits/YYYY-MM-DD-setup-audit.md` with prioritised deltas. +3. **Audit** (optional) — schedule a monthly remote routine using the prompt at `scripts/audit-prompt.md`. + +4. **Uninstall** (`scripts/uninstall.sh`) — symmetric reversal. Removes the 4 hooks, 2 commands, and the 4 hook entries from `settings.json`, but only for files that still match the installed template (so any customisation you made is kept). Pass `--remove-memory`, `--remove-claude-md`, `--remove-env`, or `--all` to broaden the sweep. `--dry-run` shows what would happen. The agent clones your snapshot repo, researches the last ~30 days of Anthropic releases and canonical Claude Code voices (Cherny / Willison / Vincent / Huntley / Husain / Yegge), and PRs `audits/YYYY-MM-DD-setup-audit.md` with prioritised deltas. The hooks: - **block-force-push.sh** (PreToolUse:Bash) — segment-aware matcher. Blocks force-push to main/master, hard reset to remote, `rm -rf ~`, `--no-verify`, world-writable chmod, branch -D. Allows `--force-with-lease`. Doesn't false-trigger on echoed strings. @@ -45,6 +47,7 @@ None. The skill detects what's already in `~/.claude/`, reports gaps, runs the i | `assets/commands/*.md` | `/verify`, `/plan` | | `assets/memory/*.tmpl` | MEMORY.md index + 3 feedback memories + user_role template | | `scripts/install.sh` | Idempotent installer (`--dry-run` / `--force` / `--skip-memory` / `--skip-settings`) | +| `scripts/uninstall.sh` | Symmetric uninstaller — content-match check keeps user-modified files; `--all` for full sweep | | `scripts/snapshot.sh` | Sanitised mirror of `~/.claude/` → target git repo | | `scripts/audit-prompt.md` | Prompt template for the monthly remote-audit routine | diff --git a/skills/bootstrap-harness/SKILL.md b/skills/bootstrap-harness/SKILL.md index de04de3..d7bd602 100644 --- a/skills/bootstrap-harness/SKILL.md +++ b/skills/bootstrap-harness/SKILL.md @@ -107,6 +107,23 @@ If the user wants version-controlled config + a monthly audit: 4. **Tell the user to re-run `snapshot.sh`** whenever they materially change `~/.claude/`. Or schedule it locally via `/loop` or a launchd plist. +## Uninstall + +If the user asks to remove the harness ("uninstall", "remove the harness", "undo bootstrap-harness"), run: + +```bash +bash "$SKILL_DIR/scripts/uninstall.sh" +``` + +The uninstaller is symmetric and conservative: + +- Removes the 4 hooks and 2 slash commands **only if their content still matches the installed template** (sha256 compare against `assets/`). User-modified files are kept and reported as `keep (modified)`. +- Strips the 4 hook entries from `settings.json`. Drops empty hook event arrays. Leaves all other settings (permissions, marketplaces, statusLine, etc.) untouched. +- **Keeps by default:** `CLAUDE.md`, memory files, the `CLAUDE_CODE_AUTO_COMPACT_WINDOW` env var. Those tend to be customised. Pass `--remove-claude-md`, `--remove-memory`, `--remove-env` to opt in. +- Flags: `--dry-run`, `--force` (override content-match check), `--all` (= `--force --remove-claude-md --remove-memory --remove-env`). + +After uninstall, tell the user to **restart Claude Code** so the hook deregistration takes effect. + ## Step 5: Verify the install ```bash @@ -142,5 +159,6 @@ Tell the user to **restart Claude Code (or open a new session)** — hooks load | `assets/commands/*.md` | `/verify`, `/plan` slash commands | | `assets/memory/*.tmpl` | MEMORY.md index + 3 feedback memories + user_role template | | `scripts/install.sh` | Idempotent installer with `--dry-run` / `--force` / `--skip-*` flags | +| `scripts/uninstall.sh` | Symmetric uninstaller; content-match check keeps user-modified files; `--all` for full sweep | | `scripts/snapshot.sh` | Sanitised mirror of `~/.claude/` → target git repo | | `scripts/audit-prompt.md` | Prompt template for the monthly remote-audit routine | diff --git a/skills/bootstrap-harness/scripts/uninstall.sh b/skills/bootstrap-harness/scripts/uninstall.sh new file mode 100755 index 0000000..09e1e61 --- /dev/null +++ b/skills/bootstrap-harness/scripts/uninstall.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash +# Uninstaller for the bootstrap-harness skill. +# Conservative by default: only removes files whose content still matches the +# installed template (sha256 compare against $SKILL_DIR/assets/). User-modified +# files are kept. CLAUDE.md, memory entries, and the env var are kept unless +# explicitly opted in — those tend to be customised heavily. +# +# Usage: +# bash uninstall.sh # remove only unmodified hooks + commands + hook entries +# bash uninstall.sh --dry-run # show what would happen +# bash uninstall.sh --force # remove hooks/commands even if modified +# bash uninstall.sh --remove-memory # also remove memory templates (content-match policy) +# bash uninstall.sh --remove-claude-md # also remove CLAUDE.md (content-match policy) +# bash uninstall.sh --remove-env # also remove CLAUDE_CODE_AUTO_COMPACT_WINDOW env var +# bash uninstall.sh --all # equivalent to --force --remove-memory --remove-claude-md --remove-env + +set -euo pipefail + +FORCE=0 +DRY=0 +REMOVE_MEMORY=0 +REMOVE_CLAUDE_MD=0 +REMOVE_ENV=0 +for arg in "$@"; do + case "$arg" in + --force) FORCE=1 ;; + --dry-run) DRY=1 ;; + --remove-memory) REMOVE_MEMORY=1 ;; + --remove-claude-md) REMOVE_CLAUDE_MD=1 ;; + --remove-env) REMOVE_ENV=1 ;; + --all) FORCE=1; REMOVE_MEMORY=1; REMOVE_CLAUDE_MD=1; REMOVE_ENV=1 ;; + -h|--help) + sed -n '2,17p' "${BASH_SOURCE[0]}" | sed 's/^# \?//' + exit 0 ;; + *) echo "unknown flag: $arg" >&2; exit 2 ;; + esac +done + +SKILL_DIR="${SKILL_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +ASSETS="$SKILL_DIR/assets" +HOME_CLAUDE="${HOME_CLAUDE:-$HOME/.claude}" +USER_PROJECT_KEY="${USER_PROJECT_KEY:-$(printf '%s' "$HOME" | tr '/' '-')}" +MEMORY_DIR="$HOME_CLAUDE/projects/$USER_PROJECT_KEY/memory" + +[ -d "$ASSETS" ] || { echo "error: $ASSETS not found" >&2; exit 1; } + +say() { echo "→ $*"; } + +# sha256_eq — true if contents match. +sha256_eq() { + [ -f "$1" ] && [ -f "$2" ] || return 1 + local a b + a="$(shasum -a 256 < "$1" | awk '{print $1}')" + b="$(shasum -a 256 < "$2" | awk '{print $1}')" + [ "$a" = "$b" ] +} + +# remove_safe