Merge pull request #9765 from truffle-dev/fix/plan-mode-mkdir-recursive-onedrive-9755

fix(cli): tolerate pre-existing plan directory on OneDrive
This commit is contained in:
Marian Alexandru Alecu
2026-05-06 15:42:25 +03:00
committed by GitHub
2 changed files with 43 additions and 1 deletions
@@ -158,6 +158,16 @@ export namespace KiloSessionPrompt {
}
}
/**
* Ensures the plan file directory exists. Pre-checks with `Filesystem.isDir`
* because `fs.mkdir(recursive: true)` still throws `EEXIST` on Windows
* OneDrive ReparsePoint directories in some Node versions (kilocode#9755).
*/
export async function ensurePlanDir(dir: string) {
if (await Filesystem.isDir(dir)) return
await fs.mkdir(dir, { recursive: true })
}
/**
* Injects plan-specific reminders into the user message when using the plan agent.
* Ensures the plan file directory exists and tells the agent where to write.
@@ -170,7 +180,7 @@ export namespace KiloSessionPrompt {
if (input.agent.name !== "plan") return
const plan = Session.plan(input.session)
const exists = await Filesystem.exists(plan)
if (!exists) await fs.mkdir(path.dirname(plan), { recursive: true })
if (!exists) await ensurePlanDir(path.dirname(plan))
const info = exists
? `A plan file already exists at ${plan}. You can read it and make incremental edits using the edit tool.`
: `No plan file exists yet. You should create your plan at ${plan} using the write tool.`
@@ -0,0 +1,32 @@
import { describe, expect, test } from "bun:test"
import fs from "fs/promises"
import path from "path"
import { KiloSessionPrompt } from "../../src/kilocode/session/prompt"
import { tmpdir } from "../fixture/fixture"
describe("KiloSessionPrompt.ensurePlanDir", () => {
test("creates a missing plan directory", async () => {
await using tmp = await tmpdir({})
const dir = path.join(tmp.path, ".kilo", "plans")
await KiloSessionPrompt.ensurePlanDir(dir)
const stat = await fs.stat(dir)
expect(stat.isDirectory()).toBe(true)
})
test("is idempotent when the directory already exists", async () => {
await using tmp = await tmpdir({})
const dir = path.join(tmp.path, ".kilo", "plans")
await fs.mkdir(dir, { recursive: true })
await expect(KiloSessionPrompt.ensurePlanDir(dir)).resolves.toBeUndefined()
const stat = await fs.stat(dir)
expect(stat.isDirectory()).toBe(true)
})
test("creates intermediate parent directories", async () => {
await using tmp = await tmpdir({})
const dir = path.join(tmp.path, "deep", "nested", ".kilo", "plans")
await KiloSessionPrompt.ensurePlanDir(dir)
const stat = await fs.stat(dir)
expect(stat.isDirectory()).toBe(true)
})
})