import path from 'path'; import { logger } from './logger.js'; import { detectWorktree } from './worktree.js'; /** * Extract project name from working directory path * Handles edge cases: null/undefined cwd, drive roots, trailing slashes * * @param cwd - Current working directory (absolute path) * @returns Project name or "unknown-project" if extraction fails */ export function getProjectName(cwd: string | null | undefined): string { if (!cwd || cwd.trim() === '') { logger.warn('PROJECT_NAME', 'Empty cwd provided, using fallback', { cwd }); return 'unknown-project'; } // Extract basename (handles trailing slashes automatically) const basename = path.basename(cwd); // Edge case: Drive roots on Windows (C:\, J:\) or Unix root (/) // path.basename('C:\') returns '' (empty string) if (basename === '') { // Extract drive letter on Windows, or use 'root' on Unix const isWindows = process.platform === 'win32'; if (isWindows) { const driveMatch = cwd.match(/^([A-Z]):\\/i); if (driveMatch) { const driveLetter = driveMatch[1].toUpperCase(); const projectName = `drive-${driveLetter}`; logger.info('PROJECT_NAME', 'Drive root detected', { cwd, projectName }); return projectName; } } logger.warn('PROJECT_NAME', 'Root directory detected, using fallback', { cwd }); return 'unknown-project'; } return basename; } /** * Project context with worktree awareness */ export interface ProjectContext { /** The current project name (worktree or main repo) */ primary: string; /** Parent project name if in a worktree, null otherwise */ parent: string | null; /** True if currently in a worktree */ isWorktree: boolean; /** All projects to query: [primary] for main repo, [parent, primary] for worktree */ allProjects: string[]; } /** * Get project context with worktree detection. * * When in a worktree, returns both the worktree project name and parent project name * for unified timeline queries. * * @param cwd - Current working directory (absolute path) * @returns ProjectContext with worktree info */ export function getProjectContext(cwd: string | null | undefined): ProjectContext { const primary = getProjectName(cwd); if (!cwd) { return { primary, parent: null, isWorktree: false, allProjects: [primary] }; } const worktreeInfo = detectWorktree(cwd); if (worktreeInfo.isWorktree && worktreeInfo.parentProjectName) { // In a worktree: include parent first for chronological ordering return { primary, parent: worktreeInfo.parentProjectName, isWorktree: true, allProjects: [worktreeInfo.parentProjectName, primary] }; } return { primary, parent: null, isWorktree: false, allProjects: [primary] }; }