8.3 KiB
Question Tool Design
Date: 2026-04-09
Project: /home/alex/dotfiles
Target file: .pi/agent/extensions/question.ts
Goal
Add a tracked pi extension that gives the agent a single question tool for asking either one question or multiple questions in interactive mode, while always preserving a final user escape hatch: "Something else…" opens inline free-text entry when none of the listed options fit.
Context
- Pi supports custom tools through TypeScript extensions placed in auto-discovered extension directories.
- This dotfiles repo already tracks pi configuration under
.pi/agent/. - The working extension directory
.pi/agent/extensions/is currently empty. - Pi’s upstream examples already include:
- a single-question
question.tsexample - a multi-question
questionnaire.tsexample
- a single-question
- The requested tool should combine those use cases into one obvious agent-facing tool.
User-Approved Requirements
- The tool must be tracked in this repo at:
/home/alex/dotfiles/.pi/agent/extensions/question.ts
- The tool name should be:
question
- The tool must support both:
- a single question
- multiple questions in one interaction
- Every question is multiple-choice, but the UI must always append a final choice:
- "Something else…"
- Choosing "Something else…" must allow direct user text entry.
- Question options should support machine-friendly values and user-facing labels:
{ value, label, description? }
- This should be a unified tool, not separate
questionandquestionnairetools.
Recommended Approach
Implement a single extension modeled after pi’s upstream question.ts and questionnaire.ts examples:
- one registered tool:
question - one parameter shape:
questions: Question[] - one UI that adapts to question count:
- single-question picker for
questions.length === 1 - multi-question review flow for
questions.length > 1
- single-question picker for
This keeps the agent-facing API simple while still supporting richer user clarification flows.
Tool Contract
The extension will register a tool with this conceptual input shape:
{
questions: Array<{
id: string;
label?: string;
prompt: string;
options: Array<{
value: string;
label: string;
description?: string;
}>;
}>;
}
Field intent
id: stable identifier for the answerlabel: short summary label for tabs/review UI; defaults toQ1,Q2, etc.prompt: the full question shown to the useroptions: predefined choices the model wants the user to pick from
Normalization rules
Before rendering the UI:
- Ensure at least one question exists.
- Ensure each question has a usable short label.
- Preserve the provided predefined options as-is.
- Append a final synthetic option to every question:
- label:
Something else… - behavior: switch into inline text entry
- label:
- Do not require the model to explicitly include the synthetic option.
Interaction Design
Single-question mode
When exactly one question is provided:
- display the prompt
- display numbered predefined options
- automatically display the final appended option:
Something else…
- selecting a predefined option completes the tool immediately
- selecting
Something else…opens inline free-text entry Escin the picker cancels the toolEscin text entry exits text entry and returns to the option list
Multi-question mode
When multiple questions are provided:
- show one question at a time
- allow tab or left/right navigation between questions
- append
Something else…to every question - after answering one question, move to the next question
- include a final review/submit step summarizing all current answers
- allow navigating back to change answers before final submission
- submit only from the review step
This provides a guided flow without requiring separate tools.
Answer Model
The tool result should always remain structured.
Conceptual result shape:
{
questions: Question[];
answers: Array<{
id: string;
value: string;
label: string;
wasCustom: boolean;
index?: number;
}>;
cancelled: boolean;
}
Predefined option answers
For a predefined choice:
value= the provided option valuelabel= the provided option labelwasCustom=falseindex= 1-based index of the selected predefined option
Custom answers via “Something else…”
For a typed answer:
value= typed textlabel= typed textwasCustom=trueindexis omitted
This gives the agent consistent structured data while preserving user freedom.
Rendering
The extension should provide readable tool renderers:
renderCall
Show:
- tool name (
question) - question count
- short labels or summary where useful
renderResult
Show:
Cancelledwhen the user aborts- one concise success line per answered question
- whether an answer was predefined or custom when helpful
The rendering should remain compact in normal use and not dump full raw JSON unless the default fallback is needed.
Error Handling
The tool should return structured results for expected user/runtime states instead of throwing.
Non-interactive mode
If pi is running without interactive UI support:
- return a clear text result indicating UI is unavailable
- mark the interaction as
cancelled: truein details - do not crash the session
Invalid input
If questions is empty:
- return a clear text result like
Error: No questions provided - include a structured details payload with
cancelled: true
User cancel
If the user cancels from the picker or review flow:
- return
cancelled: true - do not throw an exception
Empty custom text
If the user enters free-text mode and submits an empty value:
- do not accept an empty answer
- keep the user in text-entry mode until they provide non-empty text or press
Esc - avoid returning meaningless blank answers to the model
File Structure
Implementation stays in one file unless complexity clearly justifies splitting later:
- Create:
/home/alex/dotfiles/.pi/agent/extensions/question.ts
Internal sections inside the file should stay logically separated:
- types and schemas
- question normalization helpers
- single-question UI flow
- multi-question UI flow
- tool registration
- call/result rendering
Loading and Usage
Because the file will live in an auto-discovered project extension directory, the expected activation flow is:
- start pi from the dotfiles repo or a directory where the project extension is in scope
- use
/reloadif pi is already running - allow the model to call
questionwhen clarification is needed
Testing Strategy
No dedicated automated test harness is required for the first version.
Manual verification should cover:
- Single question, predefined answer
- tool returns selected option value/label
- Single question, custom answer
- selecting
Something else…opens text entry and returns typed text
- selecting
- Single question, cancel
- cancellation returns structured cancelled result
- Multi-question, all predefined
- step-through and final review work correctly
- Multi-question, mixed predefined/custom
- at least one typed answer and one predefined answer are preserved correctly
- Multi-question, edit before submit
- user can revisit and change answers before final submission
- Empty custom submission
- blank text is rejected or bounced back safely
- Non-interactive mode
- tool returns a clear UI-unavailable result
Non-Goals
The first version will not add:
- separate text-only question types
- nested conditional question trees
- validation rules beyond basic non-empty custom text handling
- persistence beyond normal pi session/tool result storage
- a separate
questionnairetool name
Acceptance Criteria
The work is complete when:
.pi/agent/extensions/question.tsexists in this repo- pi discovers the extension via project auto-discovery
- the agent has a single
questiontool - the tool supports both one-question and multi-question flows
- every question automatically ends with
Something else… - selecting
Something else…allows direct typed input - results are structured and distinguish custom answers from predefined ones
- cancel/error states return cleanly without crashing the session