add agentws prune
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful

Agents leave their managed worktrees behind, and `agentws rm` takes one path at
a time with no idea whether a branch's work is safely upstream, so clearing an
accumulation by hand risks destroying unmerged commits.

- classify every managed worktree: dirty, PR open, upstream, or unproven
- remove only what is safe; delete the local branch only when work is upstream
- prove "upstream" with merge-base and git cherry, so squash merges count
- match a PR by head.label, which survives the branch deletion a merge does
- dry run by default; --yes applies, --keep-branches spares every branch
- read the Gitea path from origin's URL rather than assuming the owner
This commit is contained in:
2026-09-09 23:41:09 +10:00
parent 5c0eb1e899
commit 4bbeaae8f0
11 changed files with 1156 additions and 2 deletions
+25
View File
@@ -104,6 +104,11 @@ agentws list
agentws rm benvin/my-change
agentws rm ~/.cache/agentws/argocd-apps__benvin-my-change --delete-branch
# Classify every managed worktree; dry run unless --yes is given
agentws prune
agentws prune --yes
agentws prune --yes --keep-branches
# Remove every managed worktree and prune each source repo
agentws clean
@@ -111,6 +116,26 @@ agentws clean
agentws token
```
### prune
`agentws prune` decides, per worktree, whether its work is safely upstream:
| Signal (first match wins) | Verdict |
|---|---|
| uncommitted or untracked changes | keep |
| branch has an open PR | keep |
| tip contained in `origin/<default>`, or `git cherry` finds no unmerged patch, or its PR is merged | remove worktree + local branch |
| PR closed unmerged and the branch is still on origin | remove worktree + local branch |
| anything else | remove worktree, keep the branch |
The git checks are authoritative and work offline: these repos squash-merge, so
a merged branch's commits carry different SHAs upstream and a plain
`rev-list origin/<default>..HEAD` count proves nothing. Gitea PR state only adds
to the git answer — when it cannot be reached, prune says so and never deletes a
branch it could not prove. Matching a branch to its PR uses `head.label`, since
Gitea rewrites `head.ref` to `refs/pull/<n>/head` once the branch is deleted on
merge.
### Auth / credential-helper design
Gitea tokens minted from Vault are short-lived (~1h), so `agentws` never