diff --git a/core/config/profile/LocalProfileLoader.ts b/core/config/profile/LocalProfileLoader.ts index 9c57ae94c9c..bfd4473dc0d 100644 --- a/core/config/profile/LocalProfileLoader.ts +++ b/core/config/profile/LocalProfileLoader.ts @@ -1,4 +1,5 @@ import { ConfigResult, parseConfigYaml } from "@continuedev/config-yaml"; +import fs from "node:fs"; import { ControlPlaneClient } from "../../control-plane/client.js"; import { ContinueConfig, IDE, ILLMLogger } from "../../index.js"; @@ -21,6 +22,7 @@ export default class LocalProfileLoader implements IProfileLoader { | { path: string; content: string } | undefined, ) { + const defaultTitle = "Local Config"; const description: ProfileDescription = { id: overrideAssistantFile?.path ?? LocalProfileLoader.ID, profileType: "local", @@ -32,7 +34,7 @@ export default class LocalProfileLoader implements IProfileLoader { iconUrl: "", title: overrideAssistantFile?.path ? getUriPathBasename(overrideAssistantFile.path) - : "Local Config", + : defaultTitle, errors: undefined, uri: overrideAssistantFile?.path ?? @@ -40,12 +42,21 @@ export default class LocalProfileLoader implements IProfileLoader { rawYaml: undefined, }; this.description = description; - if (overrideAssistantFile?.content) { + let configContent = overrideAssistantFile?.content; + if (configContent === undefined) { try { - const parsedAssistant = parseConfigYaml( - overrideAssistantFile?.content ?? "", - ); - this.description.title = parsedAssistant.name; + configContent = fs.readFileSync(getPrimaryConfigFilePath(), "utf8"); + } catch (e) { + console.error("Failed to read config file: ", e); + } + } + if (configContent) { + try { + const parsedAssistant = parseConfigYaml(configContent); + this.description.title = + parsedAssistant.name?.trim() || + this.description.title || + defaultTitle; } catch (e) { console.error("Failed to parse config file: ", e); } diff --git a/core/config/profile/LocalProfileLoader.vitest.ts b/core/config/profile/LocalProfileLoader.vitest.ts new file mode 100644 index 00000000000..924e8254c45 --- /dev/null +++ b/core/config/profile/LocalProfileLoader.vitest.ts @@ -0,0 +1,53 @@ +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +import { afterEach, describe, expect, it, vi } from "vitest"; + +async function createLoader(configYaml?: string) { + const tempDir = fs.mkdtempSync( + path.join(os.tmpdir(), "continue-local-profile-loader-"), + ); + process.env.CONTINUE_GLOBAL_DIR = tempDir; + + if (configYaml !== undefined) { + fs.writeFileSync(path.join(tempDir, "config.yaml"), configYaml); + } + + vi.resetModules(); + const { default: LocalProfileLoader } = await import("./LocalProfileLoader"); + + return { + tempDir, + loader: new LocalProfileLoader({} as any, {} as any, {} as any), + }; +} + +afterEach(() => { + delete process.env.CONTINUE_GLOBAL_DIR; + vi.restoreAllMocks(); +}); + +describe("LocalProfileLoader", () => { + it("uses name from default local config.yaml for the profile title", async () => { + const { loader, tempDir } = await createLoader( + "name: Custom Local Profile\nversion: '1.0'", + ); + expect(loader.description.title).toBe("Custom Local Profile"); + fs.rmSync(tempDir, { recursive: true, force: true }); + }); + + it("falls back to Local Config when default config.yaml is malformed", async () => { + const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + const { loader, tempDir } = await createLoader("name: ["); + expect(loader.description.title).toBe("Local Config"); + expect(errorSpy).toHaveBeenCalled(); + fs.rmSync(tempDir, { recursive: true, force: true }); + }); + + it("falls back to Local Config when name is missing", async () => { + const { loader, tempDir } = await createLoader("models: []"); + expect(loader.description.title).toBe("Local Config"); + fs.rmSync(tempDir, { recursive: true, force: true }); + }); +});