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..b3047e780dc 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,12 @@ 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 +453,12 @@ 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 {