fix(agents): resolve typed paths against Windows project roots

This commit is contained in:
l0ng-ai
2026-10-30 11:40:08 +08:00
parent 0d02eb54e2
commit e485037c34
2 changed files with 11 additions and 4 deletions
+2
View File
@@ -288,5 +288,7 @@ describe("typed path references", () => {
expect(resolveTyped("/r/idea", "../kalo/a.ts")).toBe("/r/kalo/a.ts");
expect(resolveTyped("/r/idea", "./src")).toBe("/r/idea/src");
expect(resolveTyped("/r/idea", "/etc/hosts")).toBe("/etc/hosts");
expect(resolveTyped("C:\\r\\idea", "../kalo/a.ts")).toBe("C:/r/kalo/a.ts");
expect(resolveTyped("C:\\r", "../../x")).toBe("C:/x");
});
});
+9 -4
View File
@@ -77,14 +77,19 @@ function inOrder(text: string, query: string): boolean {
/** Whether an `@` query is a typed-out path (`../x`, `./x`, `/x`) rather than a project file search. */
export const isPathQuery = (query: string) => /^(\.\.?(\/|$)|\/)/.test(query);
const isDrive = (part: string | undefined) => !!part && /^[A-Za-z]:$/.test(part);
/** `rel` (as typed after `@`) resolved against `root`, `.` and `..` folded lexically. */
export function resolveTyped(root: string, rel: string): string {
const out: string[] = [];
for (const part of (rel.startsWith("/") ? rel : `${root}/${rel}`).split("/")) {
if (part === "..") out.pop();
else if (part && part !== ".") out.push(part);
const path = rel.startsWith("/") ? rel : `${root.replace(/\\/g, "/")}/${rel}`;
for (const part of path.split("/")) {
if (part === "..") {
// Never fold away a Windows drive (`C:`).
if (out.length > 1 || !isDrive(out[0])) out.pop();
} else if (part && part !== ".") out.push(part);
}
return `/${out.join("/")}`;
return isDrive(out[0]) ? out.join("/") : `/${out.join("/")}`;
}
/** `@rel` with a line range suffix when given ("@src/a.rs:10-20"). */