import { test } from "node:test"; import assert from "node:assert/strict"; import { parsePromptRequest, requestFromArguments, renderWarnings, validateSweepArguments, DEFAULT_BATCH_SIZE, DEFAULT_PARSE_MODEL, } from "./promptRequest"; // The parser exists because Claude Code's slash-command tokenizer shredded a // real request into link="This" channel="search" group="deleted" // directive="videos." batch_size="Why" and dropped the rest. Every case below // is either that failure or a neighbour of it. // ─── Link extraction ─── test("a bare URL is extracted and the rest stays prose", () => { const r = parsePromptRequest("https://site.example/?q=x what did they say"); assert.equal(r.link, "https://site.example/?q=x"); assert.equal(r.query, "what did they say"); }); test("a URL's query string survives intact — & and = are not settings", () => { const r = parsePromptRequest( "https://site.example/?qt=abc&fc=chan-a&fav=deleted why though", ); assert.equal(r.link, "https://site.example/?qt=abc&fc=chan-a&fav=deleted"); assert.equal(r.query, "why though"); assert.deepEqual(r.warnings, []); }); test("only the FIRST url becomes the link; a second stays in the prose", () => { const r = parsePromptRequest( "https://a.example/one compare with https://b.example/two", ); assert.equal(r.link, "https://a.example/one"); assert.match(r.query, /https:\/\/b\.example\/two/); }); test("an angle-bracketed url is unwrapped", () => { const r = parsePromptRequest(" and then some"); assert.equal(r.link, "https://site.example/x"); assert.ok(!r.query.includes("<"), r.query); assert.ok(!r.query.includes(">"), r.query); }); test("a markdown link is unwrapped, label and all", () => { const r = parsePromptRequest("[Search results](https://site.example/?q=a) go on"); assert.equal(r.link, "https://site.example/?q=a"); assert.equal(r.query, "go on"); }); test("a quoted url is unwrapped", () => { const r = parsePromptRequest('"https://site.example/x" please'); assert.equal(r.link, "https://site.example/x"); assert.equal(r.query, "please"); }); test("a trailing full stop is not part of the url", () => { const r = parsePromptRequest("see https://site.example/x. Then explain."); assert.equal(r.link, "https://site.example/x"); }); test("a trailing comma, colon and semicolon are not part of the url", () => { for (const p of [",", ":", ";", "!", "?"]) { const r = parsePromptRequest(`see https://site.example/x${p} more`); assert.equal(r.link, "https://site.example/x", `punctuation ${p}`); } }); test("an unbalanced closing paren is dropped but a balanced one is kept", () => { const a = parsePromptRequest("(see https://site.example/x) ok"); assert.equal(a.link, "https://site.example/x"); const b = parsePromptRequest("https://site.example/x(y) ok"); assert.equal(b.link, "https://site.example/x(y)"); }); test("no url at all is fine", () => { const r = parsePromptRequest("just a question about coffee"); assert.equal(r.link, undefined); assert.equal(r.query, "just a question about coffee"); }); // ─── The whole-request regression ─── test("the shredded Rekieta line survives whole", () => { const input = "https://hasanalyzer.pages.dev/?qt=eyJhIjoxfQ&fav=deleted " + "This search finds deleted videos. Why might have Rekieta privated " + "these? Look for context around each one. batch_size=12"; const r = parsePromptRequest(input); assert.equal(r.link, "https://hasanalyzer.pages.dev/?qt=eyJhIjoxfQ&fav=deleted"); assert.equal(r.batchSize, 12); // Every word of the question is present, in order, punctuation intact. assert.equal( r.query, "This search finds deleted videos. Why might have Rekieta privated " + "these? Look for context around each one.", ); assert.deepEqual(r.warnings, []); }); // ─── The key=value whitelist ─── test("recognised settings are parsed off the prose", () => { const r = parsePromptRequest( "coffee talk channels=chan-a,chan-b groups=other batch_size=5 " + "parse_model=sonnet report=./out.md source=remote:https://x.example", ); assert.equal(r.query, "coffee talk"); assert.deepEqual(r.channels, ["chan-a", "chan-b"]); assert.deepEqual(r.groups, ["other"]); assert.equal(r.batchSize, 5); assert.equal(r.parseModel, "sonnet"); assert.equal(r.reportPath, "./out.md"); assert.equal(r.source, "remote:https://x.example"); }); test("aliases map onto the canonical keys", () => { const r = parsePromptRequest("x channel=chan-a group=other model=opus out=./r.md"); assert.deepEqual(r.channels, ["chan-a"]); assert.deepEqual(r.groups, ["other"]); assert.equal(r.parseModel, "opus"); assert.equal(r.reportPath, "./r.md"); }); test("an unknown key=value STAYS in the prose and warns", () => { const r = parsePromptRequest("find x flavour=vanilla please"); assert.match(r.query, /flavour=vanilla/); assert.match(r.warnings.join(" "), /"flavour=" is not a recognised setting/); }); test("a near-miss key gets a typo hint and still stays in the prose", () => { const r = parsePromptRequest("find x chanels=chan-a"); assert.match(r.query, /chanels=chan-a/); assert.match(r.warnings.join(" "), /did you mean channels=\?/); }); test("prose that merely contains '=' is never treated as a setting", () => { const r = parsePromptRequest("solve x=y+2 and 3==3 for me"); assert.match(r.query, /x=y\+2/); assert.match(r.query, /3==3/); }); test("a quoted multi-word value survives as one value", () => { const r = parsePromptRequest( 'sweep it directive="key claims & contradictions" now', ); assert.equal(r.directive, "key claims & contradictions"); assert.equal(r.query, "sweep it now"); }); test("a repeated key takes the last value and warns", () => { const r = parsePromptRequest("x batch_size=4 batch_size=9"); assert.equal(r.batchSize, 9); assert.match(r.warnings.join(" "), /batch_size was given more than once/); }); // ─── Validation: the failures that used to render into the output ─── test("batch_size=Why falls back to the default with a warning", () => { const r = parsePromptRequest("x batch_size=Why"); assert.equal(r.batchSize, DEFAULT_BATCH_SIZE); assert.match(r.warnings.join(" "), /batch_size "Why" is not an integer/); }); test("batch_size out of range or fractional is rejected", () => { for (const bad of ["0", "21", "2.5", "-3"]) { const r = parsePromptRequest(`x batch_size=${bad}`); assert.equal(r.batchSize, DEFAULT_BATCH_SIZE, `batch_size=${bad}`); assert.ok(r.warnings.length > 0); } }); test("batch_size at the bounds is accepted", () => { assert.equal(parsePromptRequest("x batch_size=1").batchSize, 1); assert.equal(parsePromptRequest("x batch_size=20").batchSize, 20); }); test("a multi-word parse_model is rejected", () => { const r = parsePromptRequest('x parse_model="two words"'); assert.equal(r.parseModel, DEFAULT_PARSE_MODEL); assert.match(r.warnings.join(" "), /not a single token/); }); test("a report path escaping the cwd is rejected", () => { const r = parsePromptRequest("x report=../secrets.md"); assert.equal(r.reportPath, undefined); assert.match(r.warnings.join(" "), /escapes the working directory/); }); test("a report path that is not .md is rejected", () => { const r = parsePromptRequest("x report=./out.txt"); assert.equal(r.reportPath, undefined); assert.match(r.warnings.join(" "), /not a .md file/); }); test("a multi-word report path is rejected", () => { const r = parsePromptRequest('x report="my report.md"'); assert.equal(r.reportPath, undefined); assert.match(r.warnings.join(" "), /not a single token/); }); test("content_types keeps the valid values and warns about the rest", () => { const r = parsePromptRequest("x content_types=post,audio"); assert.deepEqual(r.contentTypes, ["post"]); assert.match(r.warnings.join(" "), /content_types value\(s\) ignored/); }); test("regex=true is honoured; anything else is false", () => { assert.equal(parsePromptRequest("x regex=true").regex, true); assert.equal(parsePromptRequest("x regex=yes").regex, false); assert.equal(parsePromptRequest("x").regex, false); }); test("empty input warns rather than producing a silent no-op plan", () => { const r = parsePromptRequest(""); assert.equal(r.query, ""); assert.equal(r.link, undefined); assert.match(r.warnings.join(" "), /no query and no link/); }); test("a link with no question is not a warning — the link supplies the query", () => { const r = parsePromptRequest("https://site.example/?q=a"); assert.deepEqual(r.warnings, []); }); // ─── The MCP prompt form shares the validators ─── test("prompt arguments route through the same validation", () => { const r = requestFromArguments({ query: "k cups", channels: "chan-a, chan-b", group: "other", batch_size: "Why", report_path: "../x.md", }); assert.equal(r.query, "k cups"); assert.deepEqual(r.channels, ["chan-a", "chan-b"]); assert.deepEqual(r.groups, ["other"]); assert.equal(r.batchSize, DEFAULT_BATCH_SIZE); assert.equal(r.reportPath, undefined); assert.equal(r.warnings.length, 2); }); test("prompt arguments accept a link and clean it", () => { const r = requestFromArguments({ link: "." }); assert.equal(r.link, "https://site.example/x"); }); test("a non-url link argument is reported, not passed through", () => { const r = requestFromArguments({ link: "not-a-url", query: "x" }); assert.equal(r.link, undefined); assert.match(r.warnings.join(" "), /is not an http\(s\) URL/); }); // ─── Rendering ─── test("warnings render as a leading block, and nothing when clean", () => { assert.equal(renderWarnings([]), ""); const out = renderWarnings(["a", "b"]); assert.ok(out.startsWith("⚠ a\n⚠ b")); assert.ok(out.endsWith("\n\n")); }); // ─── The prompt form refuses a shredded request ─── test("the exact Claude Code shredding of a real request is detected", () => { // What `text.trim().split(/\s+/)` + zipObject(declaredArgs, tokens) does to // "This search finds deleted videos. Why might have Rekieta privated these? // Look for context around each one." — nine words kept, the rest dropped. const shredded = { query: "This", link: "search", channel: "finds", channels: "deleted", group: "videos.", directive: "Why", batch_size: "might", parse_model: "have", report_path: "Rekieta", }; const { problems, reassembled } = validateSweepArguments(shredded); const named = problems.map((p) => p.arg).sort(); // "have" in parse_model is not flagged — it has the shape of a model name. // Three independent signals is already conclusive; the check only has to // fire, not catch every slot. assert.deepEqual(named, ["batch_size", "link", "report_path"]); // The surviving words come back in the order they were typed, so the user // can recognise their own sentence and see where it was cut. assert.equal( reassembled, "This search finds deleted videos. Why might have Rekieta", ); }); test("a legitimate form-client invocation trips nothing", () => { for (const args of [ { query: "k cups" }, { query: "k cups", group: "other" }, { query: "k cups", channels: "chan-a,chan-b", batch_size: "12" }, { query: "deleted videos", link: "https://site.example/?qt=abc&fav=deleted", parse_model: "haiku", report_path: "./out.md", directive: "key claims & contradictions", }, ]) { assert.deepEqual(validateSweepArguments(args).problems, [], JSON.stringify(args)); } }); test("a genuinely bad value is refused even from a form client", () => { // The prompt path is strict where the tool path is forgiving: this is the // value that used to render into `ceil(N / Why)`. assert.deepEqual( validateSweepArguments({ query: "k cups", batch_size: "Why" }).problems.map((p) => p.arg), ["batch_size"], ); assert.deepEqual( validateSweepArguments({ query: "x", report_path: "../secrets.md" }).problems.map((p) => p.arg), ["report_path"], ); });