import { describe, expect, it } from "vitest"; import { getAutocompleteFallbackIndex, orderAutocompleteOptions, } from "@/components/ui/autocomplete-utils"; import { applySlashCommandReplacement, filterAndRankCommandAutocompleteEntries, filterInlineSkillCommandEntries, findActiveSlashCommand, } from "./agent-command-autocomplete"; describe("filterAndRankCommandAutocompleteEntries", () => { const entries = [ { source: "provider" as const, command: { name: "paseo-committee" } }, { source: "provider" as const, command: { name: "commit" } }, { source: "provider" as const, command: { name: "paseo-advisor" } }, ]; it("ranks command-name prefixes above later word-boundary partial matches", () => { const result = filterAndRankCommandAutocompleteEntries(entries, "comm"); expect(result.map((entry) => entry.command.name)).toEqual(["commit", "paseo-committee"]); }); it("matches client command aliases", () => { const result = filterAndRankCommandAutocompleteEntries( [ { source: "client" as const, command: { name: "exit", aliases: ["quit", "q"] } }, { source: "client" as const, command: { name: "clear", aliases: ["new"] } }, ], "q", ); expect(result.map((entry) => entry.command.name)).toEqual(["exit"]); }); }); describe("filterAndRankCommandAutocompleteEntries on an empty query", () => { const rootEntries = [ { source: "client" as const, command: { name: "exit", aliases: ["quit", "q"] } }, { source: "client" as const, command: { name: "clear", aliases: ["new"] } }, { source: "plugin" as const, command: { name: "handoff" } }, { source: "provider" as const, command: { name: "compact" } }, { source: "provider" as const, command: { name: "review" } }, ]; function selectedOnEnter(query: string) { const listed = orderAutocompleteOptions( filterAndRankCommandAutocompleteEntries(rootEntries, query), ); return listed[getAutocompleteFallbackIndex(listed.length)]?.command.name; } it("selects a provider command, never an immediate Paseo action", () => { expect(selectedOnEnter("")).toBe("compact"); }); it("lists Paseo actions farthest from the input", () => { const listed = orderAutocompleteOptions( filterAndRankCommandAutocompleteEntries(rootEntries, ""), ); expect(listed.map((entry) => entry.command.name)).toEqual([ "clear", "exit", "handoff", "review", "compact", ]); }); it("still selects a typed Paseo action", () => { expect(selectedOnEnter("exit")).toBe("exit"); }); it("waits for a search when Paseo actions are all there is", () => { const clientOnly = rootEntries.filter((entry) => entry.source === "client"); expect(filterAndRankCommandAutocompleteEntries(clientOnly, "")).toEqual([]); expect( filterAndRankCommandAutocompleteEntries(clientOnly, "ex").map((entry) => entry.command.name), ).toEqual(["exit"]); }); }); describe("findActiveSlashCommand", () => { it("detects a slash command token in the middle of the prompt", () => { const text = "use /tas before implementation"; expect( findActiveSlashCommand({ text, cursorIndex: "use /tas".length, }), ).toEqual({ start: 4, end: "use /tas".length, query: "tas", position: "inline", }); }); it("classifies a slash command token at the prompt start", () => { expect( findActiveSlashCommand({ text: "/rew", cursorIndex: "/rew".length, }), ).toEqual({ start: 0, end: "/rew".length, query: "rew", position: "start", }); }); it("returns null when the cursor is outside the slash token", () => { expect( findActiveSlashCommand({ text: "use /taste now", cursorIndex: "use /taste now".length, }), ).toBeNull(); }); it("returns null for slash-delimited paths", () => { expect( findActiveSlashCommand({ text: "read /tmp/project", cursorIndex: "read /tmp/project".length, }), ).toBeNull(); }); }); describe("applySlashCommandReplacement", () => { it("replaces only the active slash token", () => { const text = "use /tas before implementation"; expect( applySlashCommandReplacement({ text, command: { start: 4, end: "use /tas".length, query: "tas", position: "inline" }, commandName: "taste", }), ).toBe("use /taste before implementation"); }); it("commits an inline slash token at the prompt tail", () => { const text = "use /tas"; expect( applySlashCommandReplacement({ text, command: { start: 4, end: text.length, query: "tas", position: "inline" }, commandName: "taste", }), ).toBe("use /taste "); }); it("commits a start slash token at the prompt tail", () => { const text = "/tas"; expect( applySlashCommandReplacement({ text, command: { start: 0, end: text.length, query: "tas", position: "start" }, commandName: "taste", }), ).toBe("/taste "); }); }); describe("filterInlineSkillCommandEntries", () => { it("keeps provider skills and drops executable commands", () => { const entries = [ { source: "client" as const, command: { name: "clear", kind: "command" } }, { source: "provider" as const, command: { name: "compact", kind: "command" } }, { source: "provider" as const, command: { name: "taste", kind: "skill" } }, ]; expect(filterInlineSkillCommandEntries(entries).map((entry) => entry.command.name)).toEqual([ "taste", ]); }); });