---
slug: "mickyyy68-pi-ask"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/mickyyy68/pi-ask@main/README.md"
repo: "https://github.com/mickyyy68/pi-ask"
source_file: "README.md"
branch: "main"
---
# pi-ask

`pi-ask` adds an interactive `ask_user` tool to [Pi](https://github.com/earendil-works/pi-mono). It lets the model group related clarification questions into one terminal questionnaire instead of guessing or interrupting the user with several plain-text prompts.

![Grouped multi-select questionnaire in pi-ask](https://github.com/mickyyy68/pi-ask/raw/HEAD/demo-image.png)

## Features

- Ask **1–4 related questions** in one dialog.
- Present **2–5 choices** per question.
- Mix single-select and multi-select questions.
- Add a free-form custom answer to either selection mode.
- Edit an existing custom answer, or submit it blank to remove it.
- Return optional stable machine-readable values independently of display labels.
- Review all answers before submitting.
- Navigate with tabs, arrows, Enter, and Space.
- Render safely in narrow terminals with ANSI-aware wrapping.
- Run sequentially so multiple interactive tool calls cannot compete for focus.

```text
┌ Scope ✓ ─ Features ─ Review ┐
│ Which features should ship? │
│ [x] Linting                 │
│ [ ] Formatting              │
│ [x] Tests                   │
│ [ ] Write my own answer…    │
│     Continue →              │
│                             │
│ ↑↓ move • Space/Enter toggle│
│ Tab switch • Esc dismiss    │
└─────────────────────────────┘
```

## Installation

Install from npm:

```sh
pi install npm:@mickyyy68/pi-ask
```

Or install directly from GitHub:

```sh
pi install git:github.com/mickyyy68/pi-ask
```

Then restart Pi or run `/reload` in an existing interactive session.

From a local checkout:

```sh
pi install "$PWD"
```

## Tool

The package registers one tool:

```text
ask_user
```

Pi adds tool-specific guidance that encourages the model to group related questions, use multi-select only for non-exclusive choices, and provide stable values when labels are not reliable identifiers.

### Minimal example

```ts
ask_user({
  questions: [
    {
      question: "Which database should the project use?",
      options: [
        {
          value: "postgres",
          label: "PostgreSQL",
          description: "Full relational database with production tooling."
        },
        {
          value: "sqlite",
          label: "SQLite",
          description: "Embedded database with minimal setup."
        }
      ]
    }
  ]
})
```

### Grouped and multi-select example

```ts
ask_user({
  questions: [
    {
      id: "language",
      header: "Language",
      question: "Which language should the project use?",
      options: [
        { value: "typescript", label: "TypeScript" },
        { value: "python", label: "Python" }
      ]
    },
    {
      id: "features",
      header: "Features",
      question: "Which features should be included?",
      multiSelect: true,
      options: [
        { value: "auth", label: "Authentication" },
        { value: "api", label: "REST API" },
        { value: "jobs", label: "Background jobs" }
      ]
    }
  ]
})
```

## Input schema

```ts
interface AskUserInput {
  questions: Array<{
    id?: string;
    header?: string;
    question: string;
    multiSelect?: boolean;
    options: Array<{
      value?: string;
      label: string;
      description?: string;
    }>;
  }>;
}
```

| Field | Behavior |
| --- | --- |
| `questions` | Between 1 and 4 questions. |
| `id` | Optional stable question identifier; defaults to `q1`, `q2`, and so on. |
| `header` | Optional tab label, maximum 16 characters; defaults to `Q1`, `Q2`, and so on. |
| `question` | Non-empty question text. |
| `multiSelect` | Defaults to `false`. |
| `options` | Between 2 and 5 choices. |
| `value` | Optional stable value; defaults to the trimmed label. |
| `label` | Non-empty display label, maximum 60 characters. |
| `description` | Optional text rendered below the option. |

Question IDs must be unique within an invocation. Option values must be unique within their question. Runtime control labels such as `Write my own answer…` and `Continue →` are reserved.

## Stable values

Display labels are written for people and may change. Stable values are written for tools and workflows:

```ts
{ value: "postgres", label: "PostgreSQL" }
```

The result contains both values:

```json
{
  "value": "postgres",
  "label": "PostgreSQL",
  "custom": false
}
```

When `value` is omitted, the normalized label is used for both fields.

## Custom answers

Every question includes `Write my own answer…`.

- On a single-select question, a custom answer becomes the only selection.
- On a multi-select question, it is added alongside predefined selections.
- Reopen the custom row to edit an existing answer.
- Clear the editor and press Enter to remove the existing custom answer.
- Press Escape while editing to discard uncommitted changes and keep the existing answer.

A custom selection uses the submitted text as both its value and label and sets `custom: true`.

## Keyboard controls

| Key | Action |
| --- | --- |
| `↑` / `↓` | Move through options or review actions. |
| `Enter` | Select a single choice, toggle a multi-select choice, open/save custom input, advance, or submit. |
| `Space` | Toggle a multi-select choice or open the custom row. |
| `Tab` / `→` | Move to the next question or Review tab. |
| `Shift+Tab` / `←` | Move to the previous tab. |
| `Escape` | Leave custom editing or dismiss the questionnaire. |

Single-select answers advance automatically. Multi-select questions use a `Continue →` row. The Review tab refuses submission until every question has at least one selection.

## Result contract

A successful result stores the same selection shape for single- and multi-select questions:

```ts
interface AskUserDetails {
  answers: Array<{
    questionId: string;
    question: string;
    selections: Array<{
      value: string;
      label: string;
      custom: boolean;
    }>;
  }>;
  cancelled: false;
}
```

Example:

```json
{
  "answers": [
    {
      "questionId": "features",
      "question": "Which features should be included?",
      "selections": [
        { "value": "api", "label": "REST API", "custom": false },
        { "value": "Dark mode", "label": "Dark mode", "custom": true }
      ]
    }
  ],
  "cancelled": false
}
```

Dismissal discards unsubmitted choices:

```json
{
  "answers": [],
  "cancelled": true
}
```

Validation and environment failures add an `error` field. Current codes are:

- `no_ui`
- `aborted`
- `no_questions`
- `too_many_questions`
- `too_few_options`
- `too_many_options`
- `empty_question`
- `empty_question_id`
- `empty_header`
- `header_too_long`
- `empty_option_label`
- `label_too_long`
- `empty_option_value`
- `duplicate_question_id`
- `duplicate_option_value`
- `reserved_label`

`pi-ask` requires Pi's interactive TUI. In print, JSON, and RPC modes it returns `no_ui` and tells the model to ask in plain text. Pi may handle cancellation before dispatching the tool; aborts received while the questionnaire is open are returned as `aborted` with no partial answers.

## Why sequential execution?

Pi can execute independent sibling tool calls concurrently. Interactive dialogs should not overlap or compete for keyboard focus, so `ask_user` declares:

```ts
executionMode: "sequential"
```

This does not affect normal parallel execution for other tools.

## Development

Requirements:

- Node.js 22.19 or newer
- npm

```sh
npm install
npm run check
npm test
npm run pack:check
```

Run every validation step together:

```sh
npm run validate
```

See [CONTRIBUTING.md](https://github.com/mickyyy68/pi-ask/blob/HEAD/CONTRIBUTING.md) for the source layout and contribution expectations.

## Limitations

The initial release intentionally does not include preview panes, localization, per-option notes, or non-TUI questionnaire transport. It focuses on a compact, predictable grouped-question flow.

## License

[MIT](https://github.com/mickyyy68/pi-ask/tree/HEAD/LICENSE)
