import { describe, expect, it } from "vitest"; import type { AgentSession } from "../src/core/agent-session.js"; import { collectArgumentCompletions, createAgentConnectionCommands } from "../src/modes/agent-connection/snapshot.js"; import { getPublishedArgumentCompletions } from "../src/modes/command-autocomplete.js"; const routeCandidates = [ { value: "status", label: "status", description: "Show the route" }, { value: "policy ", label: "policy", description: "Select a policy" }, { value: "policy deepseek-economic", label: "deepseek-economic" }, { value: "use deepseek/deepseek-flash", label: "deepseek-flash" }, ]; describe("published argument completions", () => { it("completes the first token from an empty or partial prefix", () => { expect(getPublishedArgumentCompletions(routeCandidates, "")?.map((item) => item.value)).toEqual([ "status", "policy ", ]); expect(getPublishedArgumentCompletions(routeCandidates, "pol")?.map((item) => item.value)).toEqual(["policy "]); }); it("completes deeper tokens once the prefix has a space", () => { expect(getPublishedArgumentCompletions(routeCandidates, "policy ")?.map((item) => item.value)).toEqual([ "policy deepseek-economic", ]); expect(getPublishedArgumentCompletions(routeCandidates, "policy dee")?.map((item) => item.value)).toEqual([ "policy deepseek-economic", ]); expect(getPublishedArgumentCompletions(routeCandidates, "use deepseek/")).not.toBeNull(); }); it("keeps labels, descriptions, and a case-insensitive match", () => { const items = getPublishedArgumentCompletions(routeCandidates, "Sta"); expect(items).toEqual([{ value: "status", label: "status", description: "Show the route" }]); expect(getPublishedArgumentCompletions(routeCandidates, "zzz")).toBeNull(); }); }); describe("collecting a command's argument completions", () => { const command = (respond: (prefix: string) => unknown) => respond; it("probes one level into a candidate that ends in a space", async () => { const completions = await collectArgumentCompletions( command((prefix) => { if (prefix === "") return [{ value: "status" }, { value: "policy " }]; if (prefix === "policy ") return [{ value: "policy deepseek-economic", label: "economic" }]; return null; }), ); expect(completions).toEqual([ { value: "status" }, { value: "policy " }, { value: "policy deepseek-economic", label: "economic" }, ]); }); it("treats an absent, failing, or empty completer as no candidates", async () => { expect(await collectArgumentCompletions(undefined)).toBeUndefined(); expect( await collectArgumentCompletions( command(() => { throw new Error("no completions here"); }), ), ).toBeUndefined(); expect(await collectArgumentCompletions(command(() => []))).toBeUndefined(); }); it("deduplicates repeated candidates and skips unusable ones", async () => { const completions = await collectArgumentCompletions( command(() => [{ value: "policy " }, { value: "policy " }, { value: 7 }, { label: "no value" }, {}]), ); expect(completions).toEqual([{ value: "policy " }]); }); }); describe("the published command catalog", () => { const session = { extensionRunner: { getRegisteredCommands: () => [ { name: "route", invocationName: "route", description: "Select a routing policy", sourceInfo: { path: "/tmp/extensions/route.ts" }, getArgumentCompletions: (prefix: string) => prefix === "" ? [{ value: "status" }, { value: "policy " }] : prefix === "policy " ? [{ value: "policy deepseek-economic" }] : null, }, { name: "plain", invocationName: "plain", description: "No completions", sourceInfo: { path: "/tmp/extensions/plain.ts" }, }, ], }, promptTemplates: [], resourceLoader: { getSkills: () => ({ skills: [] }) }, } as unknown as AgentSession; it("publishes a command's completions and omits the field for a command without any", async () => { const commands = await createAgentConnectionCommands(session); const [route, plain] = commands; expect(route?.argumentCompletions).toEqual([ { value: "status" }, { value: "policy " }, { value: "policy deepseek-economic" }, ]); expect(plain && "argumentCompletions" in plain).toBe(false); }); });