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:
rakshith1928
2026-07-23 19:42:01 +05:30
committed by GitHub
parent 182d18bb28
commit 0d830cbd32
3 changed files with 75 additions and 2 deletions
@@ -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
+8 -2
View File
@@ -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