mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:44:06 +08:00
fix: avoid write-on-read mutation of config file (#11940)
* fix: avoid write-on-read mutation of config file * fix: update test description for schema injection case * Apply suggestion from @kilo-code-bot[bot] Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com> * chore: add changeset * fix(cli): preserve as first config property via getInsertionIndex * fix(cli): close missing brackets in config test preventing syntax error * Removed test for preserving environment variables when adding $schema to config. Removed test for preserving environment variables when adding $schema to config. * fix(cli): add ordering assertions to existing injection tests * docs(changeset): clarify per-load churn scope for comment-first JSONC * test(cli): use FSUtil in config schema tests after upstream refactor * chore: combine changeset lines --------- Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Fix: inject `$schema` into config files using jsonc-parser, avoiding write-on-read for comment-first JSONC and preventing unnecessary file rewrites on every load
|
||||
@@ -319,9 +319,15 @@ export const layer = Layer.effect(
|
||||
if (!data.$schema) {
|
||||
// kilocode_change start
|
||||
data.$schema = "https://app.kilo.ai/config.json"
|
||||
const updated = text.replace(/^\s*\{/, '{\n "$schema": "https://app.kilo.ai/config.json",')
|
||||
const edits = modify(text, ["$schema"], "https://app.kilo.ai/config.json", {
|
||||
formattingOptions: { insertSpaces: true, tabSize: 2 },
|
||||
getInsertionIndex: () => 0,
|
||||
})
|
||||
const updated = applyEdits(text, edits)
|
||||
if (updated !== text) {
|
||||
yield* fs.writeFileString(options.path, updated).pipe(Effect.catch(() => Effect.void))
|
||||
}
|
||||
// kilocode_change end
|
||||
yield* fs.writeFileString(options.path, updated).pipe(Effect.catch(() => Effect.void))
|
||||
}
|
||||
return data
|
||||
})
|
||||
|
||||
@@ -578,6 +578,68 @@ it.instance("rejects environment variable substitution in project config", () =>
|
||||
),
|
||||
)
|
||||
|
||||
it.instance("injects $schema into config without existing schema", () =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
// Config without $schema - should trigger auto-add
|
||||
yield* FSUtil.use.writeWithDirs(
|
||||
path.join(test.directory, "kilo.json"),
|
||||
JSON.stringify({ username: "test-user" }),
|
||||
)
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.username).toBe("test-user")
|
||||
expect(config.$schema).toBe("https://app.kilo.ai/config.json")
|
||||
|
||||
// Read the file to verify $schema was injected
|
||||
const content = yield* FSUtil.use.readFileString(path.join(test.directory, "kilo.json"))
|
||||
expect(content).toContain('"$schema": "https://app.kilo.ai/config.json"')
|
||||
const schemaIndex = content.indexOf('"$schema"')
|
||||
const usernameIndex = content.indexOf('"username"')
|
||||
expect(schemaIndex).toBeLessThan(usernameIndex)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("injects $schema into comment-first JSONC config", () =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
// Config with leading comment - regex-based injection would fail
|
||||
yield* FSUtil.use.writeWithDirs(
|
||||
path.join(test.directory, "kilo.jsonc"),
|
||||
'// project config\n{\n "model": "test/model"\n}\n',
|
||||
)
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.model).toBe("test/model")
|
||||
expect(config.$schema).toBe("https://app.kilo.ai/config.json")
|
||||
|
||||
// Read the file to verify $schema was injected correctly
|
||||
const content = yield* FSUtil.use.readFileString(path.join(test.directory, "kilo.jsonc"))
|
||||
expect(content).toContain('"$schema": "https://app.kilo.ai/config.json"')
|
||||
expect(content).toContain("// project config")
|
||||
const schemaIndex = content.indexOf('"$schema"')
|
||||
const modelIndex = content.indexOf('"model"')
|
||||
expect(schemaIndex).toBeLessThan(modelIndex)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("does not write config when $schema already present", () =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
const filepath = path.join(test.directory, "kilo.json")
|
||||
// Config already has $schema - should not rewrite file
|
||||
yield* FSUtil.use.writeWithDirs(
|
||||
filepath,
|
||||
JSON.stringify({ $schema: "https://app.kilo.ai/config.json", username: "test-user" }),
|
||||
)
|
||||
const before = yield* Effect.promise(() => fs.stat(filepath))
|
||||
|
||||
const config = yield* Config.use.get()
|
||||
expect(config.username).toBe("test-user")
|
||||
|
||||
const after = yield* Effect.promise(() => fs.stat(filepath))
|
||||
expect(after.mtimeMs).toBe(before.mtimeMs)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("allows {file:} that stays inside the project root", () =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
|
||||
Reference in New Issue
Block a user