fix: handle Windows drive-letter paths across the codebase (#148)

* Add metrics collection and terminal performance tests

* fix: handle Windows drive-letter paths across the codebase

Windows paths like C:\Users\foo\project were broken in multiple places:
- agent-storage slugified D:\MyProject as D:-MyProject (illegal colon)
- terminal-manager rejected all non-/ paths as relative
- bootstrap parser misparsed drive colons as TCP host:port
- daemon/client connection helpers misclassified Windows paths
- CLI cwd filtering used hardcoded / separators
- checkout-git worktree detection used hardcoded / in path checks
- worktree archive used split("/").pop() instead of path.basename()

All path helpers now normalize separators and handle Windows
drive letters with case-insensitive comparison where needed.
This commit is contained in:
Mohamed Boudra
2026-03-27 18:17:48 +07:00
committed by GitHub
parent c667aca3e0
commit ca443b3ecf
25 changed files with 980 additions and 49 deletions
+23 -4
View File
@@ -603,20 +603,39 @@ async function getMainRepoRoot(cwd: string): Promise<string> {
});
const worktrees = parseWorktreeList(worktreeOut);
const nonBareNonPaseo = worktrees.filter(
(wt) => !wt.isBare && !wt.path.includes("/.paseo/worktrees/"),
(wt) => !wt.isBare && !isPaseoWorktreePath(wt.path),
);
const childrenOfBareRepo = nonBareNonPaseo.filter((wt) => wt.path.startsWith(normalized + "/"));
const childrenOfBareRepo = nonBareNonPaseo.filter((wt) => isDescendantPath(wt.path, normalized));
const mainChild = childrenOfBareRepo.find((wt) => basename(wt.path) === "main");
return mainChild?.path ?? childrenOfBareRepo[0]?.path ?? nonBareNonPaseo[0]?.path ?? normalized;
}
type GitWorktreeEntry = {
export type GitWorktreeEntry = {
path: string;
branchRef?: string;
isBare?: boolean;
};
function parseWorktreeList(output: string): GitWorktreeEntry[] {
/** Check whether a path contains a `.paseo/worktrees/` segment (both `/` and `\`). */
export function isPaseoWorktreePath(p: string): boolean {
return /[/\\]\.paseo[/\\]worktrees[/\\]/.test(p);
}
/** True when `child` is strictly inside `parent` (handles both `/` and `\`). */
export function isDescendantPath(child: string, parent: string): boolean {
let c = child.replace(/\\/g, "/").replace(/\/+$/, "");
let p = parent.replace(/\\/g, "/").replace(/\/+$/, "");
// Case-insensitive on Windows (drive letter like C: or D:)
if (/^[A-Za-z]:/.test(c) || /^[A-Za-z]:/.test(p)) {
c = c.toLowerCase();
p = p.toLowerCase();
}
if (!c.startsWith(p)) return false;
if (c.length === p.length) return false;
return c[p.length] === "/";
}
export function parseWorktreeList(output: string): GitWorktreeEntry[] {
const entries: GitWorktreeEntry[] = [];
let current: GitWorktreeEntry | null = null;
for (const line of output.split("\n")) {