fix(FN-XXX): harden windows path handling
This commit is contained in:
@@ -433,5 +433,20 @@ describe("HybridExecutor", () => {
|
||||
executor.updateProject("non-existent", { maxConcurrent: 4 })
|
||||
).rejects.toThrow("Runtime not found");
|
||||
});
|
||||
|
||||
it("reuses the registered project path when isolation mode changes without an explicit working directory", async () => {
|
||||
const manager = mockProjectManagerInstances[0];
|
||||
manager?.addProject.mockClear();
|
||||
|
||||
await executor.updateProject("proj_test123", { isolationMode: "child-process" });
|
||||
|
||||
expect(mockCentralCore.getProject).toHaveBeenCalledWith("proj_test123");
|
||||
expect(manager?.removeProject).toHaveBeenCalledWith("proj_test123");
|
||||
expect(manager?.addProject).toHaveBeenCalledWith(expect.objectContaining({
|
||||
projectId: "proj_test123",
|
||||
workingDirectory: "/tmp/test-project",
|
||||
isolationMode: "child-process",
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { describeModel, compactSessionContext, COMPACTION_FALLBACK_INSTRUCTIONS, createFnAgent, promptWithFallback, type AgentOptions } from "../pi.js";
|
||||
import { describeModel, compactSessionContext, COMPACTION_FALLBACK_INSTRUCTIONS, createFnAgent, getProjectRootFromWorktree, promptWithFallback, type AgentOptions } from "../pi.js";
|
||||
import { createAgentSession, type AgentSession } from "@mariozechner/pi-coding-agent";
|
||||
import { piLog } from "../logger.js";
|
||||
|
||||
@@ -78,6 +78,18 @@ vi.mock("@mariozechner/pi-coding-agent", () => ({
|
||||
let resolveSessionSkillsMock: ReturnType<typeof vi.fn>;
|
||||
let createSkillsOverrideFromSelectionMock: ReturnType<typeof vi.fn>;
|
||||
|
||||
describe("getProjectRootFromWorktree", () => {
|
||||
it("detects POSIX worktree paths", () => {
|
||||
expect(getProjectRootFromWorktree("/repo/.worktrees/fn-001")).toBe("/repo");
|
||||
expect(getProjectRootFromWorktree("/repo/.worktrees/fn-001/src/file.ts")).toBe("/repo");
|
||||
});
|
||||
|
||||
it("detects Windows worktree paths", () => {
|
||||
expect(getProjectRootFromWorktree("C:\\repo\\.worktrees\\fn-001")).toBe("C:\\repo");
|
||||
expect(getProjectRootFromWorktree("C:\\repo\\.worktrees\\fn-001\\src\\file.ts")).toBe("C:\\repo");
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize mocks before first test
|
||||
beforeEach(() => {
|
||||
// Access mocks from the mocked module
|
||||
|
||||
@@ -274,13 +274,19 @@ export class HybridExecutor extends EventEmitter<HybridExecutorEvents> {
|
||||
`Isolation mode changed for ${projectId}: ${currentMode} → ${config.isolationMode}`
|
||||
);
|
||||
|
||||
const project = await this.centralCore.getProject(projectId);
|
||||
const workingDirectory = config.workingDirectory ?? project?.path;
|
||||
if (!workingDirectory) {
|
||||
throw new Error(`Project not found in CentralCore: ${projectId}`);
|
||||
}
|
||||
|
||||
// Stop old runtime
|
||||
await this.projectManager.removeProject(projectId);
|
||||
|
||||
// Get the full current config
|
||||
const fullConfig: ProjectRuntimeConfig = {
|
||||
projectId,
|
||||
workingDirectory: config.workingDirectory ?? "/tmp",
|
||||
workingDirectory,
|
||||
isolationMode: config.isolationMode,
|
||||
maxConcurrent: config.maxConcurrent ?? 2,
|
||||
maxWorktrees: config.maxWorktrees ?? 4,
|
||||
|
||||
@@ -791,7 +791,7 @@ async function registerExtensionProviders(cwd: string, modelRegistry: ModelRegis
|
||||
* `/project/.worktrees/fn-001/src/file.ts` → `/project`
|
||||
* `/project` → null (not a worktree)
|
||||
*/
|
||||
function getProjectRootFromWorktree(cwd: string): string | null {
|
||||
export function getProjectRootFromWorktree(cwd: string): string | null {
|
||||
// Match paths like:
|
||||
// /project/.worktrees/task-id
|
||||
// /project/.worktrees/task-id/src/file.ts
|
||||
|
||||
Reference in New Issue
Block a user