2026-04-18 16:05:55 +03:00
|
|
|
import type { ProjectEntry } from "@/lib/api/types";
|
|
|
|
|
import type { WorktreeMetadata } from "@/types/worktree";
|
|
|
|
|
|
2026-07-13 09:43:35 +11:00
|
|
|
import { normalizePath } from "@/lib/pathNormalization";
|
|
|
|
|
export const normalizeProjectPath = normalizePath;
|
2026-04-18 16:05:55 +03:00
|
|
|
|
|
|
|
|
export const resolveProjectForDirectory = (
|
|
|
|
|
projects: ProjectEntry[],
|
|
|
|
|
directory: string | null,
|
|
|
|
|
): ProjectEntry | null => {
|
|
|
|
|
const nd = normalizeProjectPath(directory);
|
|
|
|
|
if (!nd) return null;
|
|
|
|
|
let best: ProjectEntry | null = null;
|
|
|
|
|
for (const p of projects) {
|
|
|
|
|
const pp = normalizeProjectPath(p.path);
|
|
|
|
|
if (!pp) continue;
|
|
|
|
|
if (nd !== pp && !nd.startsWith(`${pp}/`)) continue;
|
|
|
|
|
if (!best || pp.length > (normalizeProjectPath(best.path)?.length ?? 0)) best = p;
|
|
|
|
|
}
|
|
|
|
|
return best;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-26 19:27:53 +03:00
|
|
|
const resolveProjectFromWorktreeDirectory = (
|
2026-04-18 16:05:55 +03:00
|
|
|
projects: ProjectEntry[],
|
|
|
|
|
availableWorktreesByProject: Map<string, WorktreeMetadata[]>,
|
|
|
|
|
directory: string | null,
|
2026-09-05 03:04:36 -06:00
|
|
|
): { project: ProjectEntry; matchedWorktreePathLength: number } | null => {
|
2026-04-18 16:05:55 +03:00
|
|
|
const nd = normalizeProjectPath(directory);
|
|
|
|
|
if (!nd) return null;
|
|
|
|
|
let matchedWorktree: WorktreeMetadata | null = null;
|
|
|
|
|
let matchedProjectPath: string | null = null;
|
|
|
|
|
let bestLen = -1;
|
|
|
|
|
for (const [projectPath, worktrees] of availableWorktreesByProject.entries()) {
|
|
|
|
|
for (const wt of worktrees) {
|
|
|
|
|
const wp = normalizeProjectPath(wt.path);
|
|
|
|
|
if (!wp) continue;
|
|
|
|
|
if (nd !== wp && !nd.startsWith(`${wp}/`)) continue;
|
|
|
|
|
if (wp.length > bestLen) {
|
|
|
|
|
bestLen = wp.length;
|
|
|
|
|
matchedWorktree = wt;
|
|
|
|
|
matchedProjectPath = normalizeProjectPath(projectPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!matchedWorktree) return null;
|
|
|
|
|
const candidates = [normalizeProjectPath(matchedWorktree.projectDirectory), matchedProjectPath]
|
|
|
|
|
.filter((v): v is string => Boolean(v));
|
|
|
|
|
for (const c of candidates) {
|
|
|
|
|
const exact = projects.find((p) => normalizeProjectPath(p.path) === c) ?? null;
|
2026-09-05 03:04:36 -06:00
|
|
|
if (exact) return { project: exact, matchedWorktreePathLength: bestLen };
|
2026-04-18 16:05:55 +03:00
|
|
|
const nested = resolveProjectForDirectory(projects, c);
|
2026-09-05 03:04:36 -06:00
|
|
|
if (nested) return { project: nested, matchedWorktreePathLength: bestLen };
|
2026-04-18 16:05:55 +03:00
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const resolveProjectForSessionDirectory = (
|
|
|
|
|
projects: ProjectEntry[],
|
|
|
|
|
availableWorktreesByProject: Map<string, WorktreeMetadata[]>,
|
|
|
|
|
directory: string | null,
|
2026-09-05 03:04:36 -06:00
|
|
|
): ProjectEntry | null => {
|
|
|
|
|
const directProject = resolveProjectForDirectory(projects, directory);
|
|
|
|
|
const worktreeResolution = resolveProjectFromWorktreeDirectory(projects, availableWorktreesByProject, directory);
|
|
|
|
|
if (!directProject) return worktreeResolution?.project ?? null;
|
|
|
|
|
if (!worktreeResolution) return directProject;
|
|
|
|
|
|
|
|
|
|
const directPathLength = normalizeProjectPath(directProject.path)?.length ?? 0;
|
|
|
|
|
return worktreeResolution.matchedWorktreePathLength > directPathLength
|
|
|
|
|
? worktreeResolution.project
|
|
|
|
|
: directProject;
|
|
|
|
|
};
|