diff --git a/AGENTS.md b/AGENTS.md index 65c083cf..0845ba6f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ This is a **Claude Code plugins repository** containing production-ready hooks f | **content-guards** | Pre/PostToolUse | Bash, Write, Edit | Token limits, markdown/README validation, webfetch guard, issue/PR backlog limits, public-repo leakage guard | | **git-guards** | PreToolUse | Bash, Edit, Write, NotebookEdit | Blocks dangerous git/gh commands and file edits on main branch | | **git-workflows** | Command/Skill | `/sync-main`, `/git-flow-next`, `/troubleshoot-rebase`, `/troubleshoot-precommit`, `/troubleshoot-worktree`, `/pre-commit-architecture` | Local git sync, branching model, and rebase/pre-commit/worktree troubleshooting | -| **github-workflows** | Command/Skill | `/ship`, `/finalize-pr`, `/refresh-repo` (incl. `--sweep` and `--prune-stale` modes), `/rebase-pr`, `/squash-merge-pr`, `/pr-sweep`, `/resolve-pr-threads`, `/gh-cli-patterns`, `/shape-issues`, `/trigger-ai-reviews` | GitHub PR/issue management workflows, cross-repo workspace sweep, stale-branch pruning, and risk-ranked open-PR sweep-to-zero | +| **github-workflows** | Command/Skill | `/ship`, `/finalize-pr`, `/refresh-repo`, `/prune-branches` (incl. `--sweep` and `--prune-stale` modes), `/rebase-pr`, `/squash-merge-pr`, `/pr-sweep`, `/resolve-pr-threads`, `/gh-cli-patterns`, `/shape-issues`, `/trigger-ai-reviews` | GitHub PR/issue management workflows, cross-repo workspace sweep, stale-branch pruning, and risk-ranked open-PR sweep-to-zero | | **infra-orchestration** | Skill | `/orchestrate-infra`, `/sync-inventory`, `/test-e2e` | Cross-repo infrastructure orchestration for Terraform and Ansible | | **code-standards** | Skill | `/code-quality-standards`, `/review-standards` | Code quality standards, documentation formatting, testing philosophy, and review guidelines | | **git-standards** | Skill | `/git-workflow-standards`, `/pr-standards` | Git workflow standards, branch hygiene, PR creation guards, workaround vs fix classification, and issue linking | diff --git a/README.md b/README.md index f2b90c95..ff0aea8d 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,8 @@ invocation. Skill-based plugins provide slash commands: /ship # Full automation: commit, push, PR, finalize /finalize-pr # Drive PR to merge-ready state /squash-merge-pr # Validate and squash merge -/refresh-repo # Sync main, check PRs, cleanup worktrees +/refresh-repo # Sync main, check PRs +/prune-branches # Delete dead branches and worktrees /wrap-up # Session-completion verdict + forward artifact /resolve-codeql # Fix CodeQL security alerts /delegate-to-ai # Route tasks to available AI models diff --git a/ai-cli-harness-better-practices/ARCHITECTURE.md b/ai-cli-harness-better-practices/ARCHITECTURE.md index 7381f187..73aacb0a 100644 --- a/ai-cli-harness-better-practices/ARCHITECTURE.md +++ b/ai-cli-harness-better-practices/ARCHITECTURE.md @@ -21,8 +21,8 @@ flowchart TD subgraph optional["Optional git enrichment"] refresh["/refresh-repo\n(github-workflows)"]:::external + prune["/prune-branches\n(github-workflows)"]:::external ghcli["/gh-cli-patterns\n(github-workflows)"]:::external - cleangone["/clean_gone\n(commit-commands)"]:::external end subgraph referred["Referred out, never rebuilt"] @@ -36,7 +36,7 @@ flowchart TD wrapup --> handoff wrapup --> retro wrapup -.->|"if git repo"| refresh - wrapup -.->|"if git repo"| cleangone + wrapup -.->|"if git repo"| prune status --> handoff status -.->|"if git repo"| ghcli diff --git a/ai-cli-harness-better-practices/README.md b/ai-cli-harness-better-practices/README.md index 7ff438f5..2d2f8f50 100644 --- a/ai-cli-harness-better-practices/README.md +++ b/ai-cli-harness-better-practices/README.md @@ -65,8 +65,8 @@ This plugin deliberately does not reimplement work that already exists: | Prose quality | `elements-of-style:writing-clearly-and-concisely` | The git-side actions the continuity skills delegate to live in their own -plugins: `github-workflows:refresh-repo`, `github-workflows:gh-cli-patterns`, -and `commit-commands:clean_gone`. +plugin: `github-workflows:refresh-repo`, `github-workflows:prune-branches`, +and `github-workflows:gh-cli-patterns`. ## Installation diff --git a/ai-cli-harness-better-practices/skills/session-status/SKILL.md b/ai-cli-harness-better-practices/skills/session-status/SKILL.md index 63e51526..2d4cb3bf 100644 --- a/ai-cli-harness-better-practices/skills/session-status/SKILL.md +++ b/ai-cli-harness-better-practices/skills/session-status/SKILL.md @@ -296,7 +296,7 @@ to `handoff` and `wrap-up`'s resume blocks, not this dashboard. Step 0 and handles conditional repository cleanup. - **resume** (this plugin) — cold pickup; reuses this skill's derivation. - **replan** (this plugin) — rebuilds a plan this skill shows has drifted. -- **refresh-repo** (github-workflows) — Checks PR merge-readiness, syncs local - main, and cleans worktrees. +- **refresh-repo** (github-workflows) — Checks PR merge-readiness and syncs local main. +- **prune-branches** (github-workflows) — Deletes stale branches and worktrees. - **retrospecting** (claude-retrospective) — Generates detailed retrospectives based on session logs and git diffs. diff --git a/ai-cli-harness-better-practices/skills/wrap-up/SKILL.md b/ai-cli-harness-better-practices/skills/wrap-up/SKILL.md index 516c8315..4ec4ad1d 100644 --- a/ai-cli-harness-better-practices/skills/wrap-up/SKILL.md +++ b/ai-cli-harness-better-practices/skills/wrap-up/SKILL.md @@ -84,16 +84,16 @@ skipped" in the summary, and run A2 and A4 as normal. Steps A2 and A4 are the part of a wrap-up that always applies. In a repository, run Steps A1 and A2 **in parallel** (they are independent). -Step A3 starts as soon as Step A1 completes (depends on its remote prune). Step -A4 runs after all prior steps finish. Provide a summary of actions taken. +Step A3 runs after both finish. Provide a summary of actions taken. -### A1. Refresh Repository (repository only) +### A1. Refresh and Prune Repository (repository only) -Invoke `/refresh-repo` to: +Invoke `/refresh-repo` to sync, then `/prune-branches` to delete anything +left with nothing useful: - Check merge-readiness of any remaining open PRs - Sync the local default branch with remote (main on trunk repos, develop on git-flow repos) -- Clean up stale worktrees (merged PRs, `[gone]` remote branches) +- Delete stale branches and worktrees — local, remote, or both - Report repository state ### A2. Quick Retrospective @@ -111,17 +111,7 @@ reporting a full retrospective. **Requires**: `claude-retrospective` plugin (external). If not installed, skip this step and note it was skipped. -### A3. Clean Gone Branches (repository only) - -Invoke `/clean_gone` to remove any local branches whose remote tracking branch has been deleted: - -- Identify branches marked `[gone]` -- Remove associated worktrees -- Delete the local branches - -**Requires**: `commit-commands` plugin (external). If not installed, skip this step and note it was skipped. - -### A4. Follow-Up Session Prompt +### A3. Follow-Up Session Prompt If `/session-status` in Step 0 surfaced follow-up work, invoke the `/handoff` skill to emit the next-session artifact. Pass it the "Recommended Prompt for Next @@ -159,7 +149,7 @@ Wrap-Up Summary ## Path B — Resume blocks (plan incomplete) -Skip Path A entirely. Skipping is intentional: `/refresh-repo` prunes stale +Skip Path A entirely. Skipping is intentional: `/prune-branches` deletes stale worktrees, which would delete the in-flight worktree the user needs to resume in. @@ -269,15 +259,15 @@ Sequence: Closes the gap where `gh pr close --delete-branch` removes only the remote branch and leaves the local branch + worktree behind. Reuses the worktree-removal command shape from `/troubleshoot-worktree` and aligns with -`/clean_gone`'s post-removal state. +`/prune-branches`'s post-removal state. ## Related Skills - **handoff** (this plugin) — builds the two-part next-session artifact (goal - statement under 4000 chars + full prompt) used by Path A Step A4 and Path B Step B2 -- **refresh-repo** (github-workflows) — PR readiness check + repo sync + - worktree cleanup (Path A Step A1 dependency); also provides `--sweep` and - `--prune-stale` modes + statement under 4000 chars + full prompt) used by Path A Step A3 and Path B Step B2 +- **refresh-repo** (github-workflows) — PR readiness check + default-branch sync (Path A Step A1) +- **prune-branches** (github-workflows) — stale branch and worktree cleanup + (Path A Step A1); also provides `--sweep` and `--prune-stale` modes - **shape-issues** (github-workflows) — Shape and create well-structured GitHub issues for code/repo defects and features; not for incidents (Zammad, see session-status Step 4 and pr-standards) diff --git a/git-standards/ARCHITECTURE.md b/git-standards/ARCHITECTURE.md index 0383e840..7b229822 100644 --- a/git-standards/ARCHITECTURE.md +++ b/git-standards/ARCHITECTURE.md @@ -19,6 +19,7 @@ flowchart LR subgraph consumers["Skills that load these standards"] SM["/sync-main\n(git-workflows)"]:::external RR["/refresh-repo\n(git-workflows)"]:::external + PB["/prune-branches\n(git-workflows)"]:::external RP["/rebase-pr\n(git-workflows)"]:::external SHIP["/ship\n(github-workflows)"]:::external FPR["/finalize-pr\n(github-workflows)"]:::external @@ -26,7 +27,7 @@ flowchart LR end GWS -.->|"informs branch ops"| SM - GWS -.->|"informs cleanup"| RR + GWS -.->|"informs cleanup"| PB GWS -.->|"informs merge"| RP PS -.->|"informs PR creation"| SHIP PS -.->|"informs PR finalization"| FPR diff --git a/git-standards/skills/git-workflow-standards/SKILL.md b/git-standards/skills/git-workflow-standards/SKILL.md index 432762af..c42b6078 100644 --- a/git-standards/skills/git-workflow-standards/SKILL.md +++ b/git-standards/skills/git-workflow-standards/SKILL.md @@ -18,12 +18,15 @@ Create a worktree for the change; remove it when the work is done. Every branch with commits MUST have an associated PR. Orphaned branches must get a PR or be deleted. -**Stale worktree**: A branch with no open PR, no uncommitted changes, and either a merged PR -whose `headRefOid` matches local `HEAD`, or a deleted remote (`[gone]`) with no commits ahead -of the default branch (`git log origin/..HEAD --oneline` is empty). Branches with open -PRs, local-only branches without merged PRs, local commits beyond the merged PR head, and -worktrees with uncommitted changes are NEVER stale. Use `git worktree remove` (never `--force`) -— Git natively blocks removal of dirty worktrees. +**Stale branch** (worktree-paired, bare local, or remote-only — see +`prune-branches`, github-workflows, for the full procedure): a non-default +branch, checked out nowhere, unreferenced by any open PR (`--head` or +`--base`), with no uncommitted changes (when a worktree exists), and either +a merged PR whose `headRefOid` matches the branch tip, or zero commits ahead +of the default branch. Use `git worktree remove` (never `--force`) for +worktree-paired branches — Git blocks removal of dirty worktrees — and +`git push origin --delete` for a live remote ref, only after +`prune-branches`'s pre-push ancestry re-check. ## Branch Hygiene @@ -53,7 +56,7 @@ Sync workflow (replace `` with the repo's actual default branch — see `gh-cli-patterns`, github-workflows): ```bash -git fetch origin && git pull origin # in the worktree +git pull origin # in the worktree git merge origin/ --no-edit # in the feature worktree ``` @@ -89,7 +92,8 @@ contradictions, or security-sensitive code. ## Related Skills - **sync-main** (git-workflows) — Syncs the repo's default branch and merges into current or all PR branches -- **refresh-repo** (github-workflows) — Full repo sync including PR status and worktree cleanup +- **refresh-repo** (github-workflows) — PR status and default-branch sync +- **prune-branches** (github-workflows) — Stale branch and worktree cleanup - **gh-cli-patterns** (github-workflows) — Canonical default-branch detection (trunk vs git-flow) - **pr-standards** (git-standards) — PR creation guards, issue linking, and review standards - **git-flow-next** (git-workflows) — Dedicated git-flow-next guide, worktree setup, and promotion steps diff --git a/git-workflows/skills/sync-main/SKILL.md b/git-workflows/skills/sync-main/SKILL.md index 7626b9b1..a9961d91 100644 --- a/git-workflows/skills/sync-main/SKILL.md +++ b/git-workflows/skills/sync-main/SKILL.md @@ -101,7 +101,8 @@ Read files, understand both versions, combine intelligently, stage resolved file ## Related Skills -- **refresh-repo** (github-workflows) — Full repo sync including PR status and worktree cleanup +- **refresh-repo** (github-workflows) — PR status and default-branch sync +- **prune-branches** (github-workflows) — Stale branch and worktree cleanup - **rebase-pr** (github-workflows) — Rebase-merge workflow that builds on a synced base branch - **promote-release** (github-workflows) — Moves develop to main on git-flow repos; the fast-forward step here only reads main, never pushes it - **gh-cli-patterns** (github-workflows) — Canonical default-branch detection (trunk vs git-flow) diff --git a/git-workflows/skills/troubleshoot-worktree/SKILL.md b/git-workflows/skills/troubleshoot-worktree/SKILL.md index cf277235..8be7c2b9 100644 --- a/git-workflows/skills/troubleshoot-worktree/SKILL.md +++ b/git-workflows/skills/troubleshoot-worktree/SKILL.md @@ -68,4 +68,5 @@ echo "/" >> .gitignore - **troubleshoot-rebase** (git-workflows) — Diagnose and recover from git rebase failures - **troubleshoot-precommit** (git-workflows) — Troubleshoot pre-commit hook failures -- **refresh-repo** (github-workflows) — Full repo sync including worktree cleanup +- **refresh-repo** (github-workflows) — Default-branch sync +- **prune-branches** (github-workflows) — Worktree and branch cleanup diff --git a/github-workflows/.claude-plugin/plugin.json b/github-workflows/.claude-plugin/plugin.json index 3f321ab1..6b117b72 100644 --- a/github-workflows/.claude-plugin/plugin.json +++ b/github-workflows/.claude-plugin/plugin.json @@ -24,6 +24,7 @@ "./skills/rebase-pr", "./skills/promote-release", "./skills/refresh-repo", + "./skills/prune-branches", "./skills/gh-cli-patterns", "./skills/shape-issues", "./skills/resolve-pr-threads", diff --git a/github-workflows/ARCHITECTURE.md b/github-workflows/ARCHITECTURE.md index 33b4f148..c7dc23ff 100644 --- a/github-workflows/ARCHITECTURE.md +++ b/github-workflows/ARCHITECTURE.md @@ -24,9 +24,10 @@ graph LR ## 1. Skill Dependency Map -All skills and their cross-plugin dependencies. `/refresh-repo`, `/rebase-pr`, -`/squash-merge-pr`, `/promote-release`, and `/gh-cli-patterns` are local to this -plugin (no cross-plugin hop) — `/squash-merge-pr`, `/rebase-pr`, and +All skills and their cross-plugin dependencies. `/refresh-repo`, +`/prune-branches`, `/rebase-pr`, `/squash-merge-pr`, `/promote-release`, and +`/gh-cli-patterns` are local to this plugin (no cross-plugin hop) — +`/squash-merge-pr`, `/rebase-pr`, and `/promote-release` all consume the canonical PR-readiness gate and the default-branch detection from `/gh-cli-patterns` directly. On a git-flow repo, `/squash-merge-pr` and `/rebase-pr` both refuse a `main`-targeting PR and point @@ -41,6 +42,7 @@ flowchart TD RBP["/rebase-pr"]:::ai PRR["/promote-release"]:::ai RFR["/refresh-repo"]:::ai + PBR["/prune-branches"]:::ai GCP["/gh-cli-patterns"]:::ai TAR["/trigger-ai-reviews"]:::ai SI["/shape-issues"]:::ai @@ -152,7 +154,7 @@ flowchart TD direction TB W1["/refresh-repo\n(this plugin)"]:::ai W2["/retrospecting quick\n(claude-retrospective, external)"]:::external - W3["/clean_gone\n(commit-commands, external)"]:::external + W3["/prune-branches\n(this plugin)"]:::ai W4["Summary report"]:::ai W1 --> W3 --> W4 diff --git a/github-workflows/README.md b/github-workflows/README.md index 79b9af4c..441c2ee4 100644 --- a/github-workflows/README.md +++ b/github-workflows/README.md @@ -8,7 +8,8 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for integration diagrams and the master s - **`/ship`** - Full automation: commit, push, create PR(s), and auto-finalize in one command. - **`/finalize-pr`** - Finalize PRs for merge: single PR, all repo PRs (`all`), or all org PRs (`org`). Includes bot-authored PRs in all modes. -- **`/refresh-repo`** - Check PR merge-readiness, sync local repo, and cleanup stale worktrees +- **`/refresh-repo`** - Check PR merge-readiness and sync the local repo with its default branch +- **`/prune-branches`** - Delete branches and worktrees with nothing useful, local and remote; optional multi-repo sweep - **`/rebase-pr`** - Merge a PR using local git rebase + signed commits + a direct push to its base branch (main on trunk repos, develop on git-flow repos) - **`/squash-merge-pr`** - Validate PR readiness and squash merge into its base branch (errors if not ready); refuses on git-flow repos when the base is main - **`/promote-release`** - Merge-commit a develop → main promotion PR on a git-flow repo; release-please takes over from there @@ -32,7 +33,8 @@ claude plugins add jacobpevans-cc-plugins/github-workflows /finalize-pr 42 # Finalize specific PR by number /finalize-pr all # Finalize all open PRs in repo (including bots) /finalize-pr org # Finalize all open PRs across org (including bots) -/refresh-repo # PR check, sync, worktree cleanup +/refresh-repo # PR check, default-branch sync +/prune-branches # Delete dead branches and worktrees /rebase-pr # Rebase-merge current branch /rebase-pr 42 # Rebase-merge specific PR by number /squash-merge-pr # Validate and squash merge diff --git a/github-workflows/skills/pr-sweep/SKILL.md b/github-workflows/skills/pr-sweep/SKILL.md index 05b54762..3cc22bf8 100644 --- a/github-workflows/skills/pr-sweep/SKILL.md +++ b/github-workflows/skills/pr-sweep/SKILL.md @@ -7,8 +7,8 @@ description: "Drive open PRs toward zero across one or many repos by risk-rankin Take a pile of open PRs and drive it to zero: merge what is safe, surface what is not with a specific reason. This is triage-and-clear, distinct from -`/refresh-repo --sweep` (which syncs repos and cleans worktrees). This skill acts -on **open PRs**; that one acts on **repo/worktree state**. +`/prune-branches --sweep` (which deletes dead branches and worktrees). This +skill acts on **open PRs**; that one acts on **repo/worktree state**. It does not reimplement merging. It **risk-ranks**, then hands each merge to `/squash-merge-pr` (which itself runs the readiness gate and calls `/finalize-pr`). @@ -83,5 +83,5 @@ outcome, not a failure. - **squash-merge-pr** (github-workflows) — performs each merge; this skill decides which. - **finalize-pr** (github-workflows) — PR metadata/soft-block handling, via squash-merge-pr. -- **refresh-repo** (github-workflows) — the repo/worktree sweep; complementary, not this. +- **prune-branches** (github-workflows) — the repo/worktree sweep; complementary, not this. - **promote-release** (github-workflows) — the correct path for held promotion PRs. diff --git a/github-workflows/skills/prune-branches/SKILL.md b/github-workflows/skills/prune-branches/SKILL.md new file mode 100644 index 00000000..3349df02 --- /dev/null +++ b/github-workflows/skills/prune-branches/SKILL.md @@ -0,0 +1,132 @@ +--- +name: prune-branches +description: Delete branches and worktrees with nothing useful — zero commits ahead of default, or a matched merged PR — local and remote, single-repo or workspace-wide. Run after refresh-repo syncs the default branch. +--- + +# Prune Branches + +Delete branches and worktrees carrying no unique, unmerged work: local, +remote, or both. + +> **State warning**: Branch state, remote tracking, and PR status change between +> invocations. Re-run every git/gh command from the top on each run. + +## Protected + +Skip these before computing staleness, on every pass below — each pass +re-checks independently: + +- The default branch (`main`/`master`/`develop`) and any branch checked out + in a worktree. Git enforces this for passes 1–2 via `branch -d`/`-D`; pass + 3 deletes a different ref, so check explicitly there too. +- Any branch an open PR references as `--head` or `--base` — `--base` + catches a stacked PR's target. + +Build the open-PR reference set once, at the top of the run, not per branch: + +```bash +gh pr list --state open --json number,headRefName,baseRefName --limit 200 +``` + +Union `headRefName` and `baseRefName` into one set; test membership against +it in every pass below instead of a per-branch `gh pr list` call. + +## Stale + +A branch clears Protected and meets either: + +- **Zero commits ahead of the default branch** — + `git merge-base --is-ancestor origin/` (equivalent to + `git log origin/.. --oneline` returning nothing). Ignores + `[gone]` status — the ancestor check, not `[gone]`, is the real "merged" + signal. Run the default-branch guard first: `origin/main` is routinely an + ancestor of `origin/develop` on a git-flow repo. +- A merged PR (most recent by `mergedAt`) whose `headRefOid` matches the + branch tip — `gh pr list --state merged --head --json + number,headRefOid,mergedAt`. Needed for squash-merges, where the branch's + own commits are never literal ancestors of the default branch. + +## Passes + +Three passes, one per ref shape: + +1. **Worktree-paired branches** — `git worktree list` (skip bare repo + entries). If Stale: `git worktree remove ` — never `--force`; a + dirty worktree blocks removal, report and skip. Then `git branch -d + ` (fall back to `-D` only when the merged-PR `headRefOid` matched + the branch tip before removal — a squash-merged branch unreachable from + local default). +2. **Bare local branches** — local branches with no worktree + (`git for-each-ref refs/heads`, excluding pass-1 branches). If Stale: + `git branch -d ` (same `-D` fallback as pass 1). +3. **Remote-only branches** — remote branches with no local ref + (`git branch -r`, excluding `origin/HEAD` and anything with a local + counterpart). Check Stale against `origin/` directly; its only + action is the remote delete below. + +Finish with `git worktree prune`. + +## Remote delete + +Passes 1 and 2 trigger this after their local `git branch -d`/`-D` +succeeds; pass 3 goes straight here. If a live (non-`[gone]`) remote ref +exists: `git push origin --delete` ignores tip state, so immediately before +this specific push — not once at the top of the run — refresh and +re-confirm: + +```bash +git fetch origin +git merge-base --is-ancestor origin/ origin/ +``` + +Skip and report if it no longer holds. Otherwise: `git push origin --delete +`. + +## Cross-Repo Operating Modes + +Optional modes that widen this from single-repo to workspace-wide. Both +reuse Protected + Stale and the deletion rules above — they only add +pre-filters on top of every rule above. + +### `--sweep []` + +Multi-repo cleanup of abandoned local branches. For every default-branch +worktree in your workspace (pass a custom glob for a non-standard layout), +resolve that repo's own default branch first (`main` on trunk repos, +`develop` on git-flow repos), then for every local branch where +`git log origin/..HEAD` is non-empty: + +1. **Content-equivalence check**: compute merge base, diff each touched file + against current `origin/`. If every touched file is + content-equivalent to (or older than) `origin/`, delete the + branch and its worktree. An already-on-default contribution doesn't need + a PR. +2. **Workaround filter**: surface for human review, don't draft-PR, when the + diff (a) modifies 1 of N files sharing a common shape with no written + rationale for the asymmetry, or (b) names a "sync mechanism" / + "auto-update" that `grep -r .` finds zero matches for. +3. Only branches passing both checks become draft PRs. +4. Per-repo summary: branches deleted as content-equivalent, branches + surfaced for review, branches PR-ified, branches unchanged. + +**Origin**: the 2026-05-22 sweep opened 8 dead PRs against `ansible-splunk` +(6 already-on-main duplicates, 2 workaround anti-patterns). Both filters +above would have caught all 8 before any CI ran. + +### `--prune-stale ` (default 60) + +Delete local branches with no open PR and no push activity within ``. +Adds a time threshold on top of Stale; every rule above still applies. + +Per branch: if `gh pr list --head ` is empty and +`git log -1 --format=%cr ` is older than ``, run +`git worktree remove ` (never `--force`) then `git branch -d `. + +Use `--prune-stale 30` for an aggressive sweep, `--prune-stale 90` for +conservative. + +## Related Skills + +- **refresh-repo** (github-workflows) — sync the default branch first +- **git-workflow-standards** (git-standards) — worktree and branch conventions +- **gh-cli-patterns** (github-workflows) — canonical gh CLI command shapes, default-branch detection diff --git a/github-workflows/skills/refresh-repo/SKILL.md b/github-workflows/skills/refresh-repo/SKILL.md index dca57a5b..167457fc 100644 --- a/github-workflows/skills/refresh-repo/SKILL.md +++ b/github-workflows/skills/refresh-repo/SKILL.md @@ -1,13 +1,14 @@ --- name: refresh-repo -description: Check PR merge readiness, sync local repo, cleanup stale worktrees; optional cross-repo sweep and stale-branch prune modes +description: Check PR merge readiness and sync the local repo with its default branch. See prune-branches (github-workflows) for stale branch and worktree cleanup. --- # Git Refresh -Check open PR merge-readiness status, sync the local repository, and cleanup stale worktrees. +Check open PR merge-readiness status and sync the local repository with its +default branch. > **State warning**: Branch state, remote tracking, and PR status change between > invocations. Re-run all git/gh commands from Step 1. @@ -74,10 +75,7 @@ Replace ``, ``, `` per the placeholder legend in that sk of the current shell directory: `git -C merge --ff-only origin/`. If the default worktree is dirty or divergent, report it and skip instead of resetting. -6. Delete local branches already merged into the default branch with `git branch -d`. - Never delete main/master/develop/current branches, worktree-checked-out branches, or branches - with open PRs. -7. Conclude the operation without switching branches. Because Steps 4 and 5 used +6. Conclude the operation without switching branches. Because Steps 4 and 5 used `git -C ` to operate on the default worktree directly, the current shell's working directory and branch were never changed — each worktree owns its checkout. @@ -85,6 +83,9 @@ Do not use `git fetch --tags`, `git fetch --prune-tags`, or `git pull --tags` du normal refresh. Tags are audited separately in Step 4 so local-only non-release tags and tag rewrites are not deleted by a broad fetch refspec. +Branch and worktree cleanup — local, remote, and bare-local — is +`prune-branches` (github-workflows), run separately after this sync. + ### 4. Tag Audit And Cleanup Treat `origin` as authoritative for release tags only. @@ -106,87 +107,12 @@ For local-only tags: For tags that exist both locally and on origin but point at different objects, report the mismatch and do not force-update it automatically. Never delete or rewrite remote tags. -### 5. Worktree Cleanup - -Only remove a worktree if it is confirmed stale. - -**Stale definition**: No open PR, no uncommitted changes, and either: - -- The branch has a merged PR (most recently merged by `mergedAt`) whose `headRefOid` matches the local branch `HEAD` - (`gh pr list --state merged --head --json number,headRefOid,mergedAt`) -- Its remote tracking branch was deleted (`[gone]` in `git branch -vv`) and it has no commits - ahead of the default branch (`git log origin/..HEAD --oneline` is empty) - -Branches with open PRs, local-only branches without PRs, and worktrees with uncommitted -changes are **NEVER** stale. +### 5. Summary -For each worktree from `git worktree list`: - -1. Skip the default branch worktree, the current branch, and bare repo entries -2. If the branch has an open PR, skip — it is **never** stale -3. Check if stale using the definition above -4. If not stale, skip -5. Run `git worktree remove ` — **NEVER use `--force`** -6. If Git blocks removal (dirty worktree), report it and skip -7. If removed, also delete the branch: `git branch -d `. - If this fails only because a squash-merged branch is not reachable from local default, - use `git branch -D ` only when the merged PR `headRefOid` matched the local - branch `HEAD` before removing the worktree. - -Finish with `git worktree prune`. - -### 6. Summary - -Report: PRs assessed as merge-ready (if any), tags deleted or reported, branches cleaned up, -worktrees removed, default-branch worktree restorations (with any stash references created), +Report: PRs assessed as merge-ready (if any), tags deleted or reported, +default-branch worktree restorations (with any stash references created), current branch, and sync status. -## Cross-Repo Operating Modes - -Optional modes that change `/refresh-repo` from single-repo to workspace-wide. -Both modes reuse the stale-worktree definition from Step 5 and the deletion -rules from Step 5.7 — they only add new pre-filters, never weaken existing -safety. - -### `--sweep []` - -Multi-repo cleanup of abandoned local branches. For every default-branch -worktree in your workspace (caller can pass a custom glob if their layout -differs), resolve that repo's own default branch first (per Step 3 above — -`main` on trunk repos, `develop` on git-flow repos), then for every local -branch where `git log origin/..HEAD` is non-empty: - -1. **Content-equivalence check**: compute merge base, diff each touched file - against current `origin/`. If every touched file is content-equivalent - to (or older than) `origin/`, delete the branch and its worktree. - Already-on-the-default-branch contributions do not deserve a PR. -2. **Workaround filter**: if the diff (a) modifies 1 of N files sharing a - common shape with no written rationale for the asymmetry, or (b) references - a "sync mechanism" / "auto-update" by name that `grep -r .` returns - zero matches for, surface for human review. Do not open a draft. -3. Only branches passing both checks become draft PRs. -4. Per-repo summary: branches deleted as content-equivalent, branches surfaced - for review, branches PR-ified, branches unchanged. - -**Origin**: the 2026-05-22 sweep opened 8 dead PRs against `ansible-splunk` -(6 already-on-main duplicates, 2 workaround anti-patterns). Both filters -above would have caught all 8 before any CI ran. - -### `--prune-stale ` (default 60) - -Delete local branches with no open PR and no push activity within ``. -Expands Step 5's stale definition with a time threshold; still respects every -safety rule (never delete branches with open PRs, uncommitted changes, or -the current checkout). - -Per branch: if `gh pr list --head ` is empty AND -`git log -1 --format=%cr ` is older than ``, run -`git worktree remove ` (per Step 5 — never `--force`) then -`git branch -d `. - -Use `--prune-stale 30` for an aggressive sweep, `--prune-stale 90` for -conservative. - ## Common Mistake to Avoid **DO NOT** skip the PR check just because you're on main. The user may have multiple open PRs from different branches. @@ -198,6 +124,7 @@ explicit refspec prune and can delete local-only tags that are not release artif ## Related Skills +- **prune-branches** (github-workflows) — stale branch and worktree cleanup, run after this sync - **sync-main** (git-workflows) — Syncs the default branch and merges into current or all PR branches - **rebase-pr** (github-workflows) — Rebase-merge workflow for merging individual PRs - **git-workflow-standards** (git-standards) — Worktree structure and branch hygiene conventions diff --git a/project-standards/skills/skills-registry/SKILL.md b/project-standards/skills/skills-registry/SKILL.md index 2bee3042..8af8033c 100644 --- a/project-standards/skills/skills-registry/SKILL.md +++ b/project-standards/skills/skills-registry/SKILL.md @@ -10,7 +10,8 @@ description: Use when looking up available tools, skills, commands, agents, or p | Intent | Command | Plugin | Notes | | --- | --- | --- | --- | | Sync branch with main | `/sync-main` | `git-workflows` | Merge into current | -| Sync repo, cleanup | `/refresh-repo` | `git-workflows` | Also merges PRs | +| Sync repo | `/refresh-repo` | `git-workflows` | Also merges PRs | +| Prune dead branches, worktrees | `/prune-branches` | `github-workflows` | Local and remote | | Rebase + merge PR | `/rebase-pr` | `git-workflows` | Local rebase-merge | | Squash + merge PR | `/squash-merge-pr` | `github-workflows` | Squash-merge | | Troubleshoot rebase | `/troubleshoot-rebase` | `git-workflows` | Recover failures |