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

My Claude Code mods and skills, as a plugin marketplace.
/plugin marketplace add bilal-psd/skills
/plugin install dev-band@bilal-skills
Mods live in mods/, skills in skills/.
| Plugin | Kind | What it does |
|---|---|---|
dev-band | mod | A 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-card | mod | Adds ask_many: the built-in question dialog for 5+ questions, asked one after another. |
sitrep | skill | /sitrep: a 12-line status of the session — now, last step, needs you, next. |
wrap | skill | /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.
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).MIT
hooks/register.tsx 77 lines1import 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