Skip to content

Commit 16bd0fe

Browse files
authored
Merge pull request #9266 from continuedev/dallin/relative-path-fallback-cli
fix: don't fallback to relative path if not markdown file
2 parents 32c6aeb + 53a9fa4 commit 16bd0fe

File tree

2 files changed

+41
-40
lines changed

2 files changed

+41
-40
lines changed

extensions/cli/src/services/AgentFileService.test.ts

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -525,17 +525,17 @@ You are a helpful agent`;
525525
expect(result).toEqual(mockAgentFile);
526526
});
527527

528-
it("should fallback to file path when hub loading fails for slug-like path", async () => {
528+
it("should fallback to file path when hub loading fails for slug-like path with .md extension", async () => {
529529
mockLoadPackageFromHub.mockRejectedValue(new Error("Hub error"));
530530
mockPathResolve.mockImplementation((p: string) => `/resolved/${p}`);
531531
mockReadFileSync.mockReturnValue(mockFileContent);
532532

533-
const result = await agentFileService.getAgentFile("owner/agent");
533+
const result = await agentFileService.getAgentFile("owner/agent.md");
534534

535-
expect(mockLoadPackageFromHub).toHaveBeenCalled();
536-
expect(mockPathResolve).toHaveBeenCalledWith("owner/agent");
535+
expect(mockLoadPackageFromHub).toHaveBeenCalled(); // Two-part paths try hub first
536+
expect(mockPathResolve).toHaveBeenCalledWith("owner/agent.md");
537537
expect(mockReadFileSync).toHaveBeenCalledWith(
538-
"/resolved/owner/agent",
538+
"/resolved/owner/agent.md",
539539
"utf-8",
540540
);
541541
expect(result).toBeDefined();
@@ -641,13 +641,9 @@ You are a helpful agent`;
641641

642642
describe("path format edge cases", () => {
643643
it("should treat single-part path as file path, not hub slug", async () => {
644-
const singlePath = "agent";
645-
mockPathResolve.mockReturnValue("/resolved/agent");
644+
const singlePath = "agent.md";
645+
mockPathResolve.mockReturnValue("/resolved/agent.md");
646646
mockReadFileSync.mockReturnValue(mockFileContent);
647-
// Make hub loading fail so it falls back to file system
648-
mockLoadPackageFromHub.mockRejectedValueOnce(
649-
new Error("Not a hub slug"),
650-
);
651647

652648
const result = await agentFileService.getAgentFile(singlePath);
653649

@@ -658,13 +654,9 @@ You are a helpful agent`;
658654
});
659655

660656
it("should treat three-part path as file path, not hub slug", async () => {
661-
const threePath = "path/to/agent";
662-
mockPathResolve.mockReturnValue("/resolved/path/to/agent");
657+
const threePath = "path/to/agent.md";
658+
mockPathResolve.mockReturnValue("/resolved/path/to/agent.md");
663659
mockReadFileSync.mockReturnValue(mockFileContent);
664-
// Make hub loading fail so it falls back to file system
665-
mockLoadPackageFromHub.mockRejectedValueOnce(
666-
new Error("Not a hub slug"),
667-
);
668660

669661
const result = await agentFileService.getAgentFile(threePath);
670662

@@ -675,29 +667,20 @@ You are a helpful agent`;
675667
});
676668

677669
it("should not treat two-part path with empty part as hub slug", async () => {
678-
const emptyPartPath = "owner/";
679-
mockPathResolve.mockReturnValue("/resolved/owner/");
680-
mockReadFileSync.mockReturnValue(mockFileContent);
681-
// Make hub loading fail so it falls back to file system
682-
mockLoadPackageFromHub.mockRejectedValueOnce(
683-
new Error("Not a hub slug"),
684-
);
670+
const emptyPartPath = "owner/"; // Empty second part
685671

686-
const result = await agentFileService.getAgentFile(emptyPartPath);
672+
// With the new behavior, non-markdown paths throw errors
673+
await expect(
674+
agentFileService.getAgentFile(emptyPartPath),
675+
).rejects.toThrow("Not a markdown file");
687676

688-
expect(mockLoadPackageFromHub).not.toHaveBeenCalled();
689-
expect(mockPathResolve).toHaveBeenCalledWith(emptyPartPath);
690-
expect(result).toBeDefined();
677+
expect(mockLoadPackageFromHub).not.toHaveBeenCalled(); // Empty part means no hub attempt
691678
});
692679

693680
it("should not treat path starting with slash as hub slug", async () => {
694-
const slashPath = "/owner/agent";
681+
const slashPath = "/owner/agent.md";
695682
mockPathResolve.mockReturnValue(slashPath);
696683
mockReadFileSync.mockReturnValue(mockFileContent);
697-
// Make hub loading fail so it falls back to file system
698-
mockLoadPackageFromHub.mockRejectedValueOnce(
699-
new Error("Not a hub slug"),
700-
);
701684

702685
const result = await agentFileService.getAgentFile(slashPath);
703686

@@ -741,19 +724,30 @@ You are a helpful agent`;
741724
).rejects.toThrow("Failed to load agent from ./invalid.md");
742725
});
743726

744-
it("should throw error when hub loading fails and file fallback also fails", async () => {
727+
it("should throw error when file reading fails for markdown path", async () => {
745728
mockLoadPackageFromHub.mockRejectedValue(new Error("Hub error"));
746-
mockPathResolve.mockReturnValue("/resolved/owner/agent");
729+
mockPathResolve.mockReturnValue("/resolved/owner/agent.md");
747730
mockReadFileSync.mockImplementation(() => {
748731
throw new Error("File not found");
749732
});
750733

734+
await expect(
735+
agentFileService.getAgentFile("owner/agent.md"),
736+
).rejects.toThrow("Failed to load agent from owner/agent.md");
737+
await expect(
738+
agentFileService.getAgentFile("owner/agent.md"),
739+
).rejects.toThrow("File not found");
740+
});
741+
742+
it("should throw error for non-markdown paths without trying file fallback", async () => {
743+
mockLoadPackageFromHub.mockRejectedValue(new Error("Hub error"));
744+
751745
await expect(
752746
agentFileService.getAgentFile("owner/agent"),
753747
).rejects.toThrow("Failed to load agent from owner/agent");
754748
await expect(
755749
agentFileService.getAgentFile("owner/agent"),
756-
).rejects.toThrow("File not found");
750+
).rejects.toThrow("Not a markdown file");
757751
});
758752

759753
it("should handle permission errors when reading files", async () => {

extensions/cli/src/services/AgentFileService.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,20 @@ export class AgentFileService
5858
if (parts.length === 2 && parts[0] && parts[1] && !parts.includes(".")) {
5959
try {
6060
return await loadPackageFromHub(agentPath, agentFileProcessor);
61-
} catch (e) {
62-
logger.info(
63-
`Failed to load agent file from slug-like path ${agentPath}: ${getErrorString(e)}`,
64-
);
61+
} catch {
6562
// slug COULD be path, fall back to relative path
6663
}
6764
}
65+
66+
const isMarkdownPath =
67+
agentPath.endsWith(".md") || agentPath.endsWith(".markdown");
68+
// Only fall back to relative path for markdown files
69+
if (!isMarkdownPath) {
70+
throw new Error(
71+
`Failed to load agent from ${agentPath}. Not a markdown file`,
72+
);
73+
}
74+
6875
const resolvedPath = agentPath.startsWith("file:/")
6976
? fileURLToPath(agentPath)
7077
: path.resolve(agentPath);

0 commit comments

Comments
 (0)