From 65d7e7863e48832343df4f9e2acea66aa0b06dc1 Mon Sep 17 00:00:00 2001 From: CreatorGhost Date: Sun, 12 Jul 2026 00:57:26 +0530 Subject: [PATCH] fix(config): write valid JSON when auto-adding $schema to an empty config Ported from upstream anomalyco/opencode#36380. --- packages/opencode/src/config/config.ts | 2 +- packages/opencode/test/config/config.test.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 86238f1a844c..8e5c4fc6df81 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -230,7 +230,7 @@ const layer = Layer.effect( yield* Effect.promise(() => resolveLoadedPlugins(data, options.path)) if (!data.$schema) { data.$schema = "https://opencode.ai/config.json" - const updated = text.replace(/^\s*\{/, '{\n "$schema": "https://opencode.ai/config.json",') + const updated = patchJsonc(text, { $schema: "https://opencode.ai/config.json" }) yield* fs.writeFileString(options.path, updated).pipe(Effect.catch(() => Effect.void)) } return data diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index e281831ddfa8..efae550dcb45 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -528,6 +528,19 @@ it.instance("preserves env variables when adding $schema to config", () => ), ) +it.instance("writes valid JSON when adding $schema to a config with no other keys", () => + Effect.gen(function* () { + const test = yield* TestInstance + // Empty object without $schema: auto-add must not leave a trailing comma (invalid strict JSON). + yield* FSUtil.use.writeWithDirs(path.join(test.directory, "opencode.json"), "{}") + yield* Config.use.get() + + const content = yield* FSUtil.use.readFileString(path.join(test.directory, "opencode.json")) + expect(() => JSON.parse(content)).not.toThrow() + expect(JSON.parse(content).$schema).toBe("https://opencode.ai/config.json") + }), +) + it.instance("handles file inclusion substitution", () => Effect.gen(function* () { const test = yield* TestInstance