Skip to content
cccskills
FreePublished on GitHubEnglish overview

cleanup-git-worktrees

Audit Git worktrees and local branches to find merged, stale, or otherwise removable entries. The skill groups candidates by reason, flags dirty or unpushed work for review, and guides guarded cleanup followed by a verification audit.

OriginalShow the original description

Use for stale-worktree, Git registration and branch audits. Managed removal uses the repo lifecycle.

lobehub/lobehub83,504 stars(GitHub 83,504 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Review stale worktrees before cleanup
  • Find branches whose pull requests have merged
  • Identify dirty or unpushed work to preserve
  • Remove approved worktrees and verify the result

Overview

What it does

This skill audits Git worktrees and local branches, then sorts them into removal candidates, items that need manual review, and protected entries. It checks merge and PR status, age, unpushed commits, and working tree changes, and explains why an entry may be removable. After you review the candidates, it guides cleanup of named targets and a follow-up audit to report what was removed and what remains.

When it helps

Use it to clear worktrees that have been idle for a while or to tidy local branches after PRs are merged. It separates work to keep from deletion candidates so you can review modified files and unpushed commits. If the repository manages services or other resources alongside worktrees, the skill points out that a dedicated lifecycle procedure may be needed.

Example requests

  • “Audit this repository’s worktrees and list removal candidates and protected entries with reasons.”
  • “Check merged PR branches and separate those with unpushed work.”
  • “Clean up the branches I approved and report the resulting state.”

Notes

The audit uses the included scripts/cleanup.sh. PR status can be queried with an authenticated gh CLI. Deletion is treated as destructive: when targets are not named explicitly, the skill presents candidates and waits for approval. It does not use recursive deletion for registered worktrees. Removing a leftover directory from a broken registration requires a specified path and confirmation.

This overview was written by AI (OpenAI Codex) from the public SKILL.md. Check the original below for the exact behavior.

Document check

AI checked the SKILL.md for operations that need care and for setup requirements. The score comes only from actually running the skill (hands-on test).

SafetyAre sending data, deleting files, and running commands disclosed?
BPartly lackingApproval is required, but a repo policy also permits deleting dirty merged or closed PR branches.
Easy setupDoes it need few tools or API keys, and are they documented?
BPartly lackingThe bundled script is required; PR checks use an authenticated `gh` CLI.

Not counted in the score. Rated by AI (OpenAI Codex) as of Oct 26, 2026. Treat it as a guide and read the original before installing.

Disagree? Request a re-evaluation

Tell us which point is wrong and why. No login needed. We will check it against the SKILL.md, redo the evaluation, and show the result here.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Files included (3)

  • SKILL.md10.9 KB
  • agents/openai.yaml241 B
  • scripts/cleanup.sh11.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Cleanup Git Worktrees

Use the bundled script to make classification deterministic. Treat cleanup as a destructive action: audit first, show the exact candidates grouped by why they are deletable, and obtain one explicit approval before applying deletion unless the user's current request already names the exact targets. Aim for a single approval round-trip, not a per-item Q&A.

Workflow

  1. Run the audit from any worktree in the repository:

    bash .agents/skills/cleanup-git-worktrees/scripts/cleanup.sh audit --fetch --gh --stale-days 15 --base origin/canary
    
    • --fetch: prune the remote first. Omit only when offline and say so.
    • --gh: look up each branch's PR (number, state, head SHA) with the gh CLI. This is what turns [gone] into a verified merge; use it whenever gh is authenticated. It costs roughly one second per branch, so run it in the background on large repos.
    • --stale-days N: mark clean branches idle longer than N days with nothing unpushed as candidate-stale. The repository default the user has approved is 15 days.
    • --noise REGEX: untracked paths that never count as dirt (default node_modules|\.goal-tracing/).
  2. Interpret classifications:

    ClassificationMeaningAction
    protected-branchmain, canary, or the base refnever
    protect-currentthe worktree running the commandnever
    protect-dirtytracked modifications, or untracked files outside the noise regexkeep; list the files
    candidate-mergedtip is an ancestor of the basedelete
    candidate-pr-mergedPR state MERGED and local tip == PR head SHAdelete
    candidate-pr-closedPR CLOSED and tip == PR head or nothing unpusheddelete
    candidate-goneupstream pruned and nothing unpusheddelete after confirming PR state
    candidate-staleolder than --stale-days, clean, nothing unpusheddelete
    review-pr-merged-aheadPR merged but local tip has extra commitscheck the extra commits by subject (below)
    review-gone-unpushedupstream pruned but local-only commits existkeep; deleting loses work
    review-no-upstreamno upstream, not mergedkeep unless the user names it
    review-detacheddetached HEAD worktreekeep; show its dirty files
    broken-registration(...)registration whose gitdir is gonesee below
    activeeverything elsekeep

    Columns worth reading: dirty (real dirt), noise (ignorable untracked), age_days, unpushed (commits no remote ref reaches), pr / pr_state / tip_eq_pr.

  3. Present one compact table split into three groups: deletable now (all candidate-*), needs a manual check (review-*), and protected. Keep candidate-merged separate from candidate-gone, and never describe [gone] alone as proof of merge.

  4. After approval, use the repository's worktree lifecycle workflow when it manages services or other resources alongside a checkout; the Git-only script does not tear those down. For plain Git worktrees, pass exact branch names and the same classification flags used for the audit:

    bash .agents/skills/cleanup-git-worktrees/scripts/cleanup.sh clean \
      --gh --stale-days 15 --base origin/canary \
      --branch feat/example \
      --branch fix/example \
      --apply
    

    Without --apply, cleanup is a dry run. The script re-classifies every target immediately before deletion and refuses anything that is not candidate-*. Worktrees whose only dirt is noise are removed with git worktree remove --force; everything else uses the plain, refusing form.

    Removing a worktree that still holds node_modules can take several minutes each. Run large batches in the background and report progress rather than waiting on a single foreground call.

  5. Run the audit again and report:

    • removed worktrees and branches, with the SHA each branch pointed at (the script prints it; reflog keeps it for 90 days);
    • retained dirty / unpushed / detached targets and why;
    • remaining worktree and local-branch counts;
    • any partial deletion or Git error.

Proving a merge

Squash and rebase merges leave no ancestry, so git merge-base --is-ancestor and git cherry say "not merged" for branches whose PR landed. Use this ladder instead:

  1. --gh reports pr_state=MERGED and tip_eq_pr=eq: the local tip is exactly what GitHub merged. Done.
  2. review-pr-merged-ahead (tip moved past the PR head): list the extra commits with git log <pr-head>..<branch> and search the base for each subject: git log origin/canary --oneline -F --grep="<subject>". A hit on the PR's squash commit means the commit was part of the PR. A miss means the commit is unmerged; check which live branch still contains it with git branch --contains <sha> before deleting.
  3. Branches named after a PR (pr-18412, codex/pr-18497-fix) are local review copies; verify the PR by number with gh pr view N --json state,headRefOid and compare subjects as above.
  4. Do not judge by git diff origin/canary <branch> -- <files>: on an old branch the diff is dominated by later changes on the base.

Zero unpushed means every local commit is reachable from some remote ref, so deleting the local branch loses nothing even when the PR is still OPEN; the user has approved deleting such branches once they pass the stale threshold.

Repo-approved deletion policy

The repository owner has approved the following for this repo, so a cleanup run does not need to re-ask per item. It is the sanctioned exception to the safety rules below; state it in the report and say plainly whenever uncommitted work is being discarded.

  • Dirt equal to the base is not dirt. A tracked file listed as modified whose content is byte-identical to the base (git diff --quiet <base> -- <file>) is stale residue of a branch that is simply behind. When presenting, do not count it as a real change: list such files in their own group so the genuine modifications stay visible.
  • Merged / closed PR branches are disposable together with their local commits. When a branch's PR is MERGED or CLOSED, its worktree and local branch may be removed even when unpushed > 0 or the tree is dirty. Record every branch SHA before deleting (reflog keeps them ~90 days) and state in the report that uncommitted edits are not recoverable.
  • Local scratch does not block removal. Untracked *.probe.test.ts, spike scripts under scripts/, and untracked screenshots (*.png) are disposable; do not raise them as blockers.
  • Still never delete: main, canary, the base ref, and the worktree running the command. A branch whose PR is OPEN is kept while it still holds local-only work (dirty files or unpushed > 0); a clean, fully-pushed branch with an open PR is the redundant local copy the stale rule already covers.

Proving a merge when the repo squash-merges. This repo squash-merges, so a landed PR leaves the squash commit on the base but none of the branch's own commits. Searching the base for an individual commit subject therefore fails for every squashed branch — do not read that as "unmerged". Decide review-pr-merged-ahead by the PR's state plus commit dates instead: if the branch tip is not newer than mergedAt, the branch is pure pre-merge history whose content is already on the base (verify the PR's mergeCommit with git merge-base --is-ancestor <mergeCommit> origin/canary). Only commits dated after mergedAt are candidate unmerged work — keep the branch and list them.

The bundled script still refuses anything outside candidate-*, so these removals are done manually: wt_path=$(git worktree list --porcelain …) → record git rev-parse <branch> → git worktree remove --force → git branch -D. Keep the run in the background; removals that hold node_modules take minutes each.

Broken registrations

git worktree list --porcelain marks an entry prunable when its gitdir file points nowhere (typically after a partial removal). The audit reports these as broken-registration(directory-present|missing; reason) instead of crashing on them.

  • git worktree prune drops only the registration. It never touches the directory or the branch, so it is safe to run before re-auditing.
  • A leftover directory has no .git, so its dirty state cannot be checked. Show its size and file count and ask before deleting it with rm -rf; that is the one place recursive deletion is acceptable, and only on a path the user named.

Safety rules

  • Never discard dirty worktrees merely because their branch is merged, [gone], or old; noise-only dirt (the --noise regex) does not count. The one exception is a worktree whose branch's PR is MERGED or CLOSED, per the repo-approved deletion policy above.
  • Never delete a branch with unpushed > 0 through this script; list it and let the user decide. Same exception: a MERGED/CLOSED PR branch may be removed under the owner's standing approval, with its SHA recorded first.
  • Never use recursive deletion on a registered worktree. If Git partially removes a directory, stop and inspect the exact path before deciding how to recover.
  • Never delete main, canary, or the base branch.
  • Preserve unrelated user changes and concurrent worktrees. The user's other sessions may be working in them.
  • When writing ad-hoc verification loops, run them under bash, not the interactive zsh: zsh does not word-split $var in for/argument positions, so file lists collapse into one argument and checks silently pass.

Script

Use scripts/cleanup.sh; do not recreate its parsing and guard logic ad hoc unless the repository layout makes it unusable. It is set -euo pipefail; helper functions that pipe through grep wrap it in || true so an empty match is "clean", not an abort.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

acceptance

FreeSummary

Use this skill to verify a delivery by exercising the real product through a suitable surface such as a CLI, web app, desktop app, or iOS Simulator. It helps create or find a verification plan, capture evidence, and publish an acceptance report.

  • Verifying a completed product change
  • Collecting screenshots or other evidence
  • Creating a verification plan and report
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to add an AI model provider to LobeHub across runtime integration, model cards, settings, branding, documentation, and acceptance checks. It also covers provider documentation updates and distinguishes new providers from models added to an existing provider.

  • Integrating a new AI model provider into LobeHub
  • Updating setup or usage documentation for a provider
  • Checking whether a model needs a new provider integration
lobehub/lobehub83.5KUpdated Oct 26, 2026

add-setting-env

FreeSummary

Use this skill to add a server-side environment variable that sets a default for a user setting. It walks through validation, server configuration and store merging, plus updating the example environment file and self-hosting documentation.

  • Adding an environment variable for a setting default
  • Wiring a new setting domain into server config
  • Documenting a new variable for self-hosting
lobehub/lobehub83.5KUpdated Oct 26, 2026

Provides guidance for registering hooks around an agent’s execution steps, tool calls, human approvals, context compression, and sub-agent calls. Hooks can observe events, supply local mock tool results, or use webhooks to allow or deny tool results under configured policies.

  • Observing agent steps and tool errors
  • Mocking tool results during local execution
  • Requiring webhook decisions before or after a tool call
lobehub/lobehub83.5KUpdated Oct 26, 2026

agent-signal

FreeSummary

Build event-driven background workflows for agents by defining sources, interpreting them as signals, and running actions through policies. It also guides asynchronous handoffs, deduplication, scoped execution, completion receipts, and observability.

  • Adding a new event source to an agent workflow
  • Routing interpreted signals to concrete background actions
  • Enqueuing work for asynchronous processing
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to run end-to-end acceptance checks for bot channels in real desktop chat apps. It drives apps such as Discord, Slack, Telegram, WeChat, Lark, and QQ to send a message, wait for a response, and capture the result.

  • Checking a bot response in a real Discord channel
  • Testing chat behavior through Slack or Telegram
  • Capturing a bot interaction in a native macOS app
lobehub/lobehub83.5KUpdated Oct 26, 2026

See all skills by lobehub

Report a problem with this skill