name: iso-init-repo description: Set up a repo with IsaiaScope governance defaults — GitHub repo creation, prod/test/dev branch structure with protection, prod-gate CI, commitlint, version-bump hook, and /deploy-cascade command. Use when the user runs /iso-init-repo or asks to set up repo governance.
iso-init-repo
Set up GitHub repo governance. Run from inside the target repo.
All templates live in templates/ next to this file.
Pre-flight
Run these checks before any step.
git
command -v git &>/dev/null \
|| { echo "✗ git not found. Install Xcode CLI tools: xcode-select --install"; exit 1; }
gh (GitHub CLI)
if ! command -v gh &>/dev/null; then
echo "⚠ gh not found — installing..."
brew install gh
command -v gh &>/dev/null \
|| { echo "✗ gh install failed. Run manually: brew install gh"; exit 1; }
echo "✓ gh installed"
fi
gh authentication
Authentication is interactive — cannot be automated. If not authenticated, stop and run the login command manually.
if ! gh auth status &>/dev/null; then
echo "⚠ gh not authenticated."
echo " Run: gh auth login"
echo " Then re-run /iso-init-repo"
exit 1
fi
Remote detection
git remote get-url origin 2>/dev/null || echo "no remote"
node / npx (Node repos — commitlint + version-bump)
Only relevant when package.json exists. Warns rather than fails — repo/branch steps still run.
if [ -f package.json ] && ! command -v npx &>/dev/null; then
echo "⚠ node/npx not found — Steps 5–6 (commitlint, version-bump) will be skipped."
echo " Install Node.js: https://nodejs.org or via nvm/fnm"
fi
All checks pass → proceed to Step 1.
Step 1 — GitHub repo
No remote → create
Ask user for repo name (default: current directory name) and visibility (private/public).
gh repo create <name> --private --source=. --remote=origin --push
Remote exists → verify
gh repo view
Confirm accessible, then continue.
Step 2 — Branch structure
Target: dev (default, daily work) ← test (staging) ← prod (release)
ORIGIN=$(git symbolic-ref --short HEAD) # current default, likely 'main'
# Create prod from current default
git checkout -b prod 2>/dev/null || git checkout prod
git push -u origin prod
# Create test and dev from prod
git checkout -b test prod && git push -u origin test
git checkout -b dev prod && git push -u origin dev
# Set dev as GitHub default branch
gh repo edit --default-branch dev
# Delete original default (main) if it was the starting point
if [ "$ORIGIN" = "main" ]; then
git push origin --delete main
git branch -d main
fi
Only delete main if prod was successfully created with full history.
Step 3 — Branch protection
Requires the branches from Step 2 to exist on origin.
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
# prod — PR required, no force push. ci-prod-gate enforces source=test.
gh api "repos/$REPO/branches/prod/protection" --method PUT \
--input - <<'EOF'
{
"required_status_checks": null,
"enforce_admins": false,
"required_pull_request_reviews": { "required_approving_review_count": 0 },
"restrictions": null
}
EOF
# test — PR required, no force push
gh api "repos/$REPO/branches/test/protection" --method PUT \
--input - <<'EOF'
{
"required_status_checks": null,
"enforce_admins": false,
"required_pull_request_reviews": { "required_approving_review_count": 0 },
"restrictions": null
}
EOF
# dev — PR required, no force push
gh api "repos/$REPO/branches/dev/protection" --method PUT \
--input - <<'EOF'
{
"required_status_checks": null,
"enforce_admins": false,
"required_pull_request_reviews": { "required_approving_review_count": 0 },
"restrictions": null
}
EOF
Branch protection can't restrict PR source branch — that's what ci-prod-gate.yml handles.
Step 4 — GitHub files
Read templates/ci-prod-gate.yml → write to .github/workflows/ci-prod-gate.yml.
git checkout dev
git add .github/
git commit -m "chore(repo): add prod-gate workflow"
git push origin dev
Step 5 — Commitlint
Skip if no package.json.
5a — Package manager
if [ -f pnpm-lock.yaml ]; then echo "pnpm"
elif [ -f yarn.lock ]; then echo "yarn"
elif [ -f bun.lockb ] || [ -f bun.lock ]; then echo "bun"
else echo "npm"; fi
5b — Init Husky (only if .husky/ missing)
[ -d .husky ] || npx husky init
5c — Install deps
Only install packages not already in package.json. Skip any already present.
pnpm add -D -w @commitlint/cli @commitlint/config-conventional # pnpm
yarn add -D -W @commitlint/cli @commitlint/config-conventional # yarn
bun add -d @commitlint/cli @commitlint/config-conventional # bun
npm install --save-dev @commitlint/cli @commitlint/config-conventional # npm
Also ensure "prepare": "husky" is in package.json scripts if missing.
5d — commit-msg hook
Read templates/commit-msg.sh → write to .husky/commit-msg, chmod +x.
Guard:
grep -q "commitlint" .husky/commit-msg 2>/dev/null \
&& echo "commit-msg: already configured, skipping" \
|| { cat templates/commit-msg.sh > .husky/commit-msg && chmod +x .husky/commit-msg; }
5e — commitlint.config.js
Check before writing:
[ -f commitlint.config.js ] \
&& echo "commitlint.config.js: already exists, skipping — review manually if needed" \
|| cp templates/commitlint.config.js commitlint.config.js
Before enabling scope-enum, audit all scopes in git history:
git log --oneline | sed -n 's/[^(]*(\([^)]*\)).*/\1/p' | sort -u
Only uncomment scope-enum if the repo has clean, consistent scopes. Populate from:
- scopes found above
- names from
ls apps/ packages/ - cross-cutting:
ci,deps,docs,repo
If history has free-text scopes — leave scope-enum commented. scope-empty alone is sufficient.
Commit:
git add .husky/ commitlint.config.js package.json
git commit -m "chore(repo): add commitlint"
git push origin dev
Step 6 — Version bump hook
Skip if no package.json.
Read templates/post-commit-version-bump.sh → write to .husky/post-commit-version-bump.sh, chmod +x.
If .husky/post-commit exists, append; otherwise create:
bash "$(dirname "$0")/post-commit-version-bump.sh"
Commit:
git add .husky/
git commit -m "chore(repo): add version-bump post-commit hook"
git push origin dev
Step 8 — Deploy cascade command
Read templates/deploy-cascade-command.md → write to .claude/commands/deploy-cascade.md.
This gives the repo a /deploy-cascade command. Uses caveman skill for all output. Starting point is auto-detected from current branch — runnable from any branch except prod.
mkdir -p .claude/commands
git add .claude/commands/deploy-cascade.md
git commit -m "chore(repo): add /deploy-cascade command"
git push origin dev
Step 9 — Summary
✓ GitHub repo created/configured
✓ Branches: dev (default) ← test ← prod
✓ Protection: PR required on dev, test, prod (no direct push)
✓ .github/workflows/ci-prod-gate.yml — prod accepts PRs from test only
✓ .husky/commit-msg + commitlint.config.js [or: skipped — no package.json]
✓ .husky/post-commit-version-bump.sh [or: skipped — no package.json]
✓ .claude/commands/deploy-cascade.md — /deploy-cascade command
Cascade: <any branch> → dev → test → prod
/deploy-cascade auto-detects starting point — run from any branch except prod. Uses caveman skill.
Prod-gate: PRs to prod from any branch other than test fail CI automatically.