Periodic repository cleanup sweep - finds orphaned/merged git branches, orphaned worktrees, stale `.claude/session/*.md` files, and (target:full only) orphaned documentation. Reports every finding with evidence, then executes only the branches/worktrees/session-files the user explicitly confirms via a single AskUserQuestion batch. Never deletes docs/*.md itself - those are always report-only, evidence-based, human-decided. Distinct from the Phase 8 cleanup gate (which tears down one specific ...
Installs into .claude/skills of the current project.
Are you the author of Repo Hygiene?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/marcoguillermaz-repo-hygiene-tierward)
---
name: repo-hygiene
description: Periodic repository cleanup sweep - finds orphaned/merged git branches, orphaned worktrees, stale `.claude/session/*.md` files, and (target:full only) orphaned documentation. Reports every finding with evidence, then executes only the branches/worktrees/session-files the user explicitly confirms via a single AskUserQuestion batch. Never deletes docs/*.md itself - those are always report-only, evidence-based, human-decided. Distinct from the Phase 8 cleanup gate (which tears down one specific block at closure) - this is the periodic sweep that catches what block-by-block cleanup misses.
user-invocable: true
model: sonnet
argument-hint: "[target:full]"
allowed-tools: >
Bash(bash .claude/skills/repo-hygiene/references/scan-branches-worktrees.sh), Bash(git branch:*),
Bash(git worktree:*), Bash(git status:*), Bash(git log:*), Bash(git fetch:*),
Bash(git rev-parse:*), Bash(git show:*), Bash(gh pr list:*), Bash(gh pr view:*),
Bash(ls:*), Bash(wc:*), Bash(date:*), Bash(stat:*), Bash(rm .claude/session/*.md),
Read, Grep, Glob
---
Runs in the **main conversation, deliberately without `context: fork`**. `AskUserQuestion` is unavailable to subagents/forked skill contexts, and this skill's confirm-then-execute design requires it mid-run. Do not add `context: fork` to this file.
Goal: search thoroughly, report every candidate with concrete evidence, execute only what the user explicitly confirms. Never guess-execute. Never touch `docs/*.md` regardless of confirmation.
## Step 0 - Parse scope
`$ARGUMENTS`: default = quick scan (Steps 1-2). `target:full` adds Step 3 (doc orphans) - the slow, judgment-heavy, evidence-only pass. State which mode is active before starting.
**Always exclude `.claude/worktrees/**` from every Grep/Glob in Steps 2 and 3.** Worktrees are full repo checkouts - without this exclusion every file inside every active worktree gets reported as a duplicate "root-level" finding.
## Step 1 - Branch & worktree scan (mechanical, always runs)
Run the companion script:
```bash
bash .claude/skills/repo-hygiene/references/scan-branches-worktrees.sh
```
It is read-only and prints three sections:
- `SAFE_BRANCH_DELETE` - bare branch names with no attached worktree, merged into every protected branch that exists on origin (see `PROTECTED_BRANCHES` at the top of the script), no open PR.
- `SAFE_WORKTREE_REMOVE` - `<branch>|<path>` pairs, merged into all protected branches, clean working tree, no open PR. Lines prefixed `KEEP|` are explicitly excluded with a reason (uncommitted changes / not yet merged / open PR / gh check failed) - do not re-litigate these, the script already checked.
- `PRUNABLE_WORKTREE` - paths registered in `git worktree list` whose actual directory no longer exists.
The classification is deliberately conservative (merged into ALL protected branches + clean + no open PR; a failed `gh` check reads as unknown, never as "no PR"). Trust it - do not loosen or tighten the criteria ad hoc.
**Safety nets when executing in Step 6**: always `git branch -d` (never `-D` - `-d` refuses on unmerged content, which is your backstop against a misclassification). Always `git worktree remove` without `--force` (refuses if dirty). If either refuses, STOP and report to the user - do not retry with the forced variant.
## Step 2 - Session-file staleness check (judgment, always runs)
For every `.claude/session/*.md` (block-*.md and fix-*.md):
1. Read the file. Note its mtime (`stat`), its front matter, and any `Status:` line.
2. Front matter `block_closed: true` or `fix_closed: true` means the block was closed and the file was kept on purpose at the cleanup gate - classify as **WEAK CANDIDATE** ("archived by choice - delete only if no longer needed").
3. `gh pr list --state merged --search "<slug derived from filename>" --limit 5` - does a merged PR confirm that block is closed?
4. Check `git branch -a` and `git worktree list` for a branch/worktree matching the file's block/branch name.
5. Classify - evidence-based, never by mtime alone in either direction (a fresh in-progress block and a long-blocked-but-still-relevant investigation must both resolve to KEEP absent closure evidence):
- **STRONG CANDIDATE**: a merged PR confirms closure AND no matching branch/worktree exists anymore.
- **WEAK CANDIDATE**: closed-and-kept front matter (point 2), OR no merged PR references it AND the file's own status reads as finished/abandoned AND it is several days old. Report with lower confidence, explicit evidence.
- **KEEP**: recent activity, an in-progress/blocked-pending-external-action status, or a still-existing matching branch/worktree. When in doubt, KEEP.
## Step 3 - Documentation orphan sweep (`target:full` only, judgment, evidence-only, NEVER executable)
Scan `docs/**/*.md`, excluding the pipeline-fed canonical set (`requirements.md`, `implementation-checklist.md`, `refactoring-backlog.md`, `sitemap.md`, `db-map.md`, `pipeline-standards.md`, `claudemd-standards.md`, `model-effort-policy.md`, `adr/`, `specs/`, `metrics/`). For each remaining file, check inbound references - Grep across every other tracked doc, every `SKILL.md`, `CLAUDE.md`, and the `.claude/rules/*.md` files. A file with zero inbound references AND content suggesting a closed/superseded snapshot is reported as **evidence-only**.
**Hard rule**: documentation is git-tracked content - a wrong call here is real, hard-to-reverse loss, a different risk class than a git branch or a local session file. Never include a doc-orphan finding in the Step 5 confirmation batch, never suggest a deletion command for it, regardless of how confident the evidence looks. State this limitation explicitly in the report so it doesn't read as an oversight.
## Step 4 - Report
Present findings grouped by category, each with its evidence. For branches/worktrees/session files: include the exact command that would run. For doc orphans: evidence only, explicitly labeled "not included in execution - human judgment required."
## Step 5 - Confirm (AskUserQuestion)
One batched question (or a small set): list the exact items proposed for deletion, grouped, with an option to execute all / execute a subset / cancel. Do not include doc-orphan findings in this gate - they were never candidates for execution.
## Step 6 - Execute confirmed items only
- Branches: `git branch -d <name>`
- Worktrees: `git worktree remove <path>`
- Prunable worktree registrations: `git worktree prune`
- Session files: `rm .claude/session/<file>.md` - only for items the user explicitly confirmed, one at a time, never a bulk `rm .claude/session/*.md`.
If any command refuses (unmerged branch, dirty worktree), stop and report - do not force.
## Step 7 - Summary
One line per category: found / confirmed / executed / left for later (docs). If nothing was found in a category, say so plainly rather than omitting the section - a clean report is a valid, useful outcome.