diff --git a/packages/core/src/global.ts b/packages/core/src/global.ts index 1c7003408e8..645c1a976f3 100644 --- a/packages/core/src/global.ts +++ b/packages/core/src/global.ts @@ -5,6 +5,7 @@ import os from "os" import { Context, Effect, Layer } from "effect" import { Flock } from "./util/flock" import { markNoIndex } from "./kilocode/spotlight" // kilocode_change +import { ensureRealDir } from "./kilocode/global" // kilocode_change import { Flag } from "./flag/flag" const app = "kilo" // kilocode_change @@ -42,12 +43,12 @@ export const Path = paths Flock.setGlobal({ state }) await Promise.all([ - fs.mkdir(Path.data, { recursive: true }), - fs.mkdir(Path.config, { recursive: true }), - fs.mkdir(Path.state, { recursive: true }), - fs.mkdir(Path.tmp, { recursive: true }), - fs.mkdir(Path.log, { recursive: true }), - fs.mkdir(Path.bin, { recursive: true }), + ensureRealDir(Path.data), // kilocode_change + ensureRealDir(Path.config), // kilocode_change + ensureRealDir(Path.state), // kilocode_change + ensureRealDir(Path.tmp), // kilocode_change + ensureRealDir(Path.log), // kilocode_change + ensureRealDir(Path.bin), // kilocode_change ]) // kilocode_change start - keep generated Kilo data out of macOS Spotlight diff --git a/packages/core/src/kilocode/global.ts b/packages/core/src/kilocode/global.ts new file mode 100644 index 00000000000..9c464fa8d58 --- /dev/null +++ b/packages/core/src/kilocode/global.ts @@ -0,0 +1,20 @@ +import fs from "fs/promises" + +/** + * Like `fs.mkdir({ recursive: true })` but also repairs broken symlinks and + * junctions whose target no longer exists (a Windows edge-case where the user + * had a junction at e.g. `~/.kilocode` pointing to a deleted directory). + * + * `fs.mkdir({ recursive: true })` silently no-ops when a junction exists even + * if its target is gone, so subsequent writes inside that path fail with ENOENT. + * We detect this by calling `fs.stat` (which follows the symlink/junction) after + * mkdir: if stat fails the entry is broken and we remove + recreate it. + */ +export async function ensureRealDir(p: string) { + await fs.mkdir(p, { recursive: true }).catch(() => {}) + const ok = await fs.stat(p).then(() => true).catch(() => false) + if (!ok) { + await fs.rm(p, { force: true }).catch(() => {}) + await fs.mkdir(p, { recursive: true }) + } +} diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index e6b94c6c333..bc2a2d5122b 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -544,7 +544,7 @@ export const layer = Layer.effect( ) .pipe( Effect.catchIf( - (e) => e.reason._tag === "PermissionDenied", + (e) => e.reason._tag === "PermissionDenied" || e.reason._tag === "NotFound", // kilocode_change - also ignore NotFound (broken symlink/junction on Windows) () => Effect.void, ), )