#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── # bootstrap-repo.sh · QRefAI AI Coding Field Guide, Part 4 (Q4.1) # # Drops the standard harness files into an EXISTING repo: .github/ (instructions, # agents, workflows), AGENTS.md, CLAUDE.md, and the branch-protection ruleset. # This is the "make a repo harness-ready in one command" script the guide promises. # # It pulls the golden files from your platform repo's template scaffold so every # repo inherits ONE source of truth — fix a template once, re-run, every repo updates. # # USAGE: # ./bootstrap-repo.sh [target-repo-dir] # defaults to current directory # # ENV: # TEMPLATE_SRC path or git URL of the template scaffold # (default: the dev-platform repo's scaffold/ directory) # APPLY_RULESET "1" to push the ruleset via gh API (needs gh + admin); else # the ruleset JSON is just copied in for manual import. # # SAFE BY DEFAULT: never overwrites an existing file unless OVERWRITE=1. # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail TARGET="${1:-.}" TEMPLATE_SRC="${TEMPLATE_SRC:-$HOME/dev-platform/scaffold}" OVERWRITE="${OVERWRITE:-0}" APPLY_RULESET="${APPLY_RULESET:-0}" say() { printf '\033[1;36m▸ %s\033[0m\n' "$*"; } warn() { printf '\033[1;33m! %s\033[0m\n' "$*" >&2; } die() { printf '\033[1;31m✗ %s\033[0m\n' "$*" >&2; exit 1; } [ -d "$TARGET" ] || die "target dir not found: $TARGET" [ -d "$TARGET/.git" ] || warn "target is not a git repo root: $TARGET" # Resolve the template source: support a local dir OR a git URL. WORKSRC="$TEMPLATE_SRC" TMP="" if printf '%s' "$TEMPLATE_SRC" | grep -Eq '^(https?|git@)'; then TMP="$(mktemp -d)" say "Cloning template scaffold from $TEMPLATE_SRC" git clone --depth 1 "$TEMPLATE_SRC" "$TMP" >/dev/null WORKSRC="$TMP/scaffold" fi [ -d "$WORKSRC" ] || die "template scaffold not found at: $WORKSRC" # Copy one file, respecting OVERWRITE. copy_file() { local rel="$1" src="$WORKSRC/$1" dst="$TARGET/$1" [ -f "$src" ] || { warn "template missing: $rel (skipped)"; return; } if [ -f "$dst" ] && [ "$OVERWRITE" != "1" ]; then warn "exists, skipping: $rel (set OVERWRITE=1 to replace)" return fi mkdir -p "$(dirname "$dst")" cp "$src" "$dst" say "wrote $rel" } say "Bootstrapping harness into: $TARGET" # 1. Instruction memory (cross-vendor + per-vendor wrappers) copy_file "AGENTS.md" copy_file "CLAUDE.md" # 2. Copilot surface copy_file ".github/copilot-instructions.md" copy_file ".github/instructions/payments.instructions.md" copy_file ".github/agents/security-scout.agent.md" # 3. Agentic workflows (outer loop) copy_file ".github/workflows/security-guard.md" copy_file ".github/workflows/agents-md-maintainer.md" # 4. Claude surface copy_file ".claude/settings.json" # 5. Branch-protection ruleset copy_file ".github/rulesets/main-protection.json" # Optionally apply the ruleset through the GitHub API. if [ "$APPLY_RULESET" = "1" ]; then command -v gh >/dev/null 2>&1 || die "APPLY_RULESET=1 needs the gh CLI" say "Applying branch-protection ruleset via gh API" gh api -X POST "repos/{owner}/{repo}/rulesets" \ --input "$TARGET/.github/rulesets/main-protection.json" >/dev/null \ && say "ruleset applied" \ || warn "ruleset apply failed — import it manually from .github/rulesets/" else warn "Ruleset copied but NOT applied. Import via Settings → Rules → Rulesets," warn "or re-run with APPLY_RULESET=1 (requires gh + admin on the repo)." fi [ -n "$TMP" ] && rm -rf "$TMP" say "Done. Review the dropped files, fill in s, and commit on a branch." echo echo "Next:" echo " • Replace s in AGENTS.md / CLAUDE.md / copilot-instructions.md" echo " • Compile any gh-aw workflows: gh aw compile --strict" echo " • Open a PR so the changes get the review the ruleset now requires"