Repository navigation
feat(setup): manifest-driven installer, safe merges, and /setup skill - #1
Merged
Merged
Conversation
…e arg, un-pin beta header Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Un-ignore scripts/, tests/, .claude/skills/setup/, and .github/ so the adopter-setup installer, bats tests, repo-scoped setup skill, and CI workflows can be tracked. The .claude/ allowlist chain un-ignores only the setup skill's parent directories, so agent-memory and other .claude/ content stay ignored by default. Adds an explicit .claude/settings.json ignore as a security rail, guaranteeing the repo never ships an active settings file even though .claude/ is partially un-ignored. Co-Authored-By: Claude <noreply@anthropic.com>
…ead-only agents Aligns agent frontmatter with the effort policy in WORKFLOW.md: review-type agents (design-reviewer, reviewer) get effort: xhigh, research agents (researcher) get effort: high. Adds disallowedTools: [Write, Edit] to Explore and researcher to enforce the read-only contract already claimed in their descriptions. Co-Authored-By: Claude <noreply@anthropic.com>
…tusline caveat
Rewrite all four hook command strings to resolve scripts via
${CLAUDE_CONFIG_DIR:-$HOME/.claude}/hooks/ instead of a hardcoded ~/.claude
path, so the template works for CLAUDE_CONFIG_DIR adopters with no
merge-time rewriting. Guard the macOS-only osascript Notification hook
with a command -v check so it no-ops gracefully on Linux. Extend the
statusLine _comment to flag that reading .oauthAccount from the local
config JSON relies on an undocumented internal interface that may break
in future Claude Code versions.
Co-Authored-By: Claude <noreply@anthropic.com>
… with shipped behavior Replace the three "≤3" design-reviewer loop claims with convergence language (escalates after 5 iterations, matching the shipped design skill), add the effort field to the agent frontmatter list, flag the Notification hook as macOS-only, and point Quick Start at the README's /setup flow instead of implying manual copying. Co-Authored-By: Claude <noreply@anthropic.com>
… guard The delegate guard's editable-path allowlist only recognized $HOME/.claude/plans/*, so adopters running with CLAUDE_CONFIG_DIR set had their plan-file edits under $CLAUDE_CONFIG_DIR/plans/ wrongly blocked during /build. Accept both forms and mention both in the block message. Adds tests/hooks.bats covering allow/block behavior under both layouts. Co-Authored-By: Claude <noreply@anthropic.com>
…eck.sh Foundation for the adopter installer: scripts/lib.sh (config-dir resolution with absolutization, symlinked-dir accept / regular-file + source==destination refusals, refuse_symlink, threaded backup dir, incremental receipts, jq fail-closed, atomic writes), an enforced scripts/manifest.txt (== git ls-files agents/ hooks/ rules/ skills/), scripts/check.sh (default severity map with the 2.1.53 CVE floor and 2.1.132 / 2.1.198 degradation tiers, plus --post artifact verification with filename-match hook checks), and the frozen shared bats helper. Co-Authored-By: Claude <noreply@anthropic.com>
…fallbacks Merge the repo's managed workflow-rules block into the adopter's CLAUDE.md: fresh copy of the block for a new file, version-agnostic in-place replace when markers exist, append when they do not, and a non-destructive refusal on malformed marker states or symlinked targets. Dry-run/apply modes with backup-dir threading, atomic writes, receipts, and idempotent re-runs, per the shared script contract in lib.sh. Co-Authored-By: Claude <noreply@anthropic.com>
…nd receipts Adds the manifest-driven installer (agents/hooks/rules/skills): missing files install, byte-identical files are no-ops, differing files are skipped by default and reported per category, and --force/--force=<category> overwrite after backing up the adopter's file. --dry-run (optionally with --diff) previews every action with zero mutations; a symlinked destination file is refused per-file while the rest of the run continues; receipts and backups thread through scripts/lib.sh. Co-Authored-By: Claude <noreply@anthropic.com>
…t dedup Add scripts/merge-settings.sh, the sole writer of $CLAUDE_DIR/settings.json: a missing target receives the template verbatim (M3), while an existing target gets a narrow jq merge limited to .hooks and .permissions.allow. Hook reconciliation is basename-keyed against manifest.txt, so old manual-cp-era command strings are upgraded to the config-dir-aware form without duplication. permissions.allow is concat + unique. The merge is byte-stable idempotent, so a re-run is a CCS-STATUS:unchanged no-op. --dry-run previews a unified diff and mutates nothing; refusals (symlink, missing jq, unparseable, non-object) exit 1. Co-Authored-By: Claude <noreply@anthropic.com>
…pplies Add .claude/skills/setup/SKILL.md — a repo-scoped, non-model-invocable router that orchestrates check.sh/install.sh/merge-*.sh through a preflight -> install -> merge -> handoff flow. Only read-only and --dry-run forms are pre-approved in allowed-tools; the writing forms (install, --force, --apply) deliberately trigger a permission prompt as the second consent gate. A diff is shown before every write and a single install backup dir is threaded into every apply. Add tests/manual-setup-checklist.md — the pre-PR human gate covering the conversational behaviors the .bats suites cannot: declined-merge respect, exit-2 stop, diff-before-every-consent, backup-dir threading, the withheld-convenience-keys note, idempotent re-run, and post-restart verify. Co-Authored-By: Claude <noreply@anthropic.com>
Adds a CI workflow with two jobs: an ubuntu job that lints scripts/*.sh and hooks/*.sh with shellcheck -x, runs the full bats suite, and enforces manifest.txt == git ls-files agents/ hooks/ rules/ skills/ (C3); and a macos job that runs the full bats suite under the system /bin/bash 3.2 + BSD userland (via a bats-core git checkout, not brew) to catch GNUisms. Adds tests/e2e.bats, the M4 happy-path integration test: check -> install -> merge-settings -> merge-claude-md -> check --post, run twice to prove the whole pipeline is byte-stable on repeat runs. The real `claude` binary is stripped from PATH so the pre-install check's expected failure (claude CLI not found) is deterministic on every machine, not just CI. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Validate every manifest.txt line (absolute path, or a ".." path component) before any writes occur, closing a PoC-verified traversal where a crafted manifest line wrote outside the adopter's config dir while still reporting CCS-STATUS:installed. Co-Authored-By: Claude <noreply@anthropic.com>
A byte-identical hook file whose +x bit was stripped fell into the unchanged path, which never chmods, so no run (not even --force) could repair it and check.sh --post failed forever. handle_unchanged now restores +x for the hooks category before reporting unchanged, still no backup/receipt churn for byte-identical content, and a second run stays a full no-op. Co-Authored-By: Claude <noreply@anthropic.com>
…rashing ccs_validate_object now walks every nested shape the merge jq touches (.hooks.<event> arrays, their elements, inner .hooks lists, and .command strings), so the documented 0/1/2 exit contract holds for nested shapes too instead of a raw jq exit-5 crash. Co-Authored-By: Claude <noreply@anthropic.com>
Plan-first is enforced by /design's plan mode and the post-design checkpoint (exit-plan-mode boundary), not by a global "plan" default. Co-Authored-By: Claude <noreply@anthropic.com>
npm's global prefix on the ubuntu-24.04 runner image is /usr/local, whose man dirs the runner user cannot create (EACCES), so the install step died before any gate ran. Mirrors the macos job's pinned v1.11.0 checkout. Co-Authored-By: Claude <noreply@anthropic.com>
…escape The prior traversal-regression tests were toothless: both PoC lines lacked a valid category prefix, so install.sh's category allowlist refused them before ccs_validate_manifest_rel ever ran; mutation testing (guard neutered to a no-op) proved both stayed green. Co-Authored-By: Claude <noreply@anthropic.com>
…fusals Two of the three malformed-marker shapes documented in the script's header comment (END-before-BEGIN, duplicate BEGIN/END pairs) had no test coverage; only the lone-BEGIN case was exercised. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
scripts/install.sh(diffs, backups, receipts, symlink refusal, path-containment guard) so adopters can install the shipped agents/hooks/rules/skills into~/.claudesafely and repeatably.merge-settings.sh(jq narrow merge ofhooks+permissions.allow, array-concat dedup, strict 0/1/2 exit contract incl. nested-shape validation) andmerge-claude-md.sh(marker-block CLAUDE.md merge with non-destructive fallbacks).scripts/lib.sh(shared guards), enforcedscripts/manifest.txt, and doctor-stylescripts/check.sh(--pre/--post)./setuprouter skill with two consent gates (dry-run previews pre-approved; applies require explicit approval) — never installed onto the adopter's machine.--severity=error, full bats suite, manifest-sync gate./setupwith a script-only fallback; WORKFLOW.md aligned with shipped behavior (loop caps, hook caveats,defaultMode).defaultModedoc correction.Test Plan
bats tests/*.bats— 84/84 green locally (install, both merges, check, hooks, e2e)shellcheck -x --severity=error scripts/*.sh hooks/*.sh— cleandiff <(git ls-files agents/ hooks/ rules/ skills/) scripts/manifest.txt— in syncccs_validate_manifest_relneutered, passes restoredtests/manual-setup-checklist.mdon a scratchCLAUDE_CONFIG_DIRKnown follow-ups (deliberately deferred — all minor, evidence-backed, none block merge)
merge-claude-md.shmarker matching is exact-line; a trailing space after a<!-- BEGIN ... -->marker is misclassified as malformed and refused (fails safe, no corruption).install.shrefuses a symlinked leaf destination but follows a pre-existing symlinked category directory (e.g.~/.claude/hooks → elsewhere), letting a valid install land outside the config dir. Arguably intended for stow/chezmoi layouts — decide, then either guard or document.install.shvalidates manifest path shape but not source existence; a shape-valid line with no matching source (e.g. CRLF working tree) aborts mid-install with a rawcp:error instead of an upfront refusal. Hardening: existence check in validation +.gitattributesforcing LF onscripts/manifest.txt.merge-settings.shsilently drops a foreign hook group whosehooksarray is empty (scaffolding in progress), despite the header's "foreign hooks preserved verbatim" promise — preserve already-empty groups or soften the claim.check.shmatches managed hook basenames as substrings of any command string, so a contrived config can earn a spurious "references all managed hooks" PASS — tighten to structural matching.Design Reference
Local design docs:
~/.claude/plans/adopter-setup-design-draft.md+adopter-setup-tasks.md(16 tasks, 6 tagged risk: high; 3-lens pre-PR panel + 4-lens post-PR subagent review, criticals/majors fixed on-branch).🤖 Generated with Claude Code