From 507fd55969f7db99b7faf868185085bfc13acd61 Mon Sep 17 00:00:00 2001 From: mathias-heide Date: Fri, 11 Sep 2026 10:35:39 -0700 Subject: [PATCH] fix(setup): write Goose/Hermes YAML in block style An empty config file (and --print) started from parseDocument("{}"), so the root was a flow mapping and the whole file rendered as one {...} blob. Start from a fresh Document instead; existing files keep their style as before. Co-Authored-By: Claude Fable 5.1 --- src/installer/agent-config.ts | 8 +++++--- src/installer/agent-table.test.ts | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/installer/agent-config.ts b/src/installer/agent-config.ts index f23d11f..a3779f8 100644 --- a/src/installer/agent-config.ts +++ b/src/installer/agent-config.ts @@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "fs"; import { copyFile, mkdir, readFile, writeFile } from "fs/promises"; import { dirname, join, resolve } from "path"; import { fileURLToPath } from "url"; -import { isMap, parseDocument } from "yaml"; +import { Document, isMap, parseDocument } from "yaml"; import { PACKAGE_ROOT } from "../core/package-root.js"; import { agentAliasMap, @@ -217,7 +217,7 @@ export function renderConfigSnippet( return renderJsonFile(geminiExtensionManifest(server, readBundledGeminiManifestSync())); } if (format === "yaml-goose" || format === "yaml-hermes") { - const doc = parseDocument("{}"); + const doc = new Document({}); doc.setIn([topLevelKey(format), SUMMER_MCP_SERVER_NAME], doc.createNode(mcpEntry(format, server))); return doc.toString(); } @@ -360,7 +360,9 @@ async function upsertYamlConfig( write: boolean ): Promise<{ changed: boolean }> { const current = await readTextFileIfExists(path); - const doc = parseDocument(current.trim() === "" ? "{}" : current); + // An empty file starts from a fresh block-style document; parseDocument("{}") + // would make the root a flow mapping and the whole file would render as {}. + const doc = current.trim() === "" ? new Document({}) : parseDocument(current); if (doc.errors.length > 0) { throw new Error(`Could not parse YAML in ${path}: ${doc.errors[0].message}`); } diff --git a/src/installer/agent-table.test.ts b/src/installer/agent-table.test.ts index 044389d..769a5f8 100644 --- a/src/installer/agent-table.test.ts +++ b/src/installer/agent-table.test.ts @@ -274,6 +274,17 @@ describe("config file shapes", () => { }); }); + it("yaml: a fresh file and the printed snippet are block-style, not a flow mapping", async () => { + const snippet = renderConfigSnippet("goose", { command: "npx", args: NPX_ARGS }); + expect(snippet.startsWith("extensions:\n summer-engine:\n")).toBe(true); + expect(snippet.startsWith("{")).toBe(false); + const dir = tmp(); + const path = join(dir, "config.yaml"); + await configureAgentMcp({ agent: "hermes", scope: "user", env: { SUMMER_HERMES_CONFIG_FILE: path } as NodeJS.ProcessEnv }); + const text = readFileSync(path, "utf-8"); + expect(text.startsWith("mcp_servers:\n summer-engine:\n command: npx\n")).toBe(true); + }); + it("yaml-goose: second run is a no-op", async () => { const dir = tmp(); const path = join(dir, "config.yaml");