toolkit/packages/skills/git-history-surgery/SKILL.md
Safely split, reorder, or rewrite already-pushed commits using a throwaway worktree. Isolates the surgery from any existing checkout, verifies byte-identical tree before force-pushing, and uses --force-with-lease to refuse silent clobbers from concurrent work. Use when asked to split a pushed commit into atomic pieces, fix a bulked commit after the fact, or reshape recent history on a shared branch.
npx skillsauth add stevengonsalvez/agents-in-a-box git-history-surgeryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Trigger on any of:
Doing git reset --soft HEAD~1 in an existing worktree is risky:
fix/usage-async-load in a past session), a reset rewrites the wrong branch.The safe pattern: fresh detached worktree at origin/<branch>, do surgery, push, discard.
# 1. Create throwaway worktree at the exact commit you want to rewrite
TMPWT=$(mktemp -d -t history-split-XXXXXX)
git fetch origin
git worktree add --detach "$TMPWT" origin/<branch>
cd "$TMPWT"
# 2. Verify you're where you expect
git log -3 --oneline
# 3. Undo the commit(s) keeping working-tree content
git reset --soft HEAD~1 # or HEAD~N for multiple
git reset # unstage so you can stage atomic chunks
# 4. Reset the file(s) to the *pre-change* base, then rebuild with Edits
git checkout HEAD -- <path>
# 5. Apply atomic change → stage → commit → repeat (N times)
# Each commit = one visual / structural / behavioural concern.
# Use Edit tool for text files to avoid whitespace drift.
# 6. VERIFY byte-identical rebuild before force-pushing
git diff origin/<branch>..HEAD
# ^^ MUST be empty. If not, you've lost content. STOP and investigate.
# 7. Force-with-lease: refuses if someone pushed since your fetch
git push --force-with-lease=<branch>:<EXPECTED_SHA> origin HEAD:<branch>
# 8. Cleanup
cd ..
git worktree remove --force "$TMPWT"
--force-with-lease, never --force on shared branches. Pass the expected remote SHA so it refuses if concurrent pushes landed.git diff origin/<branch>..HEAD is empty before pushing. A non-empty diff means you lost or added content vs. the original bulked commit — stop and rebuild.Keep them honest about what each commit actually does:
docs(readme): replace broken demo.gif with live dashboard hero
docs(readme): add usage analytics hero below the dashboard
docs(readme): rename section to "Terminal UI + CLI"
docs(readme): expand feature highlights with multi-provider + analytics
docs(readme): replace broken screenshot block with 6-panel showcase
docs(readme): add dedicated CLI section with command overview
Each message must stand alone — don't write "part 2 of README split" style.
If git diff origin/<branch>..HEAD is non-empty:
git log to see what's different.cat <<EOF instead of Edit.If --force-with-lease rejects:
git fetch origin, rebase your atomic commits onto the new tip, re-verify diff, push again with the new lease SHA.Shared branches with force-push capability are one shell-slip away from catastrophic history loss. The throwaway-worktree + lease + verify-diff pattern makes the operation idempotent: if any step fails, nothing bad has happened yet. Only the final push is destructive, and the lease gates that.
Scenario: A PR was already squash-merged but it should have been a merge commit (e.g., the project prefers to preserve atomic per-concern commits in main's history). The squash commit sits on the default branch.
Trick: the squash commit's tree IS the merged result. Reuse it. Build a new commit object that points to the same tree but has two parents (pre-squash main tip + feature branch tip). Force-push to swap. No actual re-merge happens, so there's no chance of conflicts and no working-tree changes.
# 1. Identify SHAs
SQUASH=$(git rev-parse origin/main) # current main tip (the squash)
PRE_SQUASH=$(git rev-parse $SQUASH^) # main before the squash
FEATURE=$(git rev-parse <feature-branch>) # feature branch tip (must still exist locally or on remote)
TREE=$(git rev-parse $SQUASH^{tree}) # merged tree (already correct)
# 2. Build the merge commit object via plumbing — no working tree touched
MERGE=$(git commit-tree $TREE -p $PRE_SQUASH -p $FEATURE \
-m "Merge pull request #N from <branch>" \
-m "<body>")
# 3. Sanity check — trees MUST match. If they don't, abort.
[ "$(git rev-parse $SQUASH^{tree})" = "$(git rev-parse $MERGE^{tree})" ] \
|| { echo "TREE MISMATCH — abort"; exit 1; }
# 4. Force-push with lease scoped to the squash SHA
# Refuses if anyone else has moved main since the squash.
git push --force-with-lease=main:$SQUASH origin $MERGE:refs/heads/main
# 5. Verify
git fetch origin main
git log --pretty=format:"%h %p %s" -1 origin/main # should show two parents
Why this works: git commit-tree is plumbing that creates a commit object pointing at any tree with any parents, no merge driver involved. Since the squash already produced the correct merged tree, you're just relabeling that tree as a merge commit with the right parentage. The byte-identical-tree assertion is the safety check that proves you haven't lost or added content.
When to use:
When NOT to use:
origin/main^ is no longer the pre-squash SHA). The lease will reject anyway, but the operation is no longer simply reversible — you'd need to rebuild the chain.Branch protection: Default-branch force-push usually requires admin override. Coordinate or use gh api with admin token if needed. Per feedback_force_push_docs, in this repo history-surgery on main is acceptable when the trees match and the lease holds.
documentation
Report reflect drain spend over a time window — tokens split by cached (cache_read), uncached writes (cache_creation), and io (input+output), with a $ estimate, grouped by day / outcome / model / transcript. Reads the drainer's cost log and surfaces outlier runs and cache-reuse health (the 41.5M-token failure mode = low cache reuse + high cache writes). Use to answer "what is reflection costing me" for the last day / week.
development
Show fleet status — every claude session running on the host, merged across ainb + claude-peers broker + background jobs. Use when you need to enumerate sessions before composing an action, see which sessions have a peer registered (broker-routable) vs tmux-only, check the `summary` of each session, or pipe the list into jq for filtering. Default output: text table. Pass --format json for LLM consumption.
testing
Ordered multi-step prompts to fleet targets, ack-gated between steps via JSONL assistant-turn-end detection. Use for cycles like disconnect→reconnect→verify, or any flow where step N+1 requires step N to have completed first. The skill BLOCKS until each target's transcript shows the next assistant turn finishing OR per-step timeout fires (default 300s).
development
Center control panel — enumerate every claude session that is blocked waiting on something: a user answer (AskUserQuestion fired), an API error retry, an idle assistant turn-end with no follow-up, or an explicit WAITING: marker. Returns rich JSON with signal kind + context per session. Use this when you've stepped away from the fleet and want one place to see everything that wants your attention and answer it.