1import { describe, expect, it } from "vite-plus/test";
2
3import { planClaudeSkillDispatch } from "./ClaudeSkillDispatch.ts";
4
5const SKILLS = new Set(["2spec", "implement", "review", "re-release-version"]);
6
7describe("planClaudeSkillDispatch", () => {
8 it("leaves a prompt without a known skill untouched", () => {
9 expect(planClaudeSkillDispatch("fix the build", SKILLS)).toBeUndefined();
10 // Not a discovered skill, so it stays prose rather than becoming a command.
11 expect(planClaudeSkillDispatch("echo $HOME then $unknown", SKILLS)).toBeUndefined();
12 });
13
14 it("moves a mid-prompt mention into a trailing slash command", () => {
15 expect(planClaudeSkillDispatch("ok, now $implement all the tickets", SKILLS)).toEqual({
16 leadingText: "ok, now",
17 commandText: "/implement all the tickets",
18 skillName: "implement",
19 });
20 });
21
22 it("keeps a mention that opens the prompt as a single command block", () => {
23 expect(planClaudeSkillDispatch("$review\nfocus on auth", SKILLS)).toEqual({
24 leadingText: undefined,
25 commandText: "/review\nfocus on auth",
26 skillName: "review",
27 });
28 });
29
30 it("dispatches a known skill whose name begins with a digit", () => {
31 expect(planClaudeSkillDispatch("use $2spec for this", SKILLS)).toEqual({
32 leadingText: "use",
33 commandText: "/2spec for this",
34 skillName: "2spec",
35 });
36 });
37
38 it("dispatches the last mention and rewrites earlier ones inline", () => {
39 expect(planClaudeSkillDispatch("$review the diff, then $implement the fixes", SKILLS)).toEqual({
40 leadingText: "/review the diff, then",
41 commandText: "/implement the fixes",
42 skillName: "implement",
43 });
44 });
45
46 it("dispatches currency-prefixed mentions and preserves their source boundaries", () => {
47 for (const symbol of ["€", "£", "¥", "₹", "₩", "₿", "𑿝"]) {
48 expect(
49 planClaudeSkillDispatch(
50 `${symbol}review the diff, then ${symbol}implement the fixes`,
51 SKILLS,
52 ),
53 ).toEqual({
54 leadingText: "/review the diff, then",
55 commandText: "/implement the fixes",
56 skillName: "implement",
57 });
58 expect(planClaudeSkillDispatch(`${symbol}2spec for this`, SKILLS)).toEqual({
59 leadingText: undefined,
60 commandText: "/2spec for this",
61 skillName: "2spec",
62 });
63 expect(planClaudeSkillDispatch(`5${symbol}review ${symbol}unknown`, SKILLS)).toBeUndefined();
64 }
65 });
66
67 it("ignores a dollar token glued to other text", () => {
68 expect(planClaudeSkillDispatch("cost is 5$implement", SKILLS)).toBeUndefined();
69 });
70
71 it("ignores currency amounts and compact monetary expressions", () => {
72 const skillsWithCurrency = new Set([...SKILLS, "20", "20k", "100M", "1e6"]);
73 for (const symbol of ["$", "€", "£", "¥", "₹", "₩", "₿", "𑿝"]) {
74 expect(
75 planClaudeSkillDispatch(
76 `pay ${symbol}20 ${symbol}20k ${symbol}100M ${symbol}1e6 tomorrow`,
77 skillsWithCurrency,
78 ),
79 ).toBeUndefined();
80 }
81 });
82});