SLOPSHOPPER

question-card

ask_many: the built-in question dialog for more than 4 questions

newguardtool
v0.2.0MITupdated 2026-10-10bilal-psd/skills/mods/question-card
A shopper browsing a rack in a slop shop
README

skills

My Claude Code mods and skills, as a plugin marketplace.

/plugin marketplace add bilal-psd/skills
/plugin install dev-band@bilal-skills

What's here

Mods live in mods/, skills in skills/.

PluginKindWhat it does
dev-bandmodA band above the prompt: git branch and changes, 5h/7d usage limits, prompt-cache warmth. Warns when context gets big (c compact, x clear, l later). After 30 idle minutes it unfolds a sitrep of the session; /sitrep-card writes one now.
question-cardmodAdds ask_many: the built-in question dialog for 5+ questions, asked one after another.
sitrepskill/sitrep: a 12-line status of the session — now, last step, needs you, next.
wrapskill/wrap: updates the work plan and AGENTS.md so a fresh session can pick up where this one left off.

Install any of them by name: /plugin install <name>@bilal-skills. Run /reload-plugins if a new command doesn't show up.

Notes

  • dev-band writes its sitrep from your own ~/.claude/skills/sitrep/SKILL.md if you have one, otherwise from the copy it ships.
  • dev-band's usage tracking is adapted from Vosssa/claude-usage-band (MIT, see mods/dev-band/LICENSE-usage-band).

License

MIT

Source 1 files
hooks/register.tsx 77 lines
1import type { EngineInterface, Register } from 'claude-code'
2
3// The built-in AskUserQuestion takes at most 4 questions. ask_many takes the same
4// shape with no cap and asks them one by one in the engine's own dialog.
5const TOOL = 'mcp__question-card__ask_many'
6
7type Option = { label: string; description?: string }
8type Question = { question: string; header?: string; options?: Option[]; multiSelect?: boolean }
9
10const DESCRIPTION = `Ask the user more than 4 questions in the built-in question dialog, one after another. Use this instead of AskUserQuestion only when you have 5 or more questions; for 1 to 4, use AskUserQuestion. Same input shape as AskUserQuestion: each question ends in "?", has a short header (12 chars max) and 2 to 4 options with concise labels; the user can always type their own answer under "Other". Put a recommended option first and add "(Recommended)" to its label. Returns every answer; if the user dismisses the dialog, you get the answers given so far.`
11
12const SCHEMA = {
13  type: 'object',
14  properties: {
15    questions: {
16      type: 'array',
17      minItems: 1,
18      items: {
19        type: 'object',
20        properties: {
21          question: { type: 'string', description: 'The full question, ending in a question mark' },
22          header: { type: 'string', description: 'Short chip label, 12 characters at most' },
23          options: {
24            type: 'array',
25            minItems: 2,
26            maxItems: 4,
27            items: {
28              type: 'object',
29              properties: {
30                label: { type: 'string' },
31                description: { type: 'string' },
32              },
33              required: ['label'],
34            },
35          },
36          multiSelect: { type: 'boolean' },
37        },
38        required: ['question', 'options'],
39      },
40    },
41  },
42  required: ['questions'],
43}
44
45async function askAll($: EngineInterface, questions: Question[]) {
46  const lines: string[] = []
47  for (const [i, q] of questions.entries()) {
48    const labels = (q.options ?? []).map(o => o.label).slice(0, 4)
49    const header = q.header ? q.header.slice(0, 12) : `${i + 1} of ${questions.length}`
50    try {
51      const answer = await $.ui.ask(q.question, {
52        options: labels,
53        header,
54        ...(q.multiSelect ? { multiSelect: true as const } : {}),
55      })
56      lines.push(`${i + 1}. ${q.question} → ${answer}`)
57    } catch {
58      lines.push(`The user dismissed the dialog at question ${i + 1}; questions ${i + 1}-${questions.length} are unanswered.`)
59      break
60    }
61  }
62  return lines.join('\n')
63}
64
65export const register: Register = on => {
66  on('session.start', async ($, e, next) => {
67    await $.tool.register({ name: 'ask_many', description: DESCRIPTION, inputSchema: SCHEMA })
68    return next(e)
69  })
70
71  on('tool.call', { tool: TOOL }, async ($, e) => {
72    const questions = (e as unknown as { questions?: Question[] }).questions ?? []
73    if (!questions.length) return { deny: 'ask_many needs at least one question.' }
74    return { result: await askAll($, questions) }
75  })
76}
77