@qmahyar/pi-ask
Structured choice and text forms for agent-user decisions. ask_user extension for the pi coding agent.
Package details
Install @qmahyar/pi-ask from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@qmahyar/pi-ask- Package
@qmahyar/pi-ask- Version
1.4.0- Published
- Aug 22, 2026
- Downloads
- 1,095/mo · 33/wk
- Author
- qmahyar
- License
- MIT
- Types
- extension
- Size
- 148.4 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./src/extension.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@qmahyar/pi-ask
Structured choice + text forms for the pi coding agent — when the agent needs your input before continuing, it pauses, shows one focused form, and resumes with structured answers.
A single ask_user tool: no bloat, no extra packages, no build step. Sourced from the same UI lineage as supi's ask-user, maintained here as a standalone pi package.
Features
- Choice and text questions — single-choice, multi-select, freeform text
- Up to 10 questions per form — one decision handoff instead of scattered interruptions
- Recommendations/prefill — agent suggests, you can change
- Trade-off details — optional per-option descriptions, consequences, code samples
- Comments at every level — per question, option, or whole form
- Review before submit — inspect and edit every answer
- Structured outcome —
submittedwhen complete;needs_discussionwhen unanswered, so the agent follows up instead of assuming - Session integration — completed forms appear as
ask_userentries, expandable in the transcript; each completed form is labeleddecision, visible and filterable in pi's/tree
Install
From npm:
pi install npm:@qmahyar/pi-ask
From a local checkout:
pi install ./path/to/pi-ask
Usage
Tell Pi when you want a structured handoff:
"Before scaffolding, ask me about the package manager and test runner." "Collect the product questions into one form with trade-offs beside each option."
The agent calls the ask_user tool. You answer in the form, review, submit. Pi resumes with structured responses.
Headless sessions (print/JSON/RPC/SDK — any non-TUI mode) error out with a clear message: ask_user requires the interactive TUI.
Behavior
- Cancel aborts the turn — pressing Esc cancels the form; the turn is aborted and the model sees
The user interaction was cancelled.(a shutdown aborts withThe user interaction was aborted.). - No implicit pre-selection — single-select questions start with nothing selected unless the agent passes a
recommendation; unanswered questions come back asneeds_discussionso the agent follows up instead of assuming. - Config edits need a reload — prompt-surface config is resolved at session start; edits to config files take effect after
/reloador in a new session. - Transcript integration — each completed form is appended as an
ask_usercustom entry (title + question count, expandable in the transcript) and the successful tool result is labeleddecision, visible and filterable in pi's/tree.
Limits
- 1-10 questions per form; 2-12 options per choice question.
- Character limits: question
header≤ 60,prompt≤ 4000, formtitle≤ 120,intro≤ 4000, textplaceholder≤ 200, optionlabel≤ 200, optiondescription≤ 1000, optiondetails≤ 2000, optionvalue≤ 200, questionid≤ 100, eachrecommendationentry ≤ 200. - Rejected with a clear validation error: duplicate question
ids, duplicate optionvalues, duplicate questionheaders, emptyid/header/prompt, empty optionvalue/label.
Deprecated fields
Top-level allowPartialSubmit and required on either question kind are rejected with an error pointing at the needs_discussion outcome for unanswered questions; choice allowOther is rejected with an error pointing at adding a text question; initial on either question kind is rejected with an error pointing at recommendation for suggested answers.
Text input
When the host TUI exposes a custom editor component, text answers use it; otherwise a built-in fallback editor handles input.
Config
Optional prompt-surface overrides and behavior settings live in pi config under an ask-user section:
- Global:
~/.pi/agent/pi-ask/config.json - Project (trust-gated):
.pi/pi-ask/config.json
{
"ask-user": {
"tools": {
"ask_user": {
"promptSurface": {
"description": "Custom tool description for the model",
"appendPromptGuidelines": ["One extra guideline"]
}
}
}
}
}
Additional prompt-surface fields:
$reset— array of field names to restore to package defaults before other overrides apply. At project scope,$resetrestores a field to the state resolved from global scope (package defaults + global overrides) — it does not discard global overrides — then the project's remaining overrides apply on top. Place it insidepromptSurface, next to the fields it resets.promptGuidelines— full-replace override of the guideline list;prependPromptGuidelines/appendPromptGuidelinesinsert string arrays before / after itdescription/promptSnippet— direct overrides of the tool description and prompt snippet
Behavior settings sit at the section level next to tools and follow the same resolution order (defaults ← global ← trusted project):
bell— sound the terminal bell (BEL) when a form opens. Defaulttrue.
{
"ask-user": {
"bell": false
}
}
Unknown keys and invalid values at every level (ask-user section, tools, tool name, promptSurface) are diagnosed with a warning instead of silently ignored — a typo'd field never silently no-ops. Config changes take effect after /reload or in a new session.
Events
The extension emits lifecycle events on pi.events, pi's inter-extension event bus — other extensions can listen with pi.events.on(...). Each form run is bracketed by:
pi-ask:ask-user:start—{ source: "pi-ask" }; emitted when a form opens.pi-ask:ask-user:end—{ source: "pi-ask" }; emitted when the form run ends.herdr:blocked—{ active: true, label: string }when the form goes on screen (labelis the form title, or"ask_user"when untitled);{ active: false }when it ends.
pi-ask:ask-user:end and herdr:blocked { active: false } are both emitted from a finally block, so they fire on every end path — submit, cancel, abort, and error. Listeners can rely on them for cleanup.
pi.events.on("pi-ask:ask-user:start", (payload: { source: "pi-ask" }) => {
// a decision form just opened
});
The herdr:blocked pair integrates with herdr, an external agent-supervisor tool: while a form is active, herdr reports the session as blocked (with the label as the message) and other agents can wait on it (--until blocked). This integration is optional — it is only meaningful when herdr is installed; otherwise the events are simply ignored.
API
The package ships TypeScript sources with no build step — pi loads them natively, so no compiled output is published.
import {
AskUserController,
AskUserParamsSchema,
AskUserValidationError,
normalizeQuestionnaire,
} from "@qmahyar/pi-ask";
- The root export
@qmahyar/pi-askand the back-compat subpath@qmahyar/pi-ask/apiexpose the same surface:normalizeQuestionnaire,AskUserValidationError,AskUserParamsSchema,AskUserController, plus the normalized questionnaire and response types. typeboxis a peer dependency (pi bundles it); to useAskUserParamsSchemaoutside pi, providetypeboxyourself.- The package ships TypeScript sources with no build step. Pi loads
.tsnatively; other consumers need a TS-aware runtime or loader — Node with--experimental-transform-types,tsx, or a bundler withallowImportingTsExtensions(plain--experimental-strip-typescannot handleAskUserController's parameter properties).
Development
npm install
npm test # vitest — validation/normalization checks
npm run check # npm pack --dry-run
License
MIT. This package is a derived work of supi (MIT, Marcel Richter); see LICENSE.