From 43f9c39dfbdfaa6e5bc260b037a506cd1f56fe6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Andrade?= <95101505+viniciusdsandrade@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:41:07 -0300 Subject: [PATCH] fix(setup): configure an empty Hermes config.yaml (#1983) * fix(setup): treat an empty Hermes config.yaml as a new document YAML parses an empty file to null, so Hermes configuration was checked as an object before the shared empty-file rule and rejected. Setup then failed its readback gate for a zero-byte config.yaml, although a missing file is configured and the Hermes serializer already writes a fresh document for empty original text. Apply the empty-file rule before the Hermes YAML parse, as #1117 did for JSON clients. * test(setup): configure an empty Hermes config.yaml Configure zero-byte and newline-only config.yaml files through the real filesystem workflow, then read the registration back as aligned. --- .../ClientConfigurationDocument.ts | 4 ++-- .../hermesClientConfiguration.test.ts | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/application/ClientConfigurationDocument.ts b/src/application/ClientConfigurationDocument.ts index bad6ee52f..4ad037f07 100644 --- a/src/application/ClientConfigurationDocument.ts +++ b/src/application/ClientConfigurationDocument.ts @@ -104,11 +104,11 @@ const parseDocument = ( // comments, trailing commas, a BOM, or an empty existing file must be // rejected instead of registering into a document Pi cannot load. if (format === "pi") return objectSchema.parse(JSON.parse(text)); - // Hermes stores MCP servers in `mcp_servers` inside a YAML document. - if (format === "hermes") return objectSchema.parse(parseYaml(text)); // An empty file, which some clients create before any server is added, // holds no settings to preserve. if (isEmptyClientConfigurationText(text)) return {}; + // Hermes stores MCP servers in `mcp_servers` inside a YAML document. + if (format === "hermes") return objectSchema.parse(parseYaml(text)); const errors: ParseError[] = []; // Accept a UTF-8 BOM without shifting diagnostics or editing the original text. const jsonText = text.startsWith("\uFEFF") ? ` ${text.slice(1)}` : text; diff --git a/tests/boundary/filesystem/hermesClientConfiguration.test.ts b/tests/boundary/filesystem/hermesClientConfiguration.test.ts index 20db28d9a..f6b3815ee 100644 --- a/tests/boundary/filesystem/hermesClientConfiguration.test.ts +++ b/tests/boundary/filesystem/hermesClientConfiguration.test.ts @@ -185,4 +185,28 @@ mcp_servers: }, }); }); + + it.each(["", "\n"])( + "configures an empty config.yaml %j as a new document", + async (original) => { + const { home, client } = await hermesClient(); + await writeFile(client.configPath, original); + expect( + (await configureClientConfiguration(client, {}, command)).status, + ).toBe("configured"); + expect(parseYaml(await readFile(client.configPath, "utf8"))).toEqual({ + mcp_servers: { + rea: { command: command[0], args: command.slice(1), enabled: true }, + }, + }); + expect( + ( + await readClientRegistrationStatuses(home, undefined, { + platform: process.platform, + environment: {}, + }) + ).find(({ client: name }) => name === "hermes"), + ).toMatchObject({ state: "aligned", command }); + }, + ); });