git-setup
Use when a project has no git strategy, no branch protection, no conventional commits, or when the user says “set up git”, “git workflow”, “branch protection”, or “version bumping”. Also use when armadillo-sync detects missing git hygiene.
| Model | Source | Category |
|---|---|---|
| sonnet | core | Git |
Tools: Read, Glob, Grep, Bash, Write, Edit, AskUserQuestion
Context: fork
Overview
Section titled “Overview”Detects a project’s git health and installs armadillo’s git workflow: branch protection, conventional commits, squash merge policy, and version-bump automation.
Mandatory Announcement — FIRST OUTPUT before anything else:
┏━ 🔧 git-setup ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃ [one-line description of what git setup action] ┃┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛No exceptions. Box frame first, then work.
- Project has no
.githooks/directory - Project has no
git-workflow.mdrule - No conventional commit history detected
- User says “set up git”, “branch protection”, “version bump”
- Called automatically by armadillo-sync during setup
Full Reference
Git Setup
Section titled “Git Setup”Overview
Section titled “Overview”Detects a project’s git health and installs armadillo’s git workflow: branch protection, conventional commits, squash merge policy, and version-bump automation.
Mandatory Announcement — FIRST OUTPUT before anything else:
┏━ 🔧 git-setup ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃ [one-line description of what git setup action] ┃┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛No exceptions. Box frame first, then work.
When to Use
Section titled “When to Use”- Project has no
.githooks/directory - Project has no
git-workflow.mdrule - No conventional commit history detected
- User says “set up git”, “branch protection”, “version bump”
- Called automatically by armadillo-sync during setup
Step 1: Detection
Section titled “Step 1: Detection”Scan the project for git health signals:
# Check for branch protectionls .githooks/pre-commit 2>/dev/null
# Check for git-workflow rulels .claude/rules/git-workflow.md 2>/dev/null
# Check for conventional commits in recent historygit log -10 --oneline 2>/dev/null | grep -cE '^[a-f0-9]+ (feat|fix|chore|docs|test|refactor)(\([^)]*\))?:'
# Check for package.json with versionnode -e "const p=require('./package.json');console.log(p.version||'')" 2>/dev/null
# Check current branchgit branch --show-current 2>/dev/null
# Check if rerere is enabledgit config rerere.enabled 2>/dev/null || echo "not set"Detection Report
Section titled “Detection Report”Present findings:
## Git Health Check
| Component | Status ||-----------|--------|| Branch protection (.githooks/pre-commit) | ✗ Missing || Git workflow rule (.claude/rules/git-workflow.md) | ✗ Missing || Conventional commits (last 10) | 2/10 || Version in package.json | ✓ v1.2.3 || Current branch | main (⚠ working directly on main) || git rerere | ✗ Not enabled |Step 1.5: Auto-Enable delete_branch_on_merge
Section titled “Step 1.5: Auto-Enable delete_branch_on_merge”Auto-enable (no question — squash merges + worktree expect remote branches to disappear after merge):
SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*github.com[:/]||;s|\.git$||')DELETE_ON_MERGE=$(env -u GITHUB_TOKEN gh api "repos/${SLUG}" --jq '.delete_branch_on_merge' 2>/dev/null || echo "")| Result | Action |
|---|---|
true | Report ✓ delete_branch_on_merge already enabled |
false | Run env -u GITHUB_TOKEN gh api "repos/${SLUG}" --method PATCH --field delete_branch_on_merge=true 2>/dev/null, report ✓ delete_branch_on_merge enabled |
| empty / error | Skip silently — local-only repo or no GitHub access |
Add to the Detection Report table:
| delete_branch_on_merge | ✓ Enabled |Or if it was just enabled:
| delete_branch_on_merge | ✓ Enabled (just turned on) |Step 2: Branch Protection
Section titled “Step 2: Branch Protection”Ask the user:
▸ Install branch protection? This adds a pre-commit hook that blocks direct commits to main/master. You'll create branches for all feature work.
Escape hatch: ARMADILLO_ALLOW_MAIN=1 git commit ...Use AskUserQuestion:
- “Yes, install it” (Recommended) — install hook
- “No, skip” — continue without branch protection
If yes:
Section titled “If yes:”- Create
.githooks/pre-commit:
#!/bin/bash# Pre-commit hook: Block commits directly to main/master# Escape hatch: ARMADILLO_ALLOW_MAIN=1 git commit ...
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then if [ "${ARMADILLO_ALLOW_MAIN:-0}" = "1" ]; then exit 0 fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✗ Direct commits to $BRANCH are blocked." echo "" echo " Create a branch first:" echo " git checkout -b feat/your-feature" echo "" echo " Emergency override:" echo " ARMADILLO_ALLOW_MAIN=1 git commit ..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" exit 1fi
exit 0-
Make executable:
chmod +x .githooks/pre-commit -
Install to
.git/hooks/:
cp .githooks/pre-commit .git/hooks/pre-commitchmod +x .git/hooks/pre-commit- If
package.jsonexists, add a postinstall script to auto-install hooks:
Check if scripts.postinstall already exists. If not:
node -e "const pkg = require('./package.json');if (!pkg.scripts) pkg.scripts = {};if (!pkg.scripts.postinstall) { pkg.scripts.postinstall = 'cp .githooks/* .git/hooks/ 2>/dev/null && chmod +x .git/hooks/* 2>/dev/null || true'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\\n');}"Step 2.5: Enable Git Rerere
Section titled “Step 2.5: Enable Git Rerere”Auto-enable (no question — this is a net improvement with zero downside):
git config rerere.enabled trueWhat this does: When you resolve a merge conflict, git remembers the resolution. Next time the same conflict appears, it’s auto-resolved. Zero overhead, saves time on rebases.
Present:
✓ Enabled git rerere (reuse recorded resolution) ↳ Merge conflicts you resolve once are auto-resolved in futureStep 3: Git Workflow Rule
Section titled “Step 3: Git Workflow Rule”Auto-install .claude/rules/git-workflow.md (no question — this is core convention):
# Git Workflow
## Branch-First Policy
**Never commit directly to `main`.** All work happens on a branch, gets reviewed via PR, and merges via squash.
### Branch Naming
| Prefix | When ||--------|------|| `feat/` | New feature or capability || `fix/` | Bug fix || `chore/` | Tooling, deps, config || `docs/` | Documentation only || `test/` | Tests only || `refactor/` | Code restructure |
Format: `<type>/<short-description>` — e.g. `feat/auth-refresh`, `fix/null-checkout`
## Commit Conventions
- Frequent, atomic commits- Conventional commit messages (feat, fix, refactor, test, docs, chore)- TDD order: test commit before (or with) implementation commit
## Merge Strategy
Always squash merge via PR — one commit per feature on main, clean linear history.
## Integration Branches
When 3+ branches touch overlapping files, create an integration branch:- Naming: `integrate/<description>`- Merge all feature branches into integration first- Then squash-merge integration to main- Developer approval required before creationCreate the directory if needed: mkdir -p .claude/rules
REST-first enforcement: The git-workflow.md rule mandates using gh api REST calls instead of gh pr create/merge/view (GraphQL). See .claude/rules/git-workflow.md for the full REST API patterns and rationale.
Step 4: Version-Bump Automation
Section titled “Step 4: Version-Bump Automation”If package.json exists with a version field, offer version-bump automation.
▸ Set up automatic version bumping? Conventional commits drive semver: feat: → minor, fix: → patch, breaking (!) → major
Two options: 1. Local pre-push hook (runs before each push) 2. GitHub Actions workflow (runs in CI)Use AskUserQuestion:
- “Local pre-push hook” (Recommended) — runs locally before push
- “GitHub Actions workflow” — runs in CI
- “No version bumping” — skip
Option A: Local pre-push hook
Section titled “Option A: Local pre-push hook”Create .githooks/pre-push:
#!/bin/bash# Pre-push hook: auto-bump version from conventional commits# Creates a version tag based on commit types since last tag
set -e
# Get latest tagLATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")LATEST_VERSION=${LATEST_TAG#v}
# Get commits since last tagCOMMITS=$(git log "${LATEST_TAG}..HEAD" --oneline 2>/dev/null || git log --oneline)
if [ -z "$COMMITS" ]; then exit 0fi
# Detect change typeHAS_BREAKING=$(echo "$COMMITS" | grep -cE '^[a-f0-9]+ [a-z]+!:' || true)HAS_FEAT=$(echo "$COMMITS" | grep -cE '^[a-f0-9]+ feat(\([^)]*\))?:' || true)
IFS='.' read -r MAJOR MINOR PATCH <<< "$LATEST_VERSION"
if [ "$HAS_BREAKING" -gt 0 ]; then MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0elif [ "$HAS_FEAT" -gt 0 ]; then MINOR=$((MINOR + 1)); PATCH=0else PATCH=$((PATCH + 1))fi
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
# Update package.json versionnode -e "const pkg = JSON.parse(require('fs').readFileSync('package.json', 'utf8'));pkg.version = '${NEW_VERSION}';require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');"
# Commit and taggit add package.jsongit commit -m "chore: release ${NEW_VERSION}" --no-verifygit tag "v${NEW_VERSION}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"echo "✓ Bumped to v${NEW_VERSION}"echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"Option B: GitHub Actions workflow
Section titled “Option B: GitHub Actions workflow”Create .github/workflows/version-bump.yml:
name: Version Bump
on: push: branches: [main]
jobs: bump: runs-on: ubuntu-latest permissions: contents: write
steps: - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@v4 with: node-version: '20'
- name: Bump version from commits run: | LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") COMMITS=$(git log "${LATEST_TAG}..HEAD" --oneline)
if [ -z "$COMMITS" ]; then exit 0; fi
HAS_BREAKING=$(echo "$COMMITS" | grep -cE '^[a-f0-9]+ [a-z]+!:' || true) HAS_FEAT=$(echo "$COMMITS" | grep -cE '^[a-f0-9]+ feat(\([^)]*\))?:' || true)
IFS='.' read -r MAJOR MINOR PATCH <<< "${LATEST_TAG#v}"
if [ "$HAS_BREAKING" -gt 0 ]; then MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 elif [ "$HAS_FEAT" -gt 0 ]; then MINOR=$((MINOR + 1)); PATCH=0 else PATCH=$((PATCH + 1)) fi
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
node -e " const pkg = JSON.parse(require('fs').readFileSync('package.json', 'utf8')); pkg.version = '${NEW_VERSION}'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); "
git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add package.json git commit -m "chore: release ${NEW_VERSION} [skip ci]" git tag "v${NEW_VERSION}" git push && git push --tagsStep 5: Summary
Section titled “Step 5: Summary”Present what was configured, ask to commit.
Key Rules
Section titled “Key Rules”- Always ask before installing branch protection — never auto-install hooks that block user actions
- git-workflow.md is auto-installed — core convention, no question needed
- Version-bump is opt-in — not every project wants automated versioning
- Never overwrite existing hooks — if
.githooks/pre-commitexists, read it first and merge or ask - postinstall only if package.json exists — non-Node projects skip this