* 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)
| 435 | * If .git is a directory, readFile throws EISDIR and we return null. |
| 436 | */ |
| 437 | async function detectWorktreeMainRepoPath(cwd: string): Promise<string | null> { |
| 438 | const gitPath = join(cwd, '.git') |
| 439 | try { |
| 440 | const gitContent = await readFile(gitPath, { encoding: 'utf8' }) |
| 441 | const gitdirMatch = gitContent.match(/^gitdir:\s*(.+)$/m) |
| 442 | if (!gitdirMatch?.[1]) { |
| 443 | return null |
| 444 | } |
| 445 | // gitdir may be relative (rare, but git accepts it) — resolve against cwd |
| 446 | const gitdir = resolve(cwd, gitdirMatch[1].trim()) |
| 447 | // gitdir format: /path/to/main/repo/.git/worktrees/worktree-name |
| 448 | // Match the /.git/worktrees/ segment specifically — indexOf('.git') alone |
| 449 | // would false-match paths like /home/user/.github-projects/... |
| 450 | const marker = `${sep}.git${sep}worktrees${sep}` |
| 451 | const markerIndex = gitdir.lastIndexOf(marker) |
| 452 | if (markerIndex > 0) { |
| 453 | return gitdir.substring(0, markerIndex) |
| 454 | } |
| 455 | return null |
| 456 | } catch { |
| 457 | // Not in a worktree, .git is a directory (EISDIR), or can't read .git file |
| 458 | return null |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | /** |
| 463 | * Check if dependencies are available (memoized) |
no test coverage detected