No description
  • TypeScript 97.4%
  • Nix 2.6%
Find a file
hayshin b2f7e53f61
All checks were successful
CI / Check, test, and validate package (push) Successful in 31s
docs(license): attribute upstream pi-ask-herdr source
- add portions copyright for lesetong and derived-from url in LICENSE
- keep hayshin as main copyright holder
2026-09-11 12:43:24 +05:00
.forgejo/workflows chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
src refactor: rename project to pi-ask 2026-09-10 22:56:54 +05:00
test chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
.gitignore chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
devenv.lock chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
devenv.nix chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
devenv.yaml chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
LICENSE docs(license): attribute upstream pi-ask-herdr source 2026-09-11 12:43:24 +05:00
package.json refactor: rename project to pi-ask 2026-09-10 22:56:54 +05:00
pnpm-lock.yaml chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
pnpm-workspace.yaml chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
README.md refactor: rename project to pi-ask 2026-09-10 22:56:54 +05:00
tsconfig.json chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00
vite.config.ts chore(tooling): adopt extension template 2026-09-10 19:04:30 +05:00

@hayshin/pi-ask

npm Pi extension

A Pi extension that adds an ask_user tool, with optional integration with Herdr agent state.

Pi loads TypeScript extensions directly, so this package publishes its source instead of creating a compiled build.

Structure

src/
  index.ts    # Pi auto-discovery entry point
  tool.ts     # Tool registration and parameter schema
  ui.ts       # Batch question wizard (text, confirm, select, multiselect)
  herdr.ts    # Herdr event-bus integration
  options.ts  # Option normalization
  types.ts    # Shared TypeScript types

What it does

  • Registers an ask_user tool that asks the user one or more questions in a single call. Each question can be:
    • free-form text (type: "text")
    • yes/no confirmation (type: "confirm")
    • single choice from a list (type: "select")
    • multiple choices from a list (type: "multiselect")
  • All questions are answered inside one wizard UI:
    • a progress header (Question 2/4 + progress bar) when there are multiple questions
    • Enter submits and moves to the next question
    • Esc steps back one layer (custom input → options → previous question)
    • Ctrl+C cancels the whole batch immediately
    • previously answered questions keep their answers when navigating back
    • long option labels and descriptions wrap instead of being truncated, including continuous CJK text and long unbroken words
  • Agent-facing results stay compact (1 -> answer), while the tool result UI retains each question alongside its answer for easy review.
  • For select and multiselect, an "Other (custom)" option is always available so the user can type their own answer.
  • For confirm, you can enable the same custom option with "allow_custom": true.
  • No extra slash command is registered — only the ask_user tool.
  • When Pi runs inside a Herdr pane, it reports the pane state as blocked while waiting, shows the pending question(s) as a sidebar token, and restores both after the user answers. (Herdr itself shows a notification for blocked panes, so the extension no longer sends its own.)

Install

pi install npm:@hayshin/pi-ask

Then reload Pi with /reload.

From git

pi install ssh://forgejo@ssh.hayshin.dev/agents/pi-ask.git

Manual

Copy or clone this directory into Pi's extensions folder:

# Global
cp -R pi-ask ~/.pi/agent/extensions/

# Or project-local
cp -R pi-ask .pi/extensions/

Then reload Pi with /reload, or test it directly with:

pi -e ~/.pi/agent/extensions/pi-ask/src/index.ts

Tool usage

A single question (one-element questions array):

{
  "questions": [
    {
      "question": "Which database should we use?",
      "type": "select",
      "options": ["PostgreSQL", "SQLite", "MySQL"]
    }
  ]
}

Multiple questions in one call:

{
  "questions": [
    {
      "question": "Which database should we use?",
      "type": "select",
      "options": [
        { "label": "PostgreSQL", "description": "Full-featured relational database" },
        { "label": "SQLite", "description": "Zero-config file database" },
        { "label": "MySQL", "description": "Widely used open-source RDBMS" }
      ]
    },
    {
      "question": "Which features should we enable?",
      "type": "multiselect",
      "options": ["dark mode", "notifications", "auto-save"]
    },
    {
      "question": "Any additional notes?",
      "type": "text"
    }
  ]
}

Parameters

Name Type Required Description
questions object[] yes Questions to ask, in order (min 1). See per-question fields.
timeout number no Total timeout in milliseconds for the whole batch

Per-question fields:

Name Type Required Description
question string yes The question to display
type string no "text", "confirm", "select", "multiselect" (default: text)
options string[] or object[] no Required when type is select or multiselect. Each item can be a string or { "label": string, "description"?: string }
default string no Prefilled value for text input
allow_custom boolean no For confirm: also offer "Other (custom)"

Key bindings

Key Action
Enter Submit current question and advance (on the last question: finish)
Esc Step back one layer: custom input → options → previous question
Ctrl+C Cancel the whole batch immediately (answers are discarded)
Space Toggle an option in multiselect questions

Herdr integration

The extension detects Herdr via the environment variables that Herdr injects into its panes:

  • HERDR_ENV=1
  • HERDR_SOCKET_PATH
  • HERDR_PANE_ID

If these are present, the extension:

  • emits herdr:blocked with active: true before prompting (the official Herdr Pi integration turns this into a blocked pane state, which Herdr surfaces as a notification)
  • reports the first pending question and remaining count as separate pane metadata tokens while waiting, allowing Herdr to truncate the question as the sidebar is resized while preserving +N
  • emits herdr:blocked with active: false and clears the token after the user answers

Sidebar display

For one question the row shows the question text itself. For multiple questions, Herdr renders the short count as a separate token, for example ❓ Which database should we use? · +2. Keeping $ask_count separate ensures it remains visible while $ask is truncated responsively as the sidebar is resized.

To render the tokens in Herdr's sidebar, add $ask and $ask_count to the same agent row in ~/.config/herdr/config.toml, then run herdr server reload-config:

[ui.sidebar.agents]
rows = [["state_icon", "workspace", "tab"], ["agent", "$ask", "$ask_count"]]

Without this config the token is simply not displayed; everything else works the same.

No extra configuration is required.

Development

Requirements and setup

Development uses the same Nix/devenv and pnpm-based toolchain as the Pi extension template:

  • Nix with flakes enabled
  • devenv
  • Pi for interactive testing
  • pnpm (the expected version is pinned in packageManager)
devenv shell
pnpm install

Launch Pi against the source entry point for an interactive check:

pnpm run dev

Run formatting, linting, strict TypeScript checks, and Vitest through Vite+:

pnpm run check
pnpm run lint
pnpm run fmt
pnpm run test
pnpm run test:watch
pnpm run test:coverage

Coverage reports are written to coverage/. The Pi-provided packages are peer dependencies, with matching concrete development dependencies so their APIs can be checked and tested locally.

Security

Pi extensions execute with the user's full system permissions. Review extension source before installing it. This extension connects to HERDR_SOCKET_PATH only when the Herdr pane environment is present; it does not send question contents anywhere else.

Report security issues privately to the repository owner rather than opening a public issue containing sensitive details.

Publishing

Validate the exact public package contents before publishing:

pnpm run check
pnpm run test:coverage
pnpm run pack:check

Only src/, README.md, LICENSE, and package metadata are published. Publish the scoped package publicly with:

pnpm publish

Consumers can then install it with pi install npm:@hayshin/pi-ask.

License

MIT