| 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | |
| 3 | import { planClaudeSkillDispatch } from "./ClaudeSkillDispatch.ts"; |
| 4 | |
| 5 | const SKILLS = new Set(["2spec", "implement", "review", "re-release-version"]); |
| 6 | |
| 7 | describe("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 | }); |