Skip to content

haoranyu/clean-closed-issue-worktrees

v0.2.0MIT

Safely audit and remove Git worktrees linked to closed GitHub or GitLab issues.

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:

  1. Scan and classify every worktree without modifying Git state.
  2. Show exact paths, risks, issue state, and estimated reclaimable space.
  3. Ask the user to confirm an exact batch and whether branches should be retained.
  4. 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

SurfaceStatus
GitHub, GitLab issue and PR/MR stateSupported through the agent's provider skill, MCP, CLI, API, or browser fallback
Generic local Git worktreesThe engine can inspect and remove any Git-registered worktree, regardless of creator; generic Git compatibility alone does not prove task inactivity
Managed-root provenanceRoot 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 worktreesInstallable 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 AgentsCursor-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 CodeInstallable with gh skill; their existing task/session mapping rules are unchanged
Other Agent Skills clientsStandard SKILL.md payload; install manually or with a compatible skill installer
Local runtimeGit and Python 3.9+
Script/package CIUbuntu, 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:

  1. GitHub/GitLab-specific skills, connectors, or MCP servers;
  2. an already authenticated gh or glab;
  3. official read-only APIs for public repositories;
  4. browser MCP or a harness-provided browser;
  5. 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