fix(lib): 補回搬家時掉了的 rmSync,並讓 origin 檢查只有一份

review 抓到一個沉默的失效:rollback 最後那一手「git 清不乾淨時把目錄本身也清掉」呼叫
rmSync,但把這段從 branch-prep 搬進 lib 時,import 留在了原處。那一行落在自己的
try/catch 裡,所以 ReferenceError 被吞掉——註解承諾的事從來沒發生過,而且測試全綠。
下一次重跑會撞上 WORKTREE_PATH_TAKEN,人看到的是一句與真正原因無關的錯誤。

順手收掉兩支腳本各寫一次的 origin 檢查(只有「為什麼需要它」那一句不同,由呼叫端給),
並把 lib 檔頭「負責六件事」改成七件——工作樹的一生現在整個住在這裡。

正本三處跟著改:
- 查現況那一行補上 --dry-run。pr-watch 在 PR 已終止時會順手清掉工作樹,而「我想看一下
  留言」不該把清理順便做掉——清不清理是使用者的決定。
- 拿掉「使用者堅持要繼續就繼續」:它與同一份正本的邊界(已合併或關閉時不繼續處理留言)
  直接矛盾,而在一棵該被清掉的工作樹上改東西,那些改動不會進到任何 PR 裡。
- worktree-ensure 的指令補上 --path:目標專案多半不是當前目錄,而「不必先手動 cd」
  正是這一步要解決的問題。

議題 #42

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 17:33:44 +08:00
co-authored by Claude Opus 5
parent 0dd8d7b7bb
commit 66064a751e
6 changed files with 63 additions and 24 deletions
+1
View File
@@ -275,6 +275,7 @@ test('工作樹建不起來時,不留下那一支已經建好的分支', async
false,
'也不該留下工作樹的中繼資料',
);
assert.equal(existsSync(join(blocked, 'worktrees')), false, '半途建出來的目錄也不該留下來');
});
// ── 目標分支已存在:接上去,不覆蓋 ─────────────────────────────────
+14
View File
@@ -17,11 +17,25 @@ const locate = steps.slice(0, steps.indexOf('## 2.'));
test('第一步先看 PR 現況,終止狀態就停下來不白做工', () => {
assert.match(locate, /pr-watch\.js/);
assert.match(locate, /terminal/);
assert.match(locate, /不要繼續處理留言/);
assert.match(locate, /白做工/, '要說明為什麼停:那棵工作樹已經該被清掉了');
});
test('查現況帶 --dry-run,看一下留言不該順手清掉工作樹', () => {
const 指令 = locate.slice(locate.indexOf('pr-watch.js'));
assert.match(指令.slice(0, 120), /--dry-run/);
assert.match(locate, /清不清理是使用者的決定/);
});
test('三個建議動作的意思都交代了,不留一個讓 agent 自由發揮', () => {
for (const 值 of ['nothing-to-do', 'cleanup', 'blocked-dirty']) {
assert.match(locate, new RegExp(值));
}
});
test('工作樹由工作包推導,不要求使用者自己切目錄', () => {
assert.match(locate, /worktree-ensure\.js/);
assert.match(locate, /--path/, '目標專案不是當前目錄時要指得出來,否則會找錯 repo');
assert.match(locate, /branch/, '輸入是 pr-watch 給的分支名');
assert.match(locate, /不必也不該由使用者自己去記/);
assert.match(locate, /都在那棵工作樹裡做/);