Clean Closed Issue Worktrees
A safety-first agent skill for auditing and removing Git worktrees associated with closed GitHub or GitLab issues.
It works with Git-registered worktrees regardless of editor, agent harness, or creator. Provider and live-task integrations stay in the agent layer, while a deterministic Python script owns local Git inspection, managed-root provenance, snapshot validation, backup refs, and removal.
Why this exists
Closed issues often leave behind worktrees containing dependency folders and build caches. Blind cleanup is dangerous: a clean git status can hide ignored local data, a detached HEAD can be the only ref to a commit, and an apparently idle worktree can still belong to an active agent task.
This skill enforces a two-phase workflow:
- Scan and classify every worktree without modifying Git state.
- Show exact paths, risks, issue state, and estimated reclaimable space.
- Ask the user to confirm an exact batch and whether branches should be retained.
- Revalidate the entire batch, then remove only unchanged, approved targets.
The default recommendation is to remove worktrees while retaining local branches.
Safety properties
- Only closed ordinary issues or merged PRs/MRs qualify.
- Dirty, locked, execution-time current, main, active-task, and broad paths are refused.
- Unknown harness state and ambiguous issue mapping are review conditions.
- Paths recognized as harness-managed cannot be mislabeled as unmanaged; unavailable task state fails closed to Needs review.
- Ignored
.env, database, key, credential, and unknown paths require explicit review. - Detached orphan commits can be protected with deterministic backup branches.
- Branch deletion uses only
git branch -d; force deletion is not implemented. - Worktree removal uses only exact paths returned by Git; recursive filesystem deletion is not implemented.
- A changed snapshot aborts the whole batch before the first removal.
- Mid-batch failures stop immediately and report removed, failed, and untouched targets.
Install
Requires GitHub CLI 2.90.0 or later. Preview the complete skill payload before installing it:
gh skill preview haoranyu/clean-closed-issue-worktrees \
clean-closed-issue-worktrees
Install and pin the audited v0.2.0 release for Codex:
gh skill install haoranyu/clean-closed-issue-worktrees \
clean-closed-issue-worktrees@v0.2.0 \
--agent codex --scope user
Or for Claude Code:
gh skill install haoranyu/clean-closed-issue-worktrees \
clean-closed-issue-worktrees@v0.2.0 \
--agent claude-code --scope user
With Node.js 22.20 or later, the cross-agent skills CLI is another option:
npx skills add haoranyu/clean-closed-issue-worktrees \
--skill clean-closed-issue-worktrees --agent codex --global
Plugin marketplaces
This repository provides a portable Agent Plugin manifest and Claude Code
plugin metadata. Cursor is one compatible Agent Plugin host. Both manifests
discover the same skills/clean-closed-issue-worktrees payload; no skill logic
is duplicated.
For local Cursor testing, clone the repository and copy its portable Agent Plugin payload into Cursor's local plugin directory, then reload Cursor. Use a physical copy: some Cursor versions reject symlinks whose resolved target is outside the local plugin directory. The guard below preserves an existing installation on macOS or Linux:
cursor_plugin_source=/absolute/path/to/clean-closed-issue-worktrees
cursor_plugin_target="$HOME/.cursor/plugins/local/clean-closed-issue-worktrees"
if [ -e "$cursor_plugin_target" ]; then
printf 'Destination already exists: %s\n' "$cursor_plugin_target" >&2
exit 1
fi
mkdir -p "$cursor_plugin_target"
cp "$cursor_plugin_source/plugin.json" "$cursor_plugin_target/plugin.json"
cp -R "$cursor_plugin_source/skills" "$cursor_plugin_target/skills"
On native Windows PowerShell, copy the plugin to the same documented ~
location under $env:USERPROFILE, with the same destination guard:
$cursorPluginSource = "C:\absolute\path\to\clean-closed-issue-worktrees"
$cursorPluginTarget = `
"$env:USERPROFILE\.cursor\plugins\local\clean-closed-issue-worktrees"
if (Test-Path -LiteralPath $cursorPluginTarget) {
throw "Destination already exists: $cursorPluginTarget"
}
New-Item -ItemType Directory -Force $cursorPluginTarget | Out-Null
Copy-Item -LiteralPath "$cursorPluginSource\plugin.json" `
-Destination "$cursorPluginTarget\plugin.json"
Copy-Item -Recurse -LiteralPath "$cursorPluginSource\skills" `
-Destination "$cursorPluginTarget\skills"
Run Developer: Reload Window, then verify the plugin and skill under
Customize. Cursor's Add > From Local Repository action imports a
marketplace repository; it is not the local single-plugin test path described
above.
For local Claude Code testing:
claude --plugin-dir /absolute/path/to/clean-closed-issue-worktrees
Marketplace installation instructions will be added after the Cursor and Claude community reviews approve the plugin.
For manual installation, download the versioned source archive from the
v0.2.0 release
and copy its skills/clean-closed-issue-worktrees directory into the skill
directory used by your agent. Do not install the entire repository: the payload is only
skills/clean-closed-issue-worktrees.
Then ask the agent to use $clean-closed-issue-worktrees with a local repository and a GitHub/GitLab issue-list URL.
Compatibility
| Surface | Status |
|---|---|
| GitHub, GitLab issue and PR/MR state | Supported through the agent's provider skill, MCP, CLI, API, or browser fallback |
| Generic local Git worktrees | The engine can inspect and remove any Git-registered worktree, regardless of creator; generic Git compatibility alone does not prove task inactivity |
| Managed-root provenance | Root recognition is adapter-specific. Cursor's documented local root is the current built-in path adapter; worktrees from other editors remain inspectable, authoritative exact-path ownership is used when available, and missing state remains Needs review |
| Known task-state mappings (non-exhaustive) | Codex, Claude Code, and Cursor have documented ownership and state mappings; named integrations are not an allowlist, and unavailable authoritative state remains Needs review |
| Cursor local worktrees | Installable as an Agent Skill or Agent Plugin; Agents Window, IDE /worktree, /best-of-n, and CLI --worktree are covered through the documented ~/.cursor/worktrees root. The scanner resolves ~ through the runtime home: $HOME/.cursor/worktrees on macOS, Linux, and WSL, and %USERPROFILE%\.cursor\worktrees on native Windows. An explicit absolute CURSOR_WORKTREES_ROOT compatibility override can bridge a known cross-runtime path; the scanner never guesses %APPDATA% or an installation directory. An isolated local subagent is covered when a recognized root contains it or authoritative metadata maps its exact path; otherwise state fails closed to Needs review |
| Remote Cursor Cloud Agents | Cursor-hosted and /in-cloud VM clones are out of local cleanup scope; an exact locally registered self-hosted/Remote Control checkout still follows normal fail-closed ownership rules |
| Codex and Claude Code | Installable with gh skill; their existing task/session mapping rules are unchanged |
| Other Agent Skills clients | Standard SKILL.md payload; install manually or with a compatible skill installer |
| Local runtime | Git and Python 3.9+ |
| Script/package CI | Ubuntu, macOS, and native Windows. CI exercises default-home and explicit-root Cursor detection plus the original generic scan/plan/remove flow; live host task/session integrations are not exercised in CI |
Provider routing
The skill prefers, in order:
- GitHub/GitLab-specific skills, connectors, or MCP servers;
- an already authenticated
ghorglab; - official read-only APIs for public repositories;
- browser MCP or a harness-provided browser;
- a user-provided issue export.
The script never reads tokens, credential stores, browser cookies, or issue pages. This separation keeps it portable across harnesses.
Local script
Requirements: Python 3.9+ and Git.
On Windows, if python3 opens the Microsoft Store or does not resolve to your installed interpreter, substitute py -3 for python3 in the commands below.
Read-only inventory:
From this repository checkout:
python3 skills/clean-closed-issue-worktrees/scripts/worktree_cleanup.py scan \
--repo /path/to/repository \
--baseline upstream/main \
--json-out /tmp/worktree-scan.json \
--markdown-out /tmp/worktree-scan.md \
--stdout none
The equivalent native Windows PowerShell scan uses a Windows repository path and the system temporary directory:
py -3 skills/clean-closed-issue-worktrees/scripts/worktree_cleanup.py scan `
--repo "C:\path\to\repository" `
--baseline upstream/main `
--json-out "$env:TEMP\worktree-scan.json" `
--markdown-out "$env:TEMP\worktree-scan.md" `
--stdout none
After provider verification and user review, the agent creates a normalized selection in a temporary directory:
python3 skills/clean-closed-issue-worktrees/scripts/worktree_cleanup.py create-plan \
--repo /path/to/repository \
--selection /tmp/selection.json \
--output /tmp/plan.json
py -3 skills/clean-closed-issue-worktrees/scripts/worktree_cleanup.py create-plan `
--repo "C:\path\to\repository" `
--selection "$env:TEMP\selection.json" `
--output "$env:TEMP\plan.json"
After the user confirms that exact plan:
python3 skills/clean-closed-issue-worktrees/scripts/worktree_cleanup.py execute \
--plan /tmp/plan.json \
--confirm-plan <plan-id> \
--delete-plan-on-success
py -3 skills/clean-closed-issue-worktrees/scripts/worktree_cleanup.py execute `
--plan "$env:TEMP\plan.json" `
--confirm-plan EXACT_PLAN_ID `
--delete-plan-on-success
See SKILL.md for the agent workflow and references/evidence-schema.md for the normalized selection format.
Test
Tests create isolated temporary repositories and never operate on the checkout containing this skill.
python3 -m unittest discover -s tests -v
On Windows, if python3 opens the Microsoft Store or does not resolve to your installed interpreter, use py -3 instead:
py -3 -m unittest discover -s tests -v
GitHub Actions runs the suite on Linux, macOS, and Windows.
License
MIT