280 lines
8.3 KiB
Markdown
280 lines
8.3 KiB
Markdown
# 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.ts` example
|
||
- a multi-question `questionnaire.ts` example
|
||
- The requested tool should combine those use cases into one obvious agent-facing tool.
|
||
|
||
## User-Approved Requirements
|
||
|
||
1. The tool must be tracked in this repo at:
|
||
- `/home/alex/dotfiles/.pi/agent/extensions/question.ts`
|
||
2. The tool name should be:
|
||
- `question`
|
||
3. The tool must support both:
|
||
- a single question
|
||
- multiple questions in one interaction
|
||
4. Every question is multiple-choice, but the UI must always append a final choice:
|
||
- **"Something else…"**
|
||
5. Choosing **"Something else…"** must allow direct user text entry.
|
||
6. Question options should support machine-friendly values and user-facing labels:
|
||
- `{ value, label, description? }`
|
||
7. This should be a unified tool, not separate `question` and `questionnaire` tools.
|
||
|
||
## 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`
|
||
|
||
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:
|
||
|
||
```ts
|
||
{
|
||
questions: Array<{
|
||
id: string;
|
||
label?: string;
|
||
prompt: string;
|
||
options: Array<{
|
||
value: string;
|
||
label: string;
|
||
description?: string;
|
||
}>;
|
||
}>;
|
||
}
|
||
```
|
||
|
||
### Field intent
|
||
|
||
- `id`: stable identifier for the answer
|
||
- `label`: short summary label for tabs/review UI; defaults to `Q1`, `Q2`, etc.
|
||
- `prompt`: the full question shown to the user
|
||
- `options`: predefined choices the model wants the user to pick from
|
||
|
||
### Normalization rules
|
||
|
||
Before rendering the UI:
|
||
|
||
1. Ensure at least one question exists.
|
||
2. Ensure each question has a usable short label.
|
||
3. Preserve the provided predefined options as-is.
|
||
4. Append a final synthetic option to every question:
|
||
- label: `Something else…`
|
||
- behavior: switch into inline text entry
|
||
5. 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
|
||
- `Esc` in the picker cancels the tool
|
||
- `Esc` in 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:
|
||
|
||
```ts
|
||
{
|
||
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 value
|
||
- `label` = the provided option label
|
||
- `wasCustom` = `false`
|
||
- `index` = 1-based index of the selected predefined option
|
||
|
||
### Custom answers via “Something else…”
|
||
|
||
For a typed answer:
|
||
|
||
- `value` = typed text
|
||
- `label` = typed text
|
||
- `wasCustom` = `true`
|
||
- `index` is 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:
|
||
|
||
- `Cancelled` when 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: true` in 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:
|
||
|
||
1. types and schemas
|
||
2. question normalization helpers
|
||
3. single-question UI flow
|
||
4. multi-question UI flow
|
||
5. tool registration
|
||
6. call/result rendering
|
||
|
||
## Loading and Usage
|
||
|
||
Because the file will live in an auto-discovered project extension directory, the expected activation flow is:
|
||
|
||
1. start pi from the dotfiles repo or a directory where the project extension is in scope
|
||
2. use `/reload` if pi is already running
|
||
3. allow the model to call `question` when clarification is needed
|
||
|
||
## Testing Strategy
|
||
|
||
No dedicated automated test harness is required for the first version.
|
||
|
||
Manual verification should cover:
|
||
|
||
1. **Single question, predefined answer**
|
||
- tool returns selected option value/label
|
||
2. **Single question, custom answer**
|
||
- selecting `Something else…` opens text entry and returns typed text
|
||
3. **Single question, cancel**
|
||
- cancellation returns structured cancelled result
|
||
4. **Multi-question, all predefined**
|
||
- step-through and final review work correctly
|
||
5. **Multi-question, mixed predefined/custom**
|
||
- at least one typed answer and one predefined answer are preserved correctly
|
||
6. **Multi-question, edit before submit**
|
||
- user can revisit and change answers before final submission
|
||
7. **Empty custom submission**
|
||
- blank text is rejected or bounced back safely
|
||
8. **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 `questionnaire` tool name
|
||
|
||
## Acceptance Criteria
|
||
|
||
The work is complete when:
|
||
|
||
1. `.pi/agent/extensions/question.ts` exists in this repo
|
||
2. pi discovers the extension via project auto-discovery
|
||
3. the agent has a single `question` tool
|
||
4. the tool supports both one-question and multi-question flows
|
||
5. every question automatically ends with `Something else…`
|
||
6. selecting `Something else…` allows direct typed input
|
||
7. results are structured and distinguish custom answers from predefined ones
|
||
8. cancel/error states return cleanly without crashing the session
|