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] };
}