From e74b4a119fc1781d16419033356f8aba64cb12b7 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sun, 16 Nov 2025 13:15:56 -0800 Subject: [PATCH 1/5] feat: support paths and file URLs for agent file loadinggs --- .../cli/src/services/AgentFileService.ts | 53 +++++++++++++------ 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/extensions/cli/src/services/AgentFileService.ts b/extensions/cli/src/services/AgentFileService.ts index 81dd0e4df40..9d7dd6d198f 100644 --- a/extensions/cli/src/services/AgentFileService.ts +++ b/extensions/cli/src/services/AgentFileService.ts @@ -1,8 +1,11 @@ import { + AgentFile, + parseAgentFile, parseAgentFileRules, parseAgentFileTools, } from "@continuedev/config-yaml"; - +import fs from "fs"; +import path from "path"; import { agentFileProcessor, loadModelFromHub, @@ -10,6 +13,8 @@ import { } from "../hubLoader.js"; import { logger } from "../util/logger.js"; +import { getErrorString } from "src/util/error.js"; +import { fileURLToPath } from "url"; import { BaseService, ServiceWithDependencies } from "./BaseService.js"; import { serviceContainer } from "./ServiceContainer.js"; import { @@ -44,37 +49,55 @@ export class AgentFileService return [SERVICE_NAMES.AUTH, SERVICE_NAMES.API_CLIENT]; } + private async getAgentFile(agentPath: string): Promise { + try { + const parts = agentPath.split("/"); + if (parts.length === 2 && parts[0] && parts[1]) { + try { + return await loadPackageFromHub(agentPath, agentFileProcessor); + } catch (e) { + logger.info( + `Failed to load agent file from slug-like path: ${agentPath}`, + ); + // slug COULD be path, fall back to relative path + } + } + if (agentPath.startsWith("file:/")) { + const path = fileURLToPath(agentPath); + const content = fs.readFileSync(path, "utf-8"); + return parseAgentFile(content); + } + const resolvedPath = path.resolve(agentPath); + const content = fs.readFileSync(resolvedPath, "utf-8"); + return parseAgentFile(content); + } catch (e) { + throw new Error( + `Failed to load agent from ${agentPath}: ${getErrorString(e)}`, + ); + } + } + /** * Initialize the agent file service with a hub slug */ async doInitialize( - agentFileSlug: string | undefined, + agentFilePath: string | undefined, authServiceState: AuthServiceState, apiClientState: ApiClientServiceState, ): Promise { - if (!agentFileSlug) { + if (!agentFilePath) { return { ...EMPTY_AGENT_FILE_STATE, }; } try { - const parts = agentFileSlug.split("/"); - if (parts.length !== 2) { - throw new Error( - `Invalid agent slug format. Expected "owner/package", got: ${agentFileSlug}`, - ); - } - - const agentFile = await loadPackageFromHub( - agentFileSlug, - agentFileProcessor, - ); + const agentFile = await this.getAgentFile(agentFilePath); // Set the basic agent file state this.setState({ agentFile, - slug: agentFileSlug, + slug: agentFilePath, }); if (agentFile.model) { From 0cb293a35110a43ae8e59d2cc21688166b63a3d8 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sun, 16 Nov 2025 13:18:18 -0800 Subject: [PATCH 2/5] fix: cleanup --- extensions/cli/src/services/AgentFileService.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/extensions/cli/src/services/AgentFileService.ts b/extensions/cli/src/services/AgentFileService.ts index 9d7dd6d198f..896d6ff60a3 100644 --- a/extensions/cli/src/services/AgentFileService.ts +++ b/extensions/cli/src/services/AgentFileService.ts @@ -62,12 +62,9 @@ export class AgentFileService // slug COULD be path, fall back to relative path } } - if (agentPath.startsWith("file:/")) { - const path = fileURLToPath(agentPath); - const content = fs.readFileSync(path, "utf-8"); - return parseAgentFile(content); - } - const resolvedPath = path.resolve(agentPath); + const resolvedPath = agentPath.startsWith("file:/") + ? fileURLToPath(agentPath) + : path.resolve(agentPath); const content = fs.readFileSync(resolvedPath, "utf-8"); return parseAgentFile(content); } catch (e) { From d80c36438f36cd66b00b48364c0e01f451dfc4a5 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sun, 16 Nov 2025 13:36:16 -0800 Subject: [PATCH 3/5] text: agent file paths --- .../cli/src/services/AgentFileService.test.ts | 432 +++++++++++++++++- .../cli/src/services/AgentFileService.ts | 4 +- 2 files changed, 428 insertions(+), 8 deletions(-) diff --git a/extensions/cli/src/services/AgentFileService.test.ts b/extensions/cli/src/services/AgentFileService.test.ts index 1100095c15e..e88b41eabc7 100644 --- a/extensions/cli/src/services/AgentFileService.test.ts +++ b/extensions/cli/src/services/AgentFileService.test.ts @@ -1,3 +1,6 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { @@ -5,6 +8,40 @@ import { EMPTY_AGENT_FILE_STATE, } from "./AgentFileService.js"; +// Mock fs module +vi.mock("fs", async () => { + const actual = await vi.importActual("fs"); + return { + ...actual, + default: { + readFileSync: vi.fn(), + }, + readFileSync: vi.fn(), + }; +}); + +// Mock path module +vi.mock("path", async () => { + const actual = await vi.importActual("path"); + return { + ...actual, + default: { + ...actual, + resolve: vi.fn(), + }, + resolve: vi.fn(), + }; +}); + +// Mock url module +vi.mock("url", async () => { + const actual = await vi.importActual("url"); + return { + ...actual, + fileURLToPath: vi.fn(), + }; +}); + // Mock the hubLoader module vi.mock("../hubLoader.js", () => ({ loadPackageFromHub: vi.fn(), @@ -28,6 +65,7 @@ vi.mock("../util/logger.js", () => ({ debug: vi.fn(), error: vi.fn(), warn: vi.fn(), + info: vi.fn(), }, })); @@ -65,6 +103,9 @@ describe("AgentFileService", () => { let agentFileService: AgentFileService; let mockLoadPackageFromHub: any; let mockLoadModelFromHub: any; + let mockReadFileSync: any; + let mockPathResolve: any; + let mockFileURLToPath: any; const mockAgentFile = { name: "Test Agent File", @@ -102,15 +143,23 @@ describe("AgentFileService", () => { mockLoadPackageFromHub = hubLoaderModule.loadPackageFromHub as any; mockLoadModelFromHub = hubLoaderModule.loadModelFromHub as any; + // Get fs mocks + mockReadFileSync = vi.mocked(fs.readFileSync); + mockPathResolve = vi.mocked(path.resolve); + mockFileURLToPath = vi.mocked(fileURLToPath); + // Create service instance agentFileService = new AgentFileService(); - // Setup default mocks + // Setup default mocks for initialization tests + // For getAgentFile tests, mocks should be set in each test mockLoadPackageFromHub.mockResolvedValue(mockAgentFile); mockLoadModelFromHub.mockResolvedValue({ name: "gpt-4-agent", provider: "openai", }); + // Default file system mocks + mockPathResolve.mockImplementation((p: string) => `/resolved/${p}`); }); describe("initialization", () => { @@ -307,6 +356,10 @@ describe("AgentFileService", () => { describe("error handling", () => { it("should throw error when agent loading fails", async () => { mockLoadPackageFromHub.mockRejectedValue(new Error("Network error")); + // Also make file reading fail so there's no fallback + mockReadFileSync.mockImplementation(() => { + throw new Error("File not found"); + }); const authServiceState = { authConfig: mockAuthConfig, @@ -320,13 +373,18 @@ describe("AgentFileService", () => { authServiceState, apiClientState, ), - ).rejects.toThrow("Network error"); + ).rejects.toThrow("Failed to load agent from owner/agent"); const state = agentFileService.getState(); expect(state.agentFile).toBeNull(); }); - it("should throw error for invalid agent slug format", async () => { + it("should throw error when both hub and file loading fail", async () => { + mockLoadPackageFromHub.mockRejectedValue(new Error("Hub error")); + mockReadFileSync.mockImplementation(() => { + throw new Error("File not found"); + }); + const authServiceState = { authConfig: mockAuthConfig, isAuthenticated: true, @@ -339,9 +397,7 @@ describe("AgentFileService", () => { authServiceState, apiClientState, ), - ).rejects.toThrow( - 'Invalid agent slug format. Expected "owner/package", got: invalid-slug', - ); + ).rejects.toThrow("Failed to load agent from invalid-slug"); }); it("should throw error when model loading fails", async () => { @@ -428,4 +484,368 @@ describe("AgentFileService", () => { expect(state.parsedTools).toBeNull(); }); }); + + describe("getAgentFile", () => { + const mockFileContent = `--- +name: Test Agent +model: gpt-4 +tools: bash,read,write +rules: Be helpful +--- +You are a helpful agent`; + + describe("hub slug loading", () => { + it("should load from hub when path is valid slug format (owner/agent)", async () => { + mockLoadPackageFromHub.mockResolvedValue(mockAgentFile); + + const result = await agentFileService.getAgentFile("owner/agent"); + + expect(mockLoadPackageFromHub).toHaveBeenCalledWith( + "owner/agent", + expect.objectContaining({ + type: "agentFile", + expectedFileExtensions: [".md"], + }), + ); + expect(result).toEqual(mockAgentFile); + expect(mockReadFileSync).not.toHaveBeenCalled(); + }); + + it("should load from hub when slug has valid two-part format", async () => { + mockLoadPackageFromHub.mockResolvedValue(mockAgentFile); + + const result = + await agentFileService.getAgentFile("continue/dev-agent"); + + expect(mockLoadPackageFromHub).toHaveBeenCalledWith( + "continue/dev-agent", + expect.any(Object), + ); + expect(result).toEqual(mockAgentFile); + }); + + it("should fallback to file path when hub loading fails for slug-like path", async () => { + mockLoadPackageFromHub.mockRejectedValue(new Error("Hub error")); + mockPathResolve.mockImplementation((p: string) => `/resolved/${p}`); + mockReadFileSync.mockReturnValue(mockFileContent); + + const result = await agentFileService.getAgentFile("owner/agent"); + + expect(mockLoadPackageFromHub).toHaveBeenCalled(); + expect(mockPathResolve).toHaveBeenCalledWith("owner/agent"); + expect(mockReadFileSync).toHaveBeenCalledWith( + "/resolved/owner/agent", + "utf-8", + ); + expect(result).toBeDefined(); + expect(result.name).toBe("Test Agent"); + }); + }); + + describe("file:/ URL loading", () => { + it("should load from file:/ URL using fileURLToPath", async () => { + const fileUrl = "file:///home/user/agent.md"; + const resolvedPath = "/home/user/agent.md"; + mockFileURLToPath.mockReturnValue(resolvedPath); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(fileUrl); + + expect(mockFileURLToPath).toHaveBeenCalledWith(fileUrl); + expect(mockReadFileSync).toHaveBeenCalledWith(resolvedPath, "utf-8"); + expect(result.name).toBe("Test Agent"); + expect(result.model).toBe("gpt-4"); + expect(mockLoadPackageFromHub).not.toHaveBeenCalled(); + }); + + it("should handle file:/ prefix with single slash", async () => { + const fileUrl = "file:/path/to/agent.md"; + const resolvedPath = "/path/to/agent.md"; + mockFileURLToPath.mockReturnValue(resolvedPath); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(fileUrl); + + expect(mockFileURLToPath).toHaveBeenCalledWith(fileUrl); + expect(mockReadFileSync).toHaveBeenCalledWith(resolvedPath, "utf-8"); + expect(result).toBeDefined(); + }); + }); + + describe("relative path loading", () => { + it("should load from relative path", async () => { + const relativePath = "./agents/my-agent.md"; + mockPathResolve.mockReturnValue("/absolute/path/agents/my-agent.md"); + mockReadFileSync.mockReturnValue(mockFileContent); + // Reset hub loading mock to ensure it's not called + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(relativePath); + + expect(mockPathResolve).toHaveBeenCalledWith(relativePath); + expect(mockReadFileSync).toHaveBeenCalledWith( + "/absolute/path/agents/my-agent.md", + "utf-8", + ); + expect(result.name).toBe("Test Agent"); + expect(mockLoadPackageFromHub).not.toHaveBeenCalled(); + }); + + it("should load from absolute path", async () => { + const absolutePath = "/home/user/agents/agent.md"; + mockPathResolve.mockReturnValue(absolutePath); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(absolutePath); + + expect(mockPathResolve).toHaveBeenCalledWith(absolutePath); + expect(mockReadFileSync).toHaveBeenCalledWith(absolutePath, "utf-8"); + expect(result).toBeDefined(); + }); + + it("should handle paths with special characters", async () => { + const specialPath = "./agents/my agent (v2).md"; + mockPathResolve.mockReturnValue("/resolved/agents/my agent (v2).md"); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(specialPath); + + expect(mockPathResolve).toHaveBeenCalledWith(specialPath); + expect(mockReadFileSync).toHaveBeenCalledWith( + "/resolved/agents/my agent (v2).md", + "utf-8", + ); + expect(result).toBeDefined(); + }); + }); + + describe("path format edge cases", () => { + it("should treat single-part path as file path, not hub slug", async () => { + const singlePath = "agent"; + mockPathResolve.mockReturnValue("/resolved/agent"); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(singlePath); + + expect(mockLoadPackageFromHub).not.toHaveBeenCalled(); + expect(mockPathResolve).toHaveBeenCalledWith(singlePath); + expect(mockReadFileSync).toHaveBeenCalled(); + expect(result).toBeDefined(); + }); + + it("should treat three-part path as file path, not hub slug", async () => { + const threePath = "path/to/agent"; + mockPathResolve.mockReturnValue("/resolved/path/to/agent"); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(threePath); + + expect(mockLoadPackageFromHub).not.toHaveBeenCalled(); + expect(mockPathResolve).toHaveBeenCalledWith(threePath); + expect(mockReadFileSync).toHaveBeenCalled(); + expect(result).toBeDefined(); + }); + + it("should not treat two-part path with empty part as hub slug", async () => { + const emptyPartPath = "owner/"; + mockPathResolve.mockReturnValue("/resolved/owner/"); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(emptyPartPath); + + expect(mockLoadPackageFromHub).not.toHaveBeenCalled(); + expect(mockPathResolve).toHaveBeenCalledWith(emptyPartPath); + expect(result).toBeDefined(); + }); + + it("should not treat path starting with slash as hub slug", async () => { + const slashPath = "/owner/agent"; + mockPathResolve.mockReturnValue(slashPath); + mockReadFileSync.mockReturnValue(mockFileContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile(slashPath); + + expect(mockLoadPackageFromHub).not.toHaveBeenCalled(); + expect(mockPathResolve).toHaveBeenCalledWith(slashPath); + expect(result).toBeDefined(); + }); + }); + + describe("error handling", () => { + it("should throw error with context when file reading fails", async () => { + const testPath = "./missing-file.md"; + mockPathResolve.mockReturnValue("/resolved/missing-file.md"); + mockReadFileSync.mockImplementation(() => { + throw new Error("ENOENT: no such file or directory"); + }); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + await expect(agentFileService.getAgentFile(testPath)).rejects.toThrow( + "Failed to load agent from ./missing-file.md", + ); + await expect(agentFileService.getAgentFile(testPath)).rejects.toThrow( + "ENOENT: no such file or directory", + ); + }); + + it("should throw error when parseAgentFile fails", async () => { + const invalidContent = "invalid yaml content {{{{"; + mockPathResolve.mockReturnValue("/resolved/invalid.md"); + mockReadFileSync.mockReturnValue(invalidContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + await expect( + agentFileService.getAgentFile("./invalid.md"), + ).rejects.toThrow("Failed to load agent from ./invalid.md"); + }); + + it("should throw error when hub loading fails and file fallback also fails", async () => { + mockLoadPackageFromHub.mockRejectedValue(new Error("Hub error")); + mockPathResolve.mockReturnValue("/resolved/owner/agent"); + mockReadFileSync.mockImplementation(() => { + throw new Error("File not found"); + }); + + await expect( + agentFileService.getAgentFile("owner/agent"), + ).rejects.toThrow("Failed to load agent from owner/agent"); + await expect( + agentFileService.getAgentFile("owner/agent"), + ).rejects.toThrow("File not found"); + }); + + it("should handle permission errors when reading files", async () => { + mockPathResolve.mockReturnValue("/restricted/file.md"); + mockReadFileSync.mockImplementation(() => { + throw new Error("EACCES: permission denied"); + }); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + await expect( + agentFileService.getAgentFile("./restricted-file.md"), + ).rejects.toThrow("Failed to load agent from ./restricted-file.md"); + await expect( + agentFileService.getAgentFile("./restricted-file.md"), + ).rejects.toThrow("EACCES: permission denied"); + }); + }); + + describe("content parsing", () => { + it("should correctly parse agent file with all fields", async () => { + const fullContent = `--- +name: Full Agent +description: A complete agent +model: gpt-4 +tools: bash,read,write +rules: Always be helpful, Be concise +--- +You are a helpful assistant`; + + mockPathResolve.mockReturnValue("/resolved/full.md"); + mockReadFileSync.mockReturnValue(fullContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile("./full.md"); + + expect(result.name).toBe("Full Agent"); + expect(result.description).toBe("A complete agent"); + expect(result.model).toBe("gpt-4"); + expect(result.tools).toBe("bash,read,write"); + expect(result.rules).toBe("Always be helpful, Be concise"); + expect(result.prompt).toBe("You are a helpful assistant"); + }); + + it("should parse agent file with minimal required fields", async () => { + const minimalContent = `--- +name: Minimal Agent +--- +Basic prompt`; + + mockPathResolve.mockReturnValue("/resolved/minimal.md"); + mockReadFileSync.mockReturnValue(minimalContent); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile("./minimal.md"); + + expect(result.name).toBe("Minimal Agent"); + expect(result.prompt).toBe("Basic prompt"); + expect(result.model).toBeUndefined(); + expect(result.tools).toBeUndefined(); + expect(result.rules).toBeUndefined(); + }); + + it("should handle UTF-8 encoded content", async () => { + const utf8Content = `--- +name: Agent with émojis 🚀 +--- +Héllo wörld`; + + mockPathResolve.mockReturnValue("/resolved/utf8.md"); + mockReadFileSync.mockReturnValue(utf8Content); + // Make hub loading fail so it falls back to file system + mockLoadPackageFromHub.mockRejectedValueOnce( + new Error("Not a hub slug"), + ); + + const result = await agentFileService.getAgentFile("./utf8.md"); + + expect(result.name).toBe("Agent with émojis 🚀"); + expect(result.prompt).toBe("Héllo wörld"); + expect(mockReadFileSync).toHaveBeenCalledWith( + "/resolved/utf8.md", + "utf-8", + ); + }); + }); + }); }); diff --git a/extensions/cli/src/services/AgentFileService.ts b/extensions/cli/src/services/AgentFileService.ts index 896d6ff60a3..a6b9ad0e351 100644 --- a/extensions/cli/src/services/AgentFileService.ts +++ b/extensions/cli/src/services/AgentFileService.ts @@ -49,10 +49,10 @@ export class AgentFileService return [SERVICE_NAMES.AUTH, SERVICE_NAMES.API_CLIENT]; } - private async getAgentFile(agentPath: string): Promise { + async getAgentFile(agentPath: string): Promise { try { const parts = agentPath.split("/"); - if (parts.length === 2 && parts[0] && parts[1]) { + if (parts.length === 2 && parts[0] && parts[1] && !parts.includes(".")) { try { return await loadPackageFromHub(agentPath, agentFileProcessor); } catch (e) { From 1e9637196ba5b37bf713c50c4f73e40366e1cfc3 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Mon, 17 Nov 2025 11:19:36 -0800 Subject: [PATCH 4/5] fix: formatting --- core/package-lock.json | 6 +++--- .../cli/src/services/AgentFileService.test.ts | 1 + extensions/cli/src/services/AgentFileService.ts | 13 ++++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/package-lock.json b/core/package-lock.json index 57518a6eb76..ddc8ba6ed20 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -220,11 +220,11 @@ "version": "1.15.0", "license": "Apache-2.0", "dependencies": { - "@anthropic-ai/sdk": "^0.62.0", + "@anthropic-ai/sdk": "^0.67.0", "@aws-sdk/client-bedrock-runtime": "^3.842.0", - "@aws-sdk/credential-providers": "^3.840.0", + "@aws-sdk/credential-providers": "^3.913.0", "@continuedev/config-types": "^1.0.14", - "@continuedev/config-yaml": "^1.14.0", + "@continuedev/config-yaml": "^1.29.0", "@continuedev/fetch": "^1.5.0", "dotenv": "^16.5.0", "google-auth-library": "^10.1.0", diff --git a/extensions/cli/src/services/AgentFileService.test.ts b/extensions/cli/src/services/AgentFileService.test.ts index e88b41eabc7..6aab1f10885 100644 --- a/extensions/cli/src/services/AgentFileService.test.ts +++ b/extensions/cli/src/services/AgentFileService.test.ts @@ -1,6 +1,7 @@ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; + import { beforeEach, describe, expect, it, vi } from "vitest"; import { diff --git a/extensions/cli/src/services/AgentFileService.ts b/extensions/cli/src/services/AgentFileService.ts index a6b9ad0e351..d7874a4f022 100644 --- a/extensions/cli/src/services/AgentFileService.ts +++ b/extensions/cli/src/services/AgentFileService.ts @@ -1,11 +1,16 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + import { AgentFile, parseAgentFile, parseAgentFileRules, parseAgentFileTools, } from "@continuedev/config-yaml"; -import fs from "fs"; -import path from "path"; + +import { getErrorString } from "src/util/error.js"; + import { agentFileProcessor, loadModelFromHub, @@ -13,8 +18,6 @@ import { } from "../hubLoader.js"; import { logger } from "../util/logger.js"; -import { getErrorString } from "src/util/error.js"; -import { fileURLToPath } from "url"; import { BaseService, ServiceWithDependencies } from "./BaseService.js"; import { serviceContainer } from "./ServiceContainer.js"; import { @@ -57,7 +60,7 @@ export class AgentFileService return await loadPackageFromHub(agentPath, agentFileProcessor); } catch (e) { logger.info( - `Failed to load agent file from slug-like path: ${agentPath}`, + `Failed to load agent file from slug-like path ${agentPath}: ${getErrorString(e)}`, ); // slug COULD be path, fall back to relative path } From bce188c70ab4b594ef90c01d05ac9cb71f22122a Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 18 Nov 2025 17:00:04 -0800 Subject: [PATCH 5/5] fix: logger mock and error message --- extensions/cli/src/integration/rule-duplication.test.ts | 2 ++ extensions/cli/src/services/ChatHistoryService.test.ts | 3 ++- .../cli/src/services/ModelService.workflow-priority.test.ts | 2 ++ extensions/cli/src/services/agent-file-integration.test.ts | 5 +++-- extensions/cli/src/util/apiClient.test.ts | 2 ++ extensions/cli/src/util/clipboard.test.ts | 3 +++ extensions/cli/src/util/tokenizer.test.ts | 1 + 7 files changed, 15 insertions(+), 3 deletions(-) diff --git a/extensions/cli/src/integration/rule-duplication.test.ts b/extensions/cli/src/integration/rule-duplication.test.ts index 3a713170d14..242844a4c0b 100644 --- a/extensions/cli/src/integration/rule-duplication.test.ts +++ b/extensions/cli/src/integration/rule-duplication.test.ts @@ -31,7 +31,9 @@ vi.mock("../auth/workos.js", () => ({ vi.mock("../util/logger.js", () => ({ logger: { debug: vi.fn(), + info: vi.fn(), warn: vi.fn(), + error: vi.fn(), }, })); diff --git a/extensions/cli/src/services/ChatHistoryService.test.ts b/extensions/cli/src/services/ChatHistoryService.test.ts index e5edef8ba4e..a7e98803dbe 100644 --- a/extensions/cli/src/services/ChatHistoryService.test.ts +++ b/extensions/cli/src/services/ChatHistoryService.test.ts @@ -1,5 +1,5 @@ import type { ChatHistoryItem } from "core/index.js"; -import { describe, it, expect, beforeEach, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { ChatHistoryService } from "./ChatHistoryService.js"; @@ -18,6 +18,7 @@ vi.mock("../session.js", () => ({ vi.mock("../util/logger.js", () => ({ logger: { debug: vi.fn(), + info: vi.fn(), warn: vi.fn(), error: vi.fn(), }, diff --git a/extensions/cli/src/services/ModelService.workflow-priority.test.ts b/extensions/cli/src/services/ModelService.workflow-priority.test.ts index 8706a115095..3f82376edbc 100644 --- a/extensions/cli/src/services/ModelService.workflow-priority.test.ts +++ b/extensions/cli/src/services/ModelService.workflow-priority.test.ts @@ -23,6 +23,8 @@ vi.mock("../config.js", () => ({ vi.mock("../util/logger.js", () => ({ logger: { debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), error: vi.fn(), }, })); diff --git a/extensions/cli/src/services/agent-file-integration.test.ts b/extensions/cli/src/services/agent-file-integration.test.ts index 080f9e40d79..47e16428e13 100644 --- a/extensions/cli/src/services/agent-file-integration.test.ts +++ b/extensions/cli/src/services/agent-file-integration.test.ts @@ -25,8 +25,9 @@ vi.mock("../hubLoader.js", () => ({ vi.mock("../util/logger.js", () => ({ logger: { debug: vi.fn(), - error: vi.fn(), + info: vi.fn(), warn: vi.fn(), + error: vi.fn(), }, })); @@ -417,7 +418,7 @@ describe("Agent file Integration Tests", () => { authServiceState, apiClientState, ), - ).rejects.toThrow("Network error"); + ).rejects.toThrow("Failed to load agent from owner/agent"); const agentFileState = agentFileService.getState(); expect(agentFileState.agentFile).toBeNull(); diff --git a/extensions/cli/src/util/apiClient.test.ts b/extensions/cli/src/util/apiClient.test.ts index 09bb12ed54c..66172f43db4 100644 --- a/extensions/cli/src/util/apiClient.test.ts +++ b/extensions/cli/src/util/apiClient.test.ts @@ -25,6 +25,8 @@ vi.mock("../env.js", () => ({ vi.mock("./logger.js", () => ({ logger: { debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), error: vi.fn(), }, })); diff --git a/extensions/cli/src/util/clipboard.test.ts b/extensions/cli/src/util/clipboard.test.ts index 308556c0584..b8bb7ace8a6 100644 --- a/extensions/cli/src/util/clipboard.test.ts +++ b/extensions/cli/src/util/clipboard.test.ts @@ -42,6 +42,9 @@ vi.mock("path", () => ({ vi.mock("./logger.js", () => ({ logger: { debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), }, })); diff --git a/extensions/cli/src/util/tokenizer.test.ts b/extensions/cli/src/util/tokenizer.test.ts index 1ceccafe6da..807f1174d26 100644 --- a/extensions/cli/src/util/tokenizer.test.ts +++ b/extensions/cli/src/util/tokenizer.test.ts @@ -17,6 +17,7 @@ import { vi.mock("./logger.js", () => ({ logger: { debug: vi.fn(), + info: vi.fn(), warn: vi.fn(), error: vi.fn(), },