2026-08-03 Inspector Quick Reply Gets a Menu Mined From Its Own History

What I set out to do

Add item selection to the SillyTavern Inspector quick reply (the Gen Prompts preset button that suspends the card’s stay-in-character rules and asks an OOC question), so the common questions are one click instead of retyping them into the /input box every time.

What I actually did

/buttons turned out to be the right primitive: it takes a JSON labels list, blocks on a popup, and returns the clicked label into the pipe (empty string on cancel). Rather than invent menu items, I mined the chats for what had actually been asked. 513 Inspector sends across data/default-user, 21 unique questions, and the distribution was lopsided — roughly 130 of them were some phrasing of “what’s the point of this roleplay, what are the possible directions, what are the implications”. The rest clustered into: the same question scoped to a specific pair, hidden wants and attractions, what the side cast can do, spell out what just happened, quote the card rules verbatim, and what opens up if the card were less restrictive. Non-Inspector OOC: messages in the same chats corroborated the same clusters. Those seven became the menu; anything unmatched falls through to the original input box, so the old behaviour is still there as “Custom question…“.

Two STscript details made the script flat instead of a nested /if chain. Non-matching /if branches don’t clobber a variable that only matching branches write, and /var key=q inside a closure walks up the scope chain to the /let q declared in the parent — so eight sibling one-liners can each conditionally set the same variable. Fall-through detection is then just rule=not on that variable.

Validated before writing anything: SillyTavern.getContext() exposes SlashCommandParser, so the whole thing parses in the browser console without executing, and executeSlashCommandsWithOptions runs branch-logic variants that stop short of /send. Confirmed the cancel path aborts, the pair branch interpolates {{pipe}} into its question, and — the one I actually worried about — a typed question containing quotes or a | is substituted at runtime rather than re-parsed, so it can’t break the pipeline. Saved through /api/quick-replies/save, backup of the old set kept.

What was striking

The menu is better than one I would have designed because I didn’t design it. Sitting in my own chat logs was a frequency-ranked list of what I actually want to know when I break character, and it was not what I would have guessed — I would have written something tidy about “motivations” and “continuity” and missed that the overwhelming real question is “where can this go from here”. Usage history is a spec, if you bother to read it.

Also worth keeping: ST’s parser is reachable from the page for a dry run. Any nontrivial quick reply can be syntax- and branch-checked without firing a generation into a live chat.