Skip to content

fix(ccw): handle prunable worktrees in picker and clean-up flows#46

Merged
tqer39 merged 11 commits into
mainfrom
worktree-happy-mantis-9951
Apr 25, 2026
Merged

fix(ccw): handle prunable worktrees in picker and clean-up flows#46
tqer39 merged 11 commits into
mainfrom
worktree-happy-mantis-9951

Conversation

@tqer39

@tqer39 tqer39 commented Apr 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • git worktree list --porcelainprunable 行をパースし、worktree.StatusPrunable を新設。worktree.List がディスクへ触らずに分類することで、ディレクトリ消失済み worktree が混在しても ccwgit status --porcelain: exit status 128 でクラッシュしなくなる。
  • picker に灰色の [prune] バッジを追加し、単独削除では 1 件 / 複数件で確認文言を出し分け、bulk 削除と --clean-all でも prunable を含めて git worktree prune を最後に 1 回呼ぶ。
  • 仕様: docs/superpowers/specs/2026-04-25-prunable-worktree-handling-design.md / 計画: docs/superpowers/plans/2026-04-25-prunable-worktree-handling.md

Test Plan

  • go test ./... (178 / 14 packages)
  • ~/workspace/tqer39/terraform-github で再現確認: 起動時クラッシュが解消し、picker に synchronous-finding-wave (prunable) が表示される
  • 単独削除フローで prunable を選択 → 確認 → git worktree prune 実行で一覧から消えることを TUI で目視確認
  • --clean-all で prunable を含む worktree がまとめて掃除されることを確認

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for prunable worktrees (git worktree entries with missing directories)
    • Implemented git worktree prune cleanup functionality
    • Added visual badge indicator (🧹) for prunable worktrees in the picker UI
    • Updated deletion workflow to automatically use prune for prunable entries instead of remove
  • Documentation

    • Added execution plan and design specification for prunable worktree handling

tqer39 and others added 10 commits April 25, 2026 18:57
ccw 起動時に git worktree list --porcelain の prunable エントリで
git status が exit 128 を返し起動失敗する問題への対応方針を spec 化。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
設計 spec を bite-sized タスクに展開。gitx → worktree → picker → cmd/ccw
の順で TDD で実装し、最後に terraform-github での手動受け入れまで含める。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
git worktree list --porcelain lists prunable entries (working directory
gone but admin files remain). ParsePorcelain now records this on
WorktreeEntry.Prunable, and the new Prune helper wraps
git worktree prune for callers that want to clean them up.

Also fix pre-existing picker test failures from bubbletea v2 upgrade:
tea.View is now a struct (use .Content for string), and lipgloss v2
removed ColorProfile/SetColorProfile from the top-level API.
When git worktree list reports a prunable entry, surface it as
StatusPrunable instead of running git status on a missing path
(which fails with exit 128 and broke ccw startup). The new Prune
helper wraps gitx.Prune so the picker can clean these up.
Selection.IsPrunable and BulkDeletion.RunPrune let the caller decide
when to invoke worktree.Prune instead of (or in addition to)
worktree.Remove. Bulk paths now exclude prunable entries since
git worktree remove cannot act on them.
Adds the prunable badge ([prune]/PRUNE) and the 🧹 icon in the legacy
Icon API. Prunable rows replace ahead/behind/dirty indicators with
a (missing on disk) hint since the working directory is gone.
Selecting a prunable row and pressing 'd' now shows a Prune prompt
that runs git worktree prune. When more than one prunable exists,
the confirm view enumerates all affected entries so the user knows
the prune is global, not just the selected one.
Bulk confirm view notes when a prune step will follow the removals,
and the existing flow lets users mix prunable + normal targets without
hitting the dirty-confirmation branch.
Selecting a prunable row in the picker now runs worktree.Prune,
and bulk delete invocations follow the removals with a single
prune call when prunable targets were selected.
runCleanAll built BulkDeletion directly without honoring the prunable
short-circuit added to picker.Bulk(). With Tasks 1-2 making prunable
entries visible to the worktree List, --clean-all would otherwise
try to git worktree remove on missing paths and emit per-entry
errors. Now prunable rows are excluded from Paths and a single
worktree.Prune call runs at the end via RunPrune.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 25, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2c720609-97ab-4e6b-bf99-69294e4e84fa

📥 Commits

Reviewing files that changed from the base of the PR and between 5a17329 and e0190b0.

📒 Files selected for processing (1)
  • internal/picker/style_test.go

📝 Walkthrough

Walkthrough

This PR implements comprehensive support for handling Git worktrees marked prunable (where the directory no longer exists on disk). It adds parsing for the prunable marker in git worktree list --porcelain output, introduces Prune() functions to execute git worktree prune, integrates prunable-aware deletion flows into the CLI with dedicated confirmation prompts, and updates the UI to display prunable status distinctly from other worktree states.

Changes

Cohort / File(s) Summary
Documentation
docs/superpowers/plans/2026-04-25-prunable-worktree-handling.md, docs/superpowers/specs/2026-04-25-prunable-worktree-handling-design.md
Adds execution plan and design specification for prunable worktree feature, detailing data structures, control-flow changes, picker UI/UX updates, confirmation behavior, and test/acceptance criteria.
Git Worktree Parsing & Operations
internal/gitx/worktree.go, internal/gitx/worktree_test.go, internal/worktree/worktree.go, internal/worktree/worktree_test.go
Extends WorktreeEntry with Prunable field and updates ParsePorcelain to detect prunable markers; introduces Prune() function to execute git worktree prune; adds status class and UI string mapping; includes comprehensive parsing and integration tests.
Picker Model & Selection Logic
internal/picker/model.go, internal/picker/model_test.go, internal/picker/update.go, internal/picker/bulk.go, internal/picker/bulk_test.go
Extends Selection with IsPrunable flag and BulkDeletion with RunPrune flag; updates bulk deletion to exclude prunable paths and signal prune invocation; adds HasPrunable helper; includes test coverage for all new flags and logic.
Picker UI & Rendering
internal/picker/view.go, internal/picker/delegate.go, internal/picker/style.go, internal/picker/style_test.go
Adds prunable-specific confirmation views with distinct prompts for single vs. bulk deletion; updates indicator rendering to show "(missing on disk)" for prunable entries; adds prunable badge styling with color and plain-text fallback.
Main CLI Integration
cmd/ccw/main.go
Integrates prunable-aware deletion: single delete of prunable worktree calls worktree.Prune() and resumes picker; bulk delete sets RunPrune flag for prunable targets and executes Prune() after processing paths.

Sequence Diagram

sequenceDiagram
    participant User
    participant Picker UI
    participant Main CLI
    participant Git Worktree

    User->>Picker UI: Select prunable worktree for deletion
    Picker UI->>Picker UI: Detect StatusPrunable status
    Picker UI->>User: Show "Prune worktree...?" confirmation
    User->>Picker UI: Confirm prune action
    Picker UI->>Main CLI: Return Selection with IsPrunable=true
    Main CLI->>Git Worktree: Call worktree.Prune(mainRepo)
    Git Worktree->>Git Worktree: Execute `git worktree prune`
    Git Worktree-->>Main CLI: Success/Error
    Main CLI->>User: Resume picker or return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • PR #5: Introduces initial worktree and gitx packages; this PR extends those code paths with prunable-status parsing, Prune helpers, and picker integration for handling missing worktree directories.
  • PR #4: Establishes the main CLI skeleton and deletion workflows; this PR enhances the main.go deletion logic with prunable-aware control flow and prune execution.

Poem

🐰 A hop, a skip, the worktrees clean,
When prunable they're missing, unseen—
With git's own shears, we trim away,
The phantom branches gone astray. ✨🧹

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding support for prunable worktrees in the picker UI and clean-up flows, which is the primary focus of this comprehensive PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-happy-mantis-9951

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cmd/ccw/main.go (1)

219-242: Minor: confirmCleanAll and the dry-run output use "remove" wording that isn't accurate for prunable targets.

When --clean-all includes prunable entries, the user is shown will remove N worktree(s) and the dry-run prints would remove N worktree(s): — but prunable targets won't actually be removed via git worktree remove; they'll be cleaned by git worktree prune instead. Consider adding a small note (similar to the picker's bulkConfirmView info line at internal/picker/view.go:124) when any target is prunable so the prompt matches the actual operation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/ccw/main.go` around lines 219 - 242, Update the confirmCleanAll prompt
and the dry-run output to distinguish prunable targets from removable ones:
detect when any selected target has StatusPrunable (use the same logic as the
loop that sets BulkDeletion.RunPrune and bulk.Paths in main.go) and change the
message from "will remove N worktree(s)" / "would remove N worktree(s):" to
reflect mixed actions (e.g. "will remove X worktree(s) and prune Y prunable
worktree(s)") or append a short note that prunable entries will be handled by
git worktree prune; modify confirmCleanAll (and any dry-run printing code) to
include this note when any prunable target is present, mirroring the explanatory
line used in picker.bulkConfirmView.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/specs/2026-04-25-prunable-worktree-handling-design.md`:
- Around line 105-106: Summary: The bulk deletion confirmation prompt is missing
an explicit warning that git worktree prune may remove prunable entries that are
not selected. Fix: Update the bulk deletion confirmation copy (the bulk deletion
confirmation prompt / confirmation text displayed before running git worktree
prune) to include a clear, concise warning sentence stating that "git worktree
prune may remove prunable entries that are not currently selected; proceed only
if you understand this may delete additional worktrees." Place this sentence
prominently near the top of the confirmation message so users see it before the
confirm action; ensure the text references git worktree prune explicitly and
appears in the same UI string or function that builds the bulk deletion
confirmation prompt.
- Line 142: The statement claiming “no breaking API change” is too strong
because adding the StatusPrunable member to the exported Status enum can break
consumers that rely on exhaustive switch/case handling; update the wording to
say there is “no signature-level breaking change” (i.e., no change to
function/class/type signatures) and add a short caveat explaining that enum
expansion is a potential source of breaking behavior for exhaustive
pattern-matching in downstream code, recommend consumers handle unknown/new
Status values (or include a default branch) and document the change and
mitigation in the design note alongside references to Status, StatusPrunable,
and WorktreeEntry.

---

Nitpick comments:
In `@cmd/ccw/main.go`:
- Around line 219-242: Update the confirmCleanAll prompt and the dry-run output
to distinguish prunable targets from removable ones: detect when any selected
target has StatusPrunable (use the same logic as the loop that sets
BulkDeletion.RunPrune and bulk.Paths in main.go) and change the message from
"will remove N worktree(s)" / "would remove N worktree(s):" to reflect mixed
actions (e.g. "will remove X worktree(s) and prune Y prunable worktree(s)") or
append a short note that prunable entries will be handled by git worktree prune;
modify confirmCleanAll (and any dry-run printing code) to include this note when
any prunable target is present, mirroring the explanatory line used in
picker.bulkConfirmView.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2a732afe-6358-4161-9928-4560b8271455

📥 Commits

Reviewing files that changed from the base of the PR and between f783ee1 and 5a17329.

📒 Files selected for processing (17)
  • cmd/ccw/main.go
  • docs/superpowers/plans/2026-04-25-prunable-worktree-handling.md
  • docs/superpowers/specs/2026-04-25-prunable-worktree-handling-design.md
  • internal/gitx/worktree.go
  • internal/gitx/worktree_test.go
  • internal/picker/bulk.go
  • internal/picker/bulk_test.go
  • internal/picker/delegate.go
  • internal/picker/model.go
  • internal/picker/model_test.go
  • internal/picker/style.go
  • internal/picker/style_test.go
  • internal/picker/update.go
  • internal/picker/view.go
  • internal/picker/view_test.go
  • internal/worktree/worktree.go
  • internal/worktree/worktree_test.go

Comment on lines +105 to +106
- `git worktree prune` は全 prunable をまとめて掃除するため、選択していない prunable も巻き込まれる可能性があるが、bulk 削除確認画面には全選択 prunable が並んでいる前提で許容する (将来必要なら警告文を追加)。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add an explicit warning in the current bulk confirmation copy

Line 105 acknowledges that git worktree prune can remove unselected prunable entries, but defers warning text to “future.” This should be surfaced now in the confirmation prompt to avoid surprising destructive side effects.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/specs/2026-04-25-prunable-worktree-handling-design.md`
around lines 105 - 106, Summary: The bulk deletion confirmation prompt is
missing an explicit warning that git worktree prune may remove prunable entries
that are not selected. Fix: Update the bulk deletion confirmation copy (the bulk
deletion confirmation prompt / confirmation text displayed before running git
worktree prune) to include a clear, concise warning sentence stating that "git
worktree prune may remove prunable entries that are not currently selected;
proceed only if you understand this may delete additional worktrees." Place this
sentence prominently near the top of the confirmation message so users see it
before the confirm action; ensure the text references git worktree prune
explicitly and appears in the same UI string or function that builds the bulk
deletion confirmation prompt.

## 影響範囲

- 変更ファイル: `internal/gitx/worktree.go`, `internal/worktree/worktree.go`, `internal/picker/{style,delegate,update,model}.go` (および対応するテスト)。
- 公開 API への破壊的変更なし (`WorktreeEntry` / `Status` への追加のみ)。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Soften the “no breaking API change” statement for Status enum expansion

Line 142 says no breaking API change, but adding StatusPrunable can still break downstream exhaustive switch logic in consumers of exported Status. Recommend clarifying this as “no signature-level breaking change” and documenting the enum expansion caveat.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/specs/2026-04-25-prunable-worktree-handling-design.md` at
line 142, The statement claiming “no breaking API change” is too strong because
adding the StatusPrunable member to the exported Status enum can break consumers
that rely on exhaustive switch/case handling; update the wording to say there is
“no signature-level breaking change” (i.e., no change to function/class/type
signatures) and add a short caveat explaining that enum expansion is a potential
source of breaking behavior for exhaustive pattern-matching in downstream code,
recommend consumers handle unknown/new Status values (or include a default
branch) and document the change and mitigation in the design note alongside
references to Status, StatusPrunable, and WorktreeEntry.

…-9951

# Conflicts:
#	internal/picker/style_test.go
#	internal/picker/view_test.go
@tqer39 tqer39 merged commit ece868d into main Apr 25, 2026
8 checks passed
@tqer39 tqer39 deleted the worktree-happy-mantis-9951 branch April 25, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant