npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zhushanwen/pi-ask-user

v7.0.8

Published

Inline adaptive ask_user tool for Pi — single/multi-question structured input with split-pane preview, inline editor and optional comments.

Readme

@zhushanwen/pi-ask-user

Inline adaptive ask_user tool for the Pi coding agent. Resolves ambiguity the agent cannot resolve itself — a single question (no tab bar) or 1-4 questions (tabbed view + submit), with split-pane option preview on wide terminals, an inline free-text editor, and optional comments.

The tool's primary caller is the LLM. This README covers both how an agent should use it (top sections) and how a maintainer reads the code (File structure → Design notes).

Install

pi install npm:@zhushanwen/pi-ask-user

Dev-only symlink: during local development you may symlink this package into ~/.pi/agent/extensions/ for debugging, but never use the symlinked copy for daily work. Local directory discovery has an index.ts fallback that masks a missing pi manifest field — npm-installed copies then silently fail to load. See the repo root CLAUDE.md "扩展安装红线".

When to use

Call ask_user only when all three hold:

  1. The request has ≥2 reasonable approaches.
  2. You have already gathered context (read/grep) and the answer is still genuinely ambiguous.
  3. Picking wrong means redoing real work.

If you can form a defensible recommendation from the codebase, proceed and state your choice — do not ask. Models over-ask because asking feels safer than deciding; resist this.

When NOT to use

  • Trivia answerable by reading code/docs — plain text suffices.
  • Simple confirmations ("I'll delete X") — plain text suffices.
  • Outsourcing judgment you should make — if context makes the answer clear, decide.
  • Free-form requirements / long-form feedback — this tool returns short selections only.
  • High-frequency grilling — do not chain ask_user calls as a default fallback when stuck. If you have no context to pass, you are not ready to ask — read code first.
  • Reversible decisions — if a wrong guess is cheap to roll back, just decide.

If you recommend an option, prefix its label with (Recommended) and list it first.

Parameters

{
  questions: Array<{
    question: string;        // one self-contained decision; ≤1000 chars; no control chars (incl. \n)
    header?: string;         // tab label ≤12 chars; REQUIRED (non-empty) when questions.length > 1
    context?: string;        // 1-3 sentences of what you learned; shown above the question
    options: Array<{         // 2-4 mutually exclusive options; do NOT add an 'Other' — it is automatic
      label: string;         // ≤ ~40 chars (longer overflows the split-pane UI); also the answer value
      description?: string;  // short rationale shown under the label and in the preview pane
    }>;
    multiSelect?: boolean;   // default false; true only when several options can validly apply
    allowComment?: boolean;  // default false; lets the user append a free-text note after selecting
  }>
}  // questions: 1-4 entries

Constraints at a glance

| Field | Constraint | Enforced by | |-------|-----------|-------------| | questions | 1-4 entries | schema (minItems/maxItems) | | options | 2-4 entries | schema | | question | ≤1000 chars, no control chars (incl. \n), unique within the call | schema description + validate.ts | | header | ≤12 chars; required when questions.length > 1 | validate.ts (length + non-empty) | | options[].label | non-empty, unique within the question | validate.ts |

Validation errors are returned as isError: true with a message that names the violation and tells you how to fix it — correct the parameters and retry.

Result format

On success the tool returns the answers joined as "question" = "answer" lines. Answer composition rules:

  • Single-select: the chosen label.
  • Multi-select: selected labels joined with , (e.g. A, B).
  • Free-text (Other): whatever the user typed.
  • Comment: if allowComment was set, the user's note is appended after (e.g. Postgres — needs TLS).

A question with no answer reports as (no answer).

Behavior on failure / cancellation

| Situation | Return | What the agent should do | |-----------|--------|--------------------------| | Parameter validation fails | isError: true + fix hint | Correct params and retry | | No interactive UI (headless) | isError: true, tool disabled for the session | Proceed with a defensible decision stated in text, or wait for the user — do not retry | | Agent aborted (goal cancelled / context compacted) | cancelled: true | The text identifies it as an agent abort, not a user cancel. Do not assume an answer; do not retry ask_user — propagate the abort, or wait for new instructions if the decision is still required. | | User cancels (Esc → confirm, or Cancel button) | cancelled: true | Wait for new instructions, or re-ask with refined options if the decision is still required | | Unexpected error | isError: true + { error } | Retry once with corrected parameters, or proceed with a defensible decision |

The headless branch physically removes the tool from the session (setActiveTools) — this is deliberate, so a function-calling loop cannot keep retrying ask_user in a non-interactive context.

Features

  • Adaptive layout: single question → no tab bar; 1-4 questions → tabbed view + Submit tab.
  • Split-pane preview (≥84 cols): option list left, selected option detail right. The right pane is plain-text option detail (label + description), not a Markdown renderer.
  • Inline free-text editor: select "Other" → Enter → type a custom answer. Multi-line aware, soft-wrapped.
  • Optional comments: allowComment: true → after selecting, the user may append a short note.
  • Multi-select: multiSelect: true → toggle checkboxes with Space, Enter to confirm.
  • Esc confirm-to-cancel: Esc on the first question opens a confirm overlay (a second Esc cancels; any other key stays).
  • Headless-safe: disables the tool and returns isError when no UI is available.

File structure

extensions/ask-user/
├── index.ts                  # re-export entry (Pi loads via package.json pi.extensions)
├── package.json
├── README.md                 # this file — usage contract for LLM callers + overview
├── ARCHITECTURE.md           # internals: dependency graph, state machine, defensive flow
├── vitest.config.ts
└── src/
    ├── index.ts              # Tool factory: registerTool + execute (6-step defensive flow) + renderCall/renderResult
    ├── types.ts              # Input schema, Result schema, shared state types (QuestionState/ThemeLike) — dependency leaf
    ├── validate.ts           # pure parameter validation; error messages aimed at LLM fixability
    ├── component.ts          # AskUserComponent: state machine, input routing, race guards
    ├── question-view.ts      # pure render: option list, split-pane, inline editor
    └── submit-view.ts        # pure render: Submit tab, answer summary, buildResult

types.ts is intentionally the shared dependency leaf — it holds QuestionState/ThemeLike (not component.ts) so the two pure-render views depend only on the leaf, breaking a would-be component → view → component cycle. See ARCHITECTURE.md for the full graph.

Steer mechanism

The tool registers three steering channels to discourage over-asking:

  • description — the long tool description shown in the agent's tool catalog (the three preconditions + negative cases).
  • promptSnippet — one-line summary injected into the system prompt.
  • promptGuidelines — six focused rules reinforcing: gather context first, one decision per question, no trivia, don't outsource judgment, don't add an Other option.

All three are consistent and point the same direction. If you tune behavior, edit all three together in src/index.ts to avoid drift.

Design notes

  • Why inline, not overlay (executectx.ui.custom without options): the question belongs in the conversation flow, not a modal that obscures context.
  • Why Other is auto-appended, not in the schema: free-text input is the user's escape hatch and must not be something the LLM can omit or mislabel. Keeping it out of options guarantees it is always present and always last.
  • Why ←/→ does not switch tabs: left/right is reserved for the Submit tab's Submit/Cancel focus toggle, so it does not yank focus away while navigating an option list.
  • Why validation messages are verbose: every message names the violation and gives a fix path, because the reader is an LLM that will retry.

Spec reference

The original design spec, acceptance criteria (FR-x / AC-x), and E2E test cases live under .xyz-harness/2026-06-15-ask-user/:

  • spec.md — requirements + functional/acceptance criteria
  • e2e-test-cases.md — end-to-end scenarios
  • clarification.md / plan.md — design rationale

Cross-references between these and the implementation are in ARCHITECTURE.md.

License

MIT