# Structured Questions

> How to use ask_user_question — the single operator-input channel for every interview question, including free-form fields via allow_other=true.

- Skill: `purpleailab/structured-questions` (Agent Skill)
- Install (CLI): `npx skillmds@latest add purpleailab/structured-questions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/purpleailab/structured-questions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: purpleailab (https://skillmd.com/u/purpleailab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/purpleailab/structured-questions

---


# Structured Operator Questions (`ask_user_question`)

The Soundwave interview has exactly **one** question channel:

- `ask_user_question` — structured picker rendered in the CLI. For
  free-form dimensions (organization name, IP ranges, contact addresses)
  set `allow_other=true` and the picker appends a free-text fallback the
  operator can type into.

There is no prose-question path. Every operator-facing question goes
through this tool.

## When to call `ask_user_question`

EVERY operator-facing question. There is no prose-question path. Provide
2–6 best-guess options for the dimension and always set `allow_other=true`
so the operator can override with a custom answer when none of your
predefined options fit.

Typical dimensions:
- **Engagement type**: External / Internal / Hybrid / Assumed-breach / Physical
- **Attack class**: Web / Cloud / AD / Mixed
- **Scope window**: Business hours / 24x7 / Custom
- **Posture choices**: OPSEC level (Loud / Quiet / Stealth)
- **Confirmations**: Two-option Yes / No after a derived assumption
- **Phase selection**: Multi-select over kill-chain phases (set `multi_select=true`)
- **Free-form fields** (organization / IP range / contact): still use the tool —
  list 2–4 plausible guesses + `allow_other=true`, the operator types the
  actual value via the Other fallback if your guesses miss.

The tool pauses the run while the operator picks, then resumes with the
chosen `label` (or list of labels for multi-select, or free text when the
operator chose `Other`). Treat the returned value as authoritative.

## Habits (the typed tool signature already encodes the schema)

- Mark the most common option with a trailing ` (Recommended)` on its `label`
- **Never invent an `Other` option yourself** — set `allow_other=true` and
  the picker appends a free-text fallback that returns the operator's text
  verbatim
- For multi-select questions, set `multi_select=true`; the tool returns the
  list of chosen labels (in selection order)

## Examples

### Single-select, with recommendation
```python
ask_user_question(
    question="What is the operator's posture for this engagement?",
    header="OPSEC",
    options=[
        {"label": "Quiet (Recommended)", "description": "Minimize detection signals; rate-limited recon"},
        {"label": "Loud",                "description": "Speed over stealth; full-rate scans permitted"},
        {"label": "Stealth",             "description": "Avoid detection at all costs; manual cadence"},
    ],
)
```

### Confirmation with `allow_other` for custom note
```python
ask_user_question(
    question="Is the testing window strictly business hours (09:00–18:00, client TZ)?",
    header="Window",
    options=[
        {"label": "Yes", "description": "Business hours only"},
        {"label": "No",  "description": "24x7 testing permitted"},
    ],
    allow_other=True,  # operator can type a custom window if neither fits
)
```

### Multi-select for kill-chain phases
```python
ask_user_question(
    question="Which kill-chain phases are in scope?",
    header="Phases",
    options=[
        {"label": "Recon",        "description": "Passive + active enumeration"},
        {"label": "Exploitation", "description": "Initial access via discovered weaknesses"},
        {"label": "Post-exploit", "description": "Privilege escalation, lateral movement, C2"},
        {"label": "Exfiltration", "description": "Crown-jewel retrieval simulation"},
    ],
    multi_select=True,
)
```

## Anti-patterns

- Asking via prose (chat message) instead of `ask_user_question` — even
  free-form fields go through the tool. Provide 2–4 best-guess options
  + `allow_other=true` so the operator can type a custom answer if your
  guesses miss. The tool is the ONLY operator-input channel.
- Adding an `"Other"` entry to `options` manually — set `allow_other=true`
  and the picker appends the free-text fallback for you
- Header longer than 12 chars (`"Engagement Type"` → use `"Eng. type"`)
- Re-asking the same dimension after the operator already answered — the
  returned value is authoritative; record it and move to the next dimension
- Pausing for per-document approval after writing RoE / CONOPS /
  Deconfliction — there is no approval gate between documents. The
  approval moments are (a) each `ask_user_question` picker during the
  interview, and (b) the final bundle summary right before
  `complete_engagement_planning`.

