Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions src/commands/init/agent-definitions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,97 @@ describe("detection configuration", () => {
expect(await agent.detectBinary!(exec)).toBe(false);
});

it("claude-code detectBinary returns true when binary found", async () => {
const exec = createMockExecService({
exec: mock(async () => ({
exitCode: 0,
stdout: "/usr/bin/claude\n",
stderr: "",
})),
});
const agent = agentDefinitions.find((a) => a.id === "claude-code")!;
expect(await agent.detectBinary!(exec)).toBe(true);
});

it("binary detectors use correct command and binary name on linux", async () => {
const originalPlatform = process.platform;
Object.defineProperty(process, "platform", {
value: "linux",
configurable: true,
});

try {
const testCases = [
{ id: "claude-code", binary: "claude" },
{ id: "codex-cli", binary: "codex" },
{ id: "gemini-cli", binary: "gemini" },
{ id: "opencode", binary: "opencode" },
];

for (const testCase of testCases) {
const exec = createMockExecService({
exec: mock(async () => ({
exitCode: 0,
stdout: "/usr/bin/mock\n",
stderr: "",
})),
});
const agent = agentDefinitions.find((a) => a.id === testCase.id)!;
expect(await agent.detectBinary!(exec)).toBe(true);
expect(exec.exec).toHaveBeenCalledWith("which", [testCase.binary]);
}
} finally {
Object.defineProperty(process, "platform", {
value: originalPlatform,
configurable: true,
});
}
});

it("binary detectors use where on win32", async () => {
const originalPlatform = process.platform;
Object.defineProperty(process, "platform", {
value: "win32",
configurable: true,
});

try {
const exec = createMockExecService({
exec: mock(async () => ({
exitCode: 0,
stdout: "C:\\Program Files\\Claude\\claude.exe\n",
stderr: "",
})),
});
const agent = agentDefinitions.find((a) => a.id === "claude-code")!;
expect(await agent.detectBinary!(exec)).toBe(true);
expect(exec.exec).toHaveBeenCalledWith("where", ["claude"]);
} finally {
Object.defineProperty(process, "platform", {
value: originalPlatform,
configurable: true,
});
}
});

it("claude-code detectBinary returns false when binary not found", async () => {
const exec = createMockExecService({
exec: mock(async () => ({ exitCode: 1, stdout: "", stderr: "" })),
});
const agent = agentDefinitions.find((a) => a.id === "claude-code")!;
expect(await agent.detectBinary!(exec)).toBe(false);
});

it("claude-code detectBinary returns false on exec error", async () => {
const exec = createMockExecService({
exec: mock(async () => {
throw new Error("spawn ENOENT");
}),
});
const agent = agentDefinitions.find((a) => a.id === "claude-code")!;
expect(await agent.detectBinary!(exec)).toBe(false);
});

it("path-detected agents use FileSystemService.getHomeDir (not hardcoded)", () => {
const originalPlatform = process.platform;
const originalAppdata = process.env.APPDATA;
Expand Down Expand Up @@ -851,6 +942,23 @@ describe("scanAgents", () => {
expect(result.notDetected.some((a) => a.id === "opencode")).toBe(false);
});

it("detects codex via detectBinary when directory does not exist", async () => {
const lookupCmd = lookupCommandFor();
const { fs, execService } = createScanMocks({
detectedDirs: [],
execResults: {
[`${lookupCmd} codex`]: {
exitCode: 0,
stdout: "/usr/bin/codex\n",
stderr: "",
},
},
});
const result = await scanAgents(agentDefinitions, fs, execService);
expect(result.needsSetup.some((a) => a.id === "codex-cli")).toBe(true);
expect(result.notDetected.some((a) => a.id === "codex-cli")).toBe(false);
});

it("does not detect opencode from config directory when binary is missing", async () => {
const originalPlatform = process.platform;
Object.defineProperty(process, "platform", {
Expand Down
1 change: 1 addition & 0 deletions src/commands/init/agent-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ const openCode: AgentDefinition = {
/**
* All supported agent definitions, ordered by popularity/likelihood.
* New agents should be added here.
* @deprecated Use scanAgents() instead, which also checks configuration status.
*/
export const agentDefinitions: AgentDefinition[] = [
claudeCode,
Expand Down
Loading