* Detect if cwd is a git worktree and resolve the main repo path. * Called once during initialize() and cached for the session. * In a worktree, .git is a file (not a directory) containing "gitdir: ...". * If .git is a directory, readFile throws EISDIR and we return null.
(cwd: string)
| 420 | * If .git is a directory, readFile throws EISDIR and we return null. |
| 421 | */ |
| 422 | async function detectWorktreeMainRepoPath(cwd: string): Promise<string | null> { |
| 423 | const gitPath = join(cwd, '.git') |
| 424 | try { |
| 425 | const gitContent = await readFile(gitPath, { encoding: 'utf8' }) |
| 426 | const gitdirMatch = gitContent.match(/^gitdir:\s*(.+)$/m) |
| 427 | if (!gitdirMatch?.[1]) { |
| 428 | return null |
| 429 | } |
| 430 | // gitdir may be relative (rare, but git accepts it) — resolve against cwd |
| 431 | const gitdir = resolve(cwd, gitdirMatch[1].trim()) |
| 432 | // gitdir format: /path/to/main/repo/.git/worktrees/worktree-name |
| 433 | // Match the /.git/worktrees/ segment specifically — indexOf('.git') alone |
| 434 | // would false-match paths like /home/user/.github-projects/... |
| 435 | const marker = `${sep}.git${sep}worktrees${sep}` |
| 436 | const markerIndex = gitdir.lastIndexOf(marker) |
| 437 | if (markerIndex > 0) { |
| 438 | return gitdir.substring(0, markerIndex) |
| 439 | } |
| 440 | return null |
| 441 | } catch { |
| 442 | // Not in a worktree, .git is a directory (EISDIR), or can't read .git file |
| 443 | return null |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | /** |
| 448 | * Check if dependencies are available (memoized) |
no test coverage detected