@eko24ive/pi-ask
@eko24ive/pi-ask is an ask tool that cares about your answers.
It lets an agent pause, ask structured questions in a terminal UI, and continue with normalized answers instead of guessing.
https://github.com/user-attachments/assets/a74827fb-6093-412a-895f-69f9514bc4fe
Install
pi install npm:@eko24ive/pi-ask
You can also install from git:
pi install git:github.com/eko24ive/pi-ask
Features
Once installed, this package gives the agent a native way to ask for clarification instead of guessing.
- 🧭 Familiar ask-style interface: tabbed questions, single/multi select, and preview mode
- ✍️ Inline free-form
Type your ownanswers - 📝 Question-level and option-level notes
- 👀 Review tab with
Submit,Elaborate, andCancel - 💬 Elaboration flow to capture note-based clarification before final submission
- ⌨️ Fast keyboard-first interaction (also mobile-friendly in remote sessions)
Feature walkthrough
Multi-question flow (tabs)
Move across several related questions in one ask flow.
Single-select question
Pick one option when answers are mutually exclusive.
Multi-select question
Choose multiple options when several answers apply.
Preview mode
Use a dedicated preview pane when options need richer detail.
Custom answer (Type your own)
Capture free-form input inline without leaving the flow.
Option notes
Attach clarification notes to a specific option (n).
Question notes
Add notes at question level for broader context (Shift+N).
Review tab — Submit
Review all answers before returning them to the agent.
Review tab — Elaborate
Ask the agent to elaborate on notes before finalizing choices.
Use
After installation, the package registers one tool: ask_user.
That is enough for the agent to gain this clarification capability. The extension already injects prompt guidance that encourages the agent to call ask_user when requirements are ambiguous or user preference matters, instead of guessing.
You can still add your own agent instruction if you want to further reinforce when and how the tool should be used.
Use type: "preview" only when every option includes preview text for a dedicated preview pane. Descriptions alone are not enough.
Optional extra agent instruction
If you need clarification, prefer `ask_user` over guessing.
Ask 1-3 concise questions.
Use short tab labels.
Prefer 2-4 options per question.
Include descriptions for each option.
Use `type: "single"` unless multiple options can genuinely apply.
Use `type: "multi"` only when the user may need to select several answers.
Always include a non-empty `value` for every option.
Use `type: "preview"` only when every option includes non-empty `preview` text for the preview panel.
After answers are returned, continue the task using those answers explicitly.
Tool input
ask_user accepts:
{
title?: string,
questions: [
{
id: string,
label?: string,
prompt: string,
type?: "single" | "multi" | "preview",
required?: boolean,
options: [
{
value: string,
label: string,
description?: string,
preview?: string
}
]
}
]
}
Example tool call payload
{
"title": "Implementation preferences",
"questions": [
{
"id": "style",
"label": "Style",
"prompt": "How should I frame the next prompt?",
"type": "single",
"options": [
{
"value": "minimal",
"label": "Minimal",
"description": "A short, direct question with few options."
},
{
"value": "balanced",
"label": "Balanced",
"description": "A standard prompt with a bit more context."
},
{
"value": "rich",
"label": "Rich",
"description": "A more descriptive prompt with extra detail."
}
]
},
{
"id": "frameworks",
"label": "Frontend",
"prompt": "Which frontend frameworks have you used?",
"type": "multi",
"options": [
{ "value": "react", "label": "React", "description": "Most popular UI library" },
{ "value": "vue", "label": "Vue", "description": "Progressive framework for building UIs" },
{ "value": "svelte", "label": "Svelte", "description": "Compiler-based approach" }
]
}
]
}
Returned result
The returned result now also includes:
{
error?: {
kind: "invalid_input"
issues: Array<{ path: string; message: string }>
}
mode: "submit" | "elaborate"
continuation?: {
strategy: "refine_only" | "resume"
affectedQuestionIds: string[]
preservedAnswers: Record<string, AskResultAnswer>
questionStates: Record<string, {
status: "answered" | "needs_clarification" | "unanswered"
}>
}
elaboration?: {
instruction: string
nextAction: "clarify" | "clarify_then_reask"
items: Array<
| {
target: { kind: "question" }
question: {
id: string
label: string
prompt: string
type: "single" | "multi" | "preview"
options: Array<{
value: string
label: string
description?: string
preview?: string
}>
}
answered: boolean
answer?: AskResultAnswer
note: string
}
| {
target: { kind: "option"; optionValue: string }
question: {
id: string
label: string
prompt: string
type: "single" | "multi" | "preview"
options: Array<{
value: string
label: string
description?: string
preview?: string
}>
}
option: {
value: string
label: string
description?: string
preview?: string
}
selected: boolean
answered: boolean
answer?: AskResultAnswer
note: string
}
>
}
}
The returned details.answers[questionId] object may include:
{
values: string[]
labels: string[]
indices: number[]
customText?: string
note?: string
optionNotes?: Record<string, string>
}
Behavior details:
- payloads are validated before the UI opens: question ids must be unique, option values must be unique within a question, and required text fields must not be blank
- preview questions require preview text for every option; descriptions alone are not enough, and invalid preview payloads suggest adding preview text or switching to
type: "single" requireddefaults tofalseand remains advisory only- question-level notes are submitted whenever authored
- option notes can be authored for any active option during the UI flow
- only notes for currently selected options are included in the submitted result
- deselecting an option keeps its note in UI state, so re-selecting it restores the note
- empty note text clears the note
- on single-select questions, saving a free-form answer replaces selected options for that question
- on multi-select questions, saving a free-form answer keeps other selected options intact
- on multi-select questions, submitted
valuesandlabelskeep selected options first and append the free-form answer last when both exist - invalid payloads return
details.error.kind === "invalid_input"with structuredissuesand a transcript-friendlyInvalid ask_user payload:message - unanswered questions are omitted from
details.answers - in elaborate mode,
details.answerscontains only committed answers; note-only entries move intodetails.elaboration.items details.continuation.strategy === "refine_only"means the next ask should refine the current flow instead of restarting itdetails.continuation.preservedAnswerscontains prior answers that should be carried forward without re-askingdetails.continuation.affectedQuestionIdslists the only questions that should be revisiteddetails.continuation.questionStatesmarks each question asanswered,needs_clarification, orunanswered- submit review tab now offers
Submit,Elaborate, andCancel - on the review tab, number hotkeys
1,2, and3triggerSubmit,Elaborate, andCancel - while
SubmitorCancelis highlighted on the review tab, notes are previewed only for questions with selected answers - while
Elaborateis highlighted on the review tab, the preview expands to show all question notes and all option notes, including notes on unselected options - choosing
Elaboratefinishes immediately and returnsmode: "elaborate" details.elaboration.itemsincludes all question notes and all option notes, even for unselected options- every elaboration item carries the full normalized question and option list for that question, so the agent can resolve referential notes like
abovewithout relying on chat memory alone details.elaboration.instructionexplicitly tells the agent to answer the clarification directly first, then re-ask only the affected questions if needed- after clarification, agents should prefer another structured follow-up over plain-text multiple choice if a decision is still unresolved
- once prior answers narrow the branch, agents should bundle the next 2-3 related unresolved decisions into one follow-up ask when possible instead of asking a long chain of one-question follow-ups
- elaborate summary/result text now emits direct note-specific lines such as
User asked to elaborate on question "Which option would you like to select?" option "Option A" with note "why this one?" - when the free-form option is selected, it becomes an inline input row with the selected-tab background style spanning the full width
- while editing a note or free-form answer, arrow keys and
Tabstay inside the editor while text is present so the typing cursor can move naturally - when a note or free-form editor is empty,
Up/Downnavigate options andTab/Shift+Tab/Left/Rightnavigate tabs without closing the editor first - free-form answer editors support pi-style
@file path autocomplete for quickly mentioning project files Esccloses the editor and returns to navigation modeCtrl+Cdismisses the entire ask flow immediately, even from note/free-form editing, without saving the current draft- use
nfor the active option note andShift+Nfor the current question note Spacetoggles the active option on single-select questions too, but does not auto-advance
If pi runs without UI, ask_user returns a Needs user input message plus normalized pending questions in details.questions so a caller can re-ask them manually.
Local development
Run locally in pi
pi -e ./src/index.ts
Install dependencies
pnpm install
Development commands
pnpm format
pnpm lint
pnpm check
pnpm typecheck
pnpm test
Commit workflow
This repo uses lefthook, Commitizen, conventional commitlint, and semantic-release.
Recommended flow:
pnpm commit
Project layout
src/— TypeScript extension implementationtests/— behavior-focused testsdocs/— small docs set for contract and architecturedocs/media/— repository-only README media assets
Documentation
Docs stay intentionally small:
docs/README.md— indexdocs/contract.md— external behaviordocs/architecture.md— module boundaries and invariants








