From d34e6da082f53f482df45b96dd6c37cc0ea4e664 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 24 Mar 2026 12:51:33 -0700 Subject: [PATCH 1/2] fix(core): pass config name through ConfigResult instead of re-reading file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework per reviewer feedback: instead of reading config.yaml separately in LocalProfileLoader's constructor, propagate the name field through ConfigResult from the existing config loading pipeline. Changes: - Add optional configName to ConfigResult interface - Populate configName in unroll.ts where AssistantUnrolled is built - Propagate through loadContinueConfigFromYaml → doLoadConfig - LocalProfileLoader now reads configName from doLoadConfig result - Remove duplicate fs.readFileSync + parseConfigYaml from constructor - Works in WSL and other environments where file paths may differ Addresses: https://github.com/continuedev/continue/issues/11061 --- core/config/profile/LocalProfileLoader.ts | 24 +++++------- .../profile/LocalProfileLoader.vitest.ts | 39 +++++++++++++++++++ core/config/profile/doLoadConfig.ts | 6 ++- core/config/yaml/loadYaml.ts | 2 + packages/config-yaml/src/load/unroll.ts | 12 +++++- packages/config-yaml/src/validation.ts | 2 + 6 files changed, 67 insertions(+), 18 deletions(-) diff --git a/core/config/profile/LocalProfileLoader.ts b/core/config/profile/LocalProfileLoader.ts index 1ddde69ad8c..d72e8c4cebb 100644 --- a/core/config/profile/LocalProfileLoader.ts +++ b/core/config/profile/LocalProfileLoader.ts @@ -1,4 +1,4 @@ -import { ConfigResult, parseConfigYaml } from "@continuedev/config-yaml"; +import { ConfigResult } from "@continuedev/config-yaml"; import { ControlPlaneClient } from "../../control-plane/client.js"; import { ContinueConfig, IDE, ILLMLogger } from "../../index.js"; @@ -13,6 +13,8 @@ import { IProfileLoader } from "./IProfileLoader.js"; export default class LocalProfileLoader implements IProfileLoader { static ID = "local"; + description: ProfileDescription; + constructor( private ide: IDE, private controlPlaneClient: ControlPlaneClient, @@ -21,7 +23,7 @@ export default class LocalProfileLoader implements IProfileLoader { | { path: string; content: string } | undefined, ) { - const description: ProfileDescription = { + this.description = { id: overrideAssistantFile?.path ?? LocalProfileLoader.ID, profileType: "local", fullSlug: { @@ -39,19 +41,7 @@ export default class LocalProfileLoader implements IProfileLoader { localPathToUri(getPrimaryConfigFilePath()), rawYaml: undefined, }; - this.description = description; - if (overrideAssistantFile?.content) { - try { - const parsedAssistant = parseConfigYaml( - overrideAssistantFile?.content ?? "", - ); - this.description.title = parsedAssistant.name; - } catch (e) { - console.error("Failed to parse config file: ", e); - } - } } - description: ProfileDescription; async doLoadConfig(): Promise> { const result = await doLoadConfig({ @@ -72,6 +62,12 @@ export default class LocalProfileLoader implements IProfileLoader { this.description.errors = result.errors; + // Use the config name from the loaded config (avoids duplicate file reads + // and works in environments like WSL where paths may differ) + if (result.configName) { + this.description.title = result.configName; + } + return result; } diff --git a/core/config/profile/LocalProfileLoader.vitest.ts b/core/config/profile/LocalProfileLoader.vitest.ts index 0253936aef3..7fdf3d18836 100644 --- a/core/config/profile/LocalProfileLoader.vitest.ts +++ b/core/config/profile/LocalProfileLoader.vitest.ts @@ -67,4 +67,43 @@ describe("LocalProfileLoader", () => { }), ); }); + + it("should update title from configName when available", async () => { + mockDoLoadConfig.mockResolvedValueOnce({ + config: {}, + errors: [], + configLoadInterrupted: false, + configName: "My Custom Config", + }); + + const loader = new LocalProfileLoader( + testIde, + controlPlaneClient, + llmLogger, + ); + + expect(loader.description.title).toBe("Local Config"); + + await loader.doLoadConfig(); + + expect(loader.description.title).toBe("My Custom Config"); + }); + + it("should keep default title when configName is not set", async () => { + mockDoLoadConfig.mockResolvedValueOnce({ + config: {}, + errors: [], + configLoadInterrupted: false, + }); + + const loader = new LocalProfileLoader( + testIde, + controlPlaneClient, + llmLogger, + ); + + await loader.doLoadConfig(); + + expect(loader.description.title).toBe("Local Config"); + }); }); diff --git a/core/config/profile/doLoadConfig.ts b/core/config/profile/doLoadConfig.ts index bcfec2ff8ab..5c24fcc2ff8 100644 --- a/core/config/profile/doLoadConfig.ts +++ b/core/config/profile/doLoadConfig.ts @@ -113,6 +113,7 @@ export default async function doLoadConfig(options: { let newConfig: ContinueConfig | undefined; let errors: ConfigValidationError[] | undefined; let configLoadInterrupted = false; + let configName: string | undefined; const hasPreReadContent = packageIdentifier.uriType === "file" && @@ -138,6 +139,7 @@ export default async function doLoadConfig(options: { newConfig = result.config; errors = result.errors; configLoadInterrupted = result.configLoadInterrupted; + configName = result.configName; } else { const result = await loadContinueConfigFromJson( ide, @@ -154,7 +156,7 @@ export default async function doLoadConfig(options: { } if (configLoadInterrupted || !newConfig) { - return { errors, config: newConfig, configLoadInterrupted: true }; + return { errors, config: newConfig, configLoadInterrupted: true, configName }; } // TODO using config result but result with non-fatal errors is an antipattern? @@ -446,7 +448,7 @@ export default async function doLoadConfig(options: { controlPlaneProxyInfo, ); - return { config: newConfig, errors, configLoadInterrupted: false }; + return { config: newConfig, errors, configLoadInterrupted: false, configName }; } // Pass ControlPlaneProxyInfo to objects that need it diff --git a/core/config/yaml/loadYaml.ts b/core/config/yaml/loadYaml.ts index 40278fdbe47..f7a82635b6e 100644 --- a/core/config/yaml/loadYaml.ts +++ b/core/config/yaml/loadYaml.ts @@ -466,6 +466,7 @@ export async function loadContinueConfigFromYaml(options: { errors: configYamlResult.errors, config: undefined, configLoadInterrupted: true, + configName: configYamlResult.configName, }; } @@ -495,5 +496,6 @@ export async function loadContinueConfigFromYaml(options: { config: withShared, errors: [...(configYamlResult.errors ?? []), ...localErrors], configLoadInterrupted: false, + configName: configYamlResult.configName, }; } diff --git a/packages/config-yaml/src/load/unroll.ts b/packages/config-yaml/src/load/unroll.ts index fd0300a2232..44285d7beb9 100644 --- a/packages/config-yaml/src/load/unroll.ts +++ b/packages/config-yaml/src/load/unroll.ts @@ -310,10 +310,12 @@ export async function unrollAssistantFromContent( }); if (!options.renderSecrets) { + const parsed = parseAssistantUnrolled(templatedYaml); return { - config: parseAssistantUnrolled(templatedYaml), + config: parsed, errors: [], configLoadInterrupted: false, + configName: parsed?.name || undefined, }; } @@ -333,7 +335,12 @@ export async function unrollAssistantFromContent( options.onPremProxyUrl, ); - return { config: renderedConfig, errors, configLoadInterrupted }; + return { + config: renderedConfig, + errors, + configLoadInterrupted, + configName: renderedConfig?.name || undefined, + }; } function isPackageAllowed( @@ -678,6 +685,7 @@ export async function unrollBlocks( config: undefined, errors: undefined, configLoadInterrupted: false, + configName: unrolledAssistant.name || undefined, }; configResult.config = unrolledAssistant; if (errors.length > 0) { diff --git a/packages/config-yaml/src/validation.ts b/packages/config-yaml/src/validation.ts index b6378f12e82..16f3a7c4eb6 100644 --- a/packages/config-yaml/src/validation.ts +++ b/packages/config-yaml/src/validation.ts @@ -10,6 +10,8 @@ export interface ConfigResult { config: T | undefined; errors: ConfigValidationError[] | undefined; configLoadInterrupted: boolean; + /** Optional display name from config.yaml `name` field */ + configName?: string; } function containsUnicode(str: string): boolean { From d9dbe38dae8c1814a922893064daee12e06ad4bf Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 24 Mar 2026 12:57:04 -0700 Subject: [PATCH 2/2] style: fix prettier formatting in doLoadConfig.ts --- core/config/profile/doLoadConfig.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/config/profile/doLoadConfig.ts b/core/config/profile/doLoadConfig.ts index 5c24fcc2ff8..b3047e780dc 100644 --- a/core/config/profile/doLoadConfig.ts +++ b/core/config/profile/doLoadConfig.ts @@ -156,7 +156,12 @@ export default async function doLoadConfig(options: { } if (configLoadInterrupted || !newConfig) { - return { errors, config: newConfig, configLoadInterrupted: true, configName }; + return { + errors, + config: newConfig, + configLoadInterrupted: true, + configName, + }; } // TODO using config result but result with non-fatal errors is an antipattern? @@ -448,7 +453,12 @@ export default async function doLoadConfig(options: { controlPlaneProxyInfo, ); - return { config: newConfig, errors, configLoadInterrupted: false, configName }; + return { + config: newConfig, + errors, + configLoadInterrupted: false, + configName, + }; } // Pass ControlPlaneProxyInfo to objects that need it