(slug: string)
| 912 | * Falls back to hook-based creation if not in a git repository. |
| 913 | */ |
| 914 | export async function createAgentWorktree(slug: string): Promise<{ |
| 915 | worktreePath: string |
| 916 | worktreeBranch?: string |
| 917 | headCommit?: string |
| 918 | gitRoot?: string |
| 919 | hookBased?: boolean |
| 920 | }> { |
| 921 | validateWorktreeSlug(slug) |
| 922 | |
| 923 | // Try hook-based worktree creation first (allows user-configured VCS) |
| 924 | if (hasWorktreeCreateHook()) { |
| 925 | const hookResult = await executeWorktreeCreateHook(slug) |
| 926 | logForDebugging( |
| 927 | `Created hook-based agent worktree at: ${hookResult.worktreePath}`, |
| 928 | ) |
| 929 | |
| 930 | return { worktreePath: hookResult.worktreePath, hookBased: true } |
| 931 | } |
| 932 | |
| 933 | // Fall back to git worktree |
| 934 | // findCanonicalGitRoot (not findGitRoot) so agent worktrees always land in |
| 935 | // the main repo's canonical worktree root (.ncode/worktrees by default, |
| 936 | // legacy .claude/worktrees if it already exists) even when spawned from |
| 937 | // inside a session worktree — otherwise they nest at |
| 938 | // <worktree>/.ncode/worktrees and the periodic cleanup (which scans the |
| 939 | // canonical root) never finds them. |
| 940 | const gitRoot = findCanonicalGitRoot(getCwd()) |
| 941 | if (!gitRoot) { |
| 942 | throw new Error( |
| 943 | 'Cannot create agent worktree: not in a git repository and no WorktreeCreate hooks are configured. ' + |
| 944 | 'Configure WorktreeCreate/WorktreeRemove hooks in settings.json to use worktree isolation with other VCS systems.', |
| 945 | ) |
| 946 | } |
| 947 | |
| 948 | const { worktreePath, worktreeBranch, headCommit, existed } = |
| 949 | await getOrCreateWorktree(gitRoot, slug) |
| 950 | |
| 951 | if (!existed) { |
| 952 | logForDebugging( |
| 953 | `Created agent worktree at: ${worktreePath} on branch: ${worktreeBranch}`, |
| 954 | ) |
| 955 | await performPostCreationSetup(gitRoot, worktreePath) |
| 956 | } else { |
| 957 | // Bump mtime so the periodic stale-worktree cleanup doesn't consider this |
| 958 | // worktree stale — the fast-resume path is read-only and leaves the original |
| 959 | // creation-time mtime intact, which can be past the 30-day cutoff. |
| 960 | const now = new Date() |
| 961 | await utimes(worktreePath, now, now) |
| 962 | logForDebugging(`Resuming existing agent worktree at: ${worktreePath}`) |
| 963 | } |
| 964 | |
| 965 | return { worktreePath, worktreeBranch, headCommit, gitRoot } |
| 966 | } |
| 967 | |
| 968 | /** |
| 969 | * Remove a worktree created by createAgentWorktree. |
no test coverage detected