()
| 642 | |
| 643 | // Helper to clean up worktree after agent completes |
| 644 | const cleanupWorktreeIfNeeded = async (): Promise<{ |
| 645 | worktreePath?: string; |
| 646 | worktreeBranch?: string; |
| 647 | }> => { |
| 648 | if (!worktreeInfo) return {}; |
| 649 | const { |
| 650 | worktreePath, |
| 651 | worktreeBranch, |
| 652 | headCommit, |
| 653 | gitRoot, |
| 654 | hookBased |
| 655 | } = worktreeInfo; |
| 656 | // Null out to make idempotent — guards against double-call if code |
| 657 | // between cleanup and end of try throws into catch |
| 658 | worktreeInfo = null; |
| 659 | if (hookBased) { |
| 660 | // Hook-based worktrees are always kept since we can't detect VCS changes |
| 661 | logForDebugging(`Hook-based agent worktree kept at: ${worktreePath}`); |
| 662 | return { |
| 663 | worktreePath |
| 664 | }; |
| 665 | } |
| 666 | if (headCommit) { |
| 667 | const changed = await hasWorktreeChanges(worktreePath, headCommit); |
| 668 | if (!changed) { |
| 669 | await removeAgentWorktree(worktreePath, worktreeBranch, gitRoot); |
| 670 | // Clear worktreePath from metadata so resume doesn't try to use |
| 671 | // a deleted directory. Fire-and-forget to match runAgent's |
| 672 | // writeAgentMetadata handling. |
| 673 | void writeAgentMetadata(asAgentId(earlyAgentId), { |
| 674 | agentType: selectedAgent.agentType, |
| 675 | description |
| 676 | }).catch(_err => logForDebugging(`Failed to clear worktree metadata: ${_err}`)); |
| 677 | return {}; |
| 678 | } |
| 679 | } |
| 680 | logForDebugging(`Agent worktree has changes, keeping: ${worktreePath}`); |
| 681 | return { |
| 682 | worktreePath, |
| 683 | worktreeBranch |
| 684 | }; |
| 685 | }; |
| 686 | if (shouldRunAsync) { |
| 687 | const asyncAgentId = earlyAgentId; |
| 688 | const agentBackgroundTask = registerAsyncAgent({ |
no test coverage detected