@yandy0725/pi-ask-user 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +19 -19
  2. package/README.zh.md +15 -16
  3. package/index.ts +12 -47
  4. package/package.json +5 -5
package/README.md CHANGED
@@ -11,7 +11,7 @@ A Pi package that adds an interactive `ask_user` tool for collecting user decisi
11
11
  - User-toggleable extra context on structured selections
12
12
  - Context display support
13
13
  - Configurable display mode: `overlay` (modal, default) or `inline` (rendered directly in the flow)
14
- - Runtime overlay toggle: press the configured overlay-toggle key (`alt+o` by default, configurable per call or via env var) while the prompt is open to temporarily hide/show the popup so you can read prior agent output, then press it again to bring it back
14
+ - Runtime overlay toggle: press the configured overlay-toggle key (`alt+o` by default, configurable via env var or `ask-user.json`) while the prompt is open to temporarily hide/show the popup so you can read prior agent output, then press it again to bring it back
15
15
  - Pi-TUI-aligned keybinding and editor behavior
16
16
  - Custom TUI rendering for tool calls and results
17
17
  - System prompt integration via `promptSnippet` and `promptGuidelines`
@@ -58,12 +58,11 @@ The registered tool name is:
58
58
  | `options` | `(string \| {title, description?})[]?` | `[]` | Multiple-choice options |
59
59
  | `allowMultiple` | `boolean?` | `false` | Enable multi-select mode |
60
60
  | `allowFreeform` | `boolean?` | `true` | Add a "Type something" freeform option |
61
- | `allowComment` | `boolean?` | `false` | Expose a user-toggleable extra-context option in the custom UI (`ctrl+g` or the toggle row) and collect an optional comment in fallback dialogs |
62
- | `displayMode` | `"overlay" \| "inline"?` | env var or `"overlay"` | Controls custom UI rendering: `overlay` shows the centered modal (current behavior), `inline` renders without overlay framing |
63
- | `overlayToggleKey` | `string?` | env var or `"alt+o"` | Shortcut for hiding/showing the overlay popup (overlay mode only). Pi-TUI key spec, e.g. `"alt+o"`, `"ctrl+shift+h"`. Pass `"off"` to disable. |
64
- | `commentToggleKey` | `string?` | env var or `"ctrl+g"` | Shortcut for toggling the optional comment/extra-context row when `allowComment: true`. Pass `"off"` to disable. |
61
+ | `allowComment` | `boolean?` | `true` | Expose a user-toggleable extra-context option in the custom UI (`ctrl+g` or the toggle row) and collect an optional comment in fallback dialogs |
65
62
  | `timeout` | `number?` | — | Auto-dismiss after N ms and return `null` if the prompt times out |
66
63
 
64
+ Appearance options (`displayMode`, `overlayToggleKey`, `commentToggleKey`) are **not** model-visible parameters. They are user preferences resolved from env vars and `ask-user.json` — see [Display mode](#display-mode) and [Shortcuts](#shortcuts). They remain accepted by the internal `AskParams` type for SDK/direct callers, but an LLM tool call cannot set them.
65
+
67
66
  ## Example usage shape
68
67
 
69
68
  ```json
@@ -76,8 +75,7 @@ The registered tool name is:
76
75
  ],
77
76
  "allowMultiple": false,
78
77
  "allowFreeform": true,
79
- "allowComment": true,
80
- "displayMode": "inline"
78
+ "allowComment": true
81
79
  }
82
80
  ```
83
81
 
@@ -95,11 +93,12 @@ export PI_ASK_USER_COMMENT_TOGGLE_KEY=alt+c
95
93
 
96
94
  Effective order:
97
95
 
98
- 1. Per-call `displayMode` parameter (if provided)
99
- 2. `PI_ASK_USER_DISPLAY_MODE` (if set to `"overlay"` or `"inline"`)
100
- 3. Project config file `.pi/ask-user.json` `displayMode` field (if exists)
101
- 4. User config file `~/.pi/agent/ask-user.json` `displayMode` field (if exists)
102
- 5. Fallback default: `"overlay"`
96
+ 1. `PI_ASK_USER_DISPLAY_MODE` (if set to `"overlay"` or `"inline"`)
97
+ 2. Project config file `.pi/ask-user.json` `displayMode` field (if exists)
98
+ 3. User config file `~/.pi/agent/ask-user.json` `displayMode` field (if exists)
99
+ 4. Fallback default: `"overlay"`
100
+
101
+ (A per-call `displayMode` still wins over all of the above for SDK/direct callers, but the field is no longer part of the model-visible tool schema.)
103
102
 
104
103
  Unrecognised values are silently ignored and fall back to `"overlay"`.
105
104
 
@@ -111,7 +110,7 @@ In addition to environment variables, you can use JSON config files:
111
110
  - User-level (global): `~/.pi/agent/ask-user.json`
112
111
  - Project-level: `.pi/ask-user.json`
113
112
 
114
- Fields in the project-level file override same-named fields in the user-level file. Priority: call param > env var > project config > user config > default.
113
+ Fields in the project-level file override same-named fields in the user-level file. Priority: env var > project config > user config > default.
115
114
 
116
115
  **Example `~/.pi/agent/ask-user.json`:**
117
116
 
@@ -129,11 +128,12 @@ All fields are optional. Missing files or malformed JSON are silently ignored, e
129
128
 
130
129
  Effective order for both `overlayToggleKey` and `commentToggleKey`:
131
130
 
132
- 1. Per-call parameter (if provided)
133
- 2. Matching env var (`PI_ASK_USER_OVERLAY_TOGGLE_KEY` / `PI_ASK_USER_COMMENT_TOGGLE_KEY`)
134
- 3. Project config file `.pi/ask-user.json` (if exists)
135
- 4. User config file `~/.pi/agent/ask-user.json` (if exists)
136
- 5. Built-in defaults: `alt+o` and `ctrl+g`
131
+ 1. Matching env var (`PI_ASK_USER_OVERLAY_TOGGLE_KEY` / `PI_ASK_USER_COMMENT_TOGGLE_KEY`)
132
+ 2. Project config file `.pi/ask-user.json` (if exists)
133
+ 3. User config file `~/.pi/agent/ask-user.json` (if exists)
134
+ 4. Built-in defaults: `alt+o` and `ctrl+g`
135
+
136
+ (A per-call parameter still wins for SDK/direct callers, but these fields are no longer part of the model-visible tool schema.)
137
137
 
138
138
  Pass `"off"`, `"none"`, or `"disabled"` (at any level) to disable the shortcut entirely. Invalid specs are silently dropped and the next source is used. Specs follow the Pi-TUI [`KeyId`](https://github.com/earendil-works/pi-mono/blob/main/packages/tui/src/keys.ts) format: `[mod+]...key` where modifiers are `ctrl`, `shift`, `alt`, `super`, in any order, joined by `+` (e.g. `ctrl+g`, `alt+shift+x`, `escape`, `tab`).
139
139
 
@@ -149,7 +149,7 @@ While an `ask_user` prompt is open:
149
149
  | `esc` | Clear the search filter, exit freeform/comment mode, or cancel the prompt. |
150
150
  | `↑` / `↓`, `ctrl+k` / `ctrl+j` | Navigate options. `ctrl+k` / `ctrl+j` (vim-style) work while typing in searchable prompts without disturbing the filter. |
151
151
 
152
- If you prefer never to see the overlay, set `displayMode: "inline"` per call or `PI_ASK_USER_DISPLAY_MODE=inline` globally.
152
+ If you prefer never to see the overlay, set `PI_ASK_USER_DISPLAY_MODE=inline` globally or `"displayMode": "inline"` in `ask-user.json`.
153
153
 
154
154
  ## Result details
155
155
 
package/README.zh.md CHANGED
@@ -11,7 +11,7 @@
11
11
  - 选择后可切换添加额外上下文(可选注释)
12
12
  - 上下文展示支持
13
13
  - 可配置的显示模式:`overlay`(模态,默认)或 `inline`(直接内联渲染)
14
- - 运行时浮层切换:当提示窗口打开时,按下配置的叠加层切换键(默认 `alt+o`,可每次调用配置或通过环境变量设置)临时隐藏/显示弹窗,以便阅读之前的 Agent 输出
14
+ - 运行时浮层切换:当提示窗口打开时,按下配置的叠加层切换键(默认 `alt+o`,可通过环境变量或 `ask-user.json` 配置)临时隐藏/显示弹窗,以便阅读之前的 Agent 输出
15
15
  - Pi-TUI 对齐的按键绑定和编辑器行为
16
16
  - 工具调用和结果的自定义 TUI 渲染
17
17
  - 通过 `promptSnippet` 和 `promptGuidelines` 集成系统提示词
@@ -58,12 +58,11 @@ pi install npm:@yandy0725/pi-ask-user
58
58
  | `options` | `(string \| {title, description?})[]?` | `[]` | 多选选项 |
59
59
  | `allowMultiple` | `boolean?` | `false` | 启用多选模式 |
60
60
  | `allowFreeform` | `boolean?` | `true` | 添加"自定义输入"自由文本选项 |
61
- | `allowComment` | `boolean?` | `false` | 在自定义 UI 中提供可切换的额外上下文选项(`ctrl+g` 或切换行),并在回退对话框中收集可选注释 |
62
- | `displayMode` | `"overlay" \| "inline"?` | 环境变量或 `"overlay"` | 控制自定义 UI 渲染:`overlay` 显示居中模态框(当前行为),`inline` 不显示叠加框架渲染 |
63
- | `overlayToggleKey` | `string?` | 环境变量或 `"alt+o"` | 隐藏/显示叠加弹窗的快捷键(仅 overlay 模式)。Pi-TUI 键位格式,例如 `"alt+o"`、`"ctrl+shift+h"`。传入 `"off"` 禁用。 |
64
- | `commentToggleKey` | `string?` | 环境变量或 `"ctrl+g"` | 当 `allowComment: true` 时切换可选注释/额外上下文行的快捷键。传入 `"off"` 禁用。 |
61
+ | `allowComment` | `boolean?` | `true` | 在自定义 UI 中提供可切换的额外上下文选项(`ctrl+g` 或切换行),并在回退对话框中收集可选注释 |
65
62
  | `timeout` | `number?` | — | N 毫秒后自动关闭,超时返回 `null` |
66
63
 
64
+ 外观类选项(`displayMode`、`overlayToggleKey`、`commentToggleKey`)**不是**模型可见参数:它们是用户偏好,由环境变量与 `ask-user.json` 解析(见[显示模式](#显示模式)与[快捷键](#快捷键))。内部 `AskParams` 类型仍接受它们(供 SDK/直接调用),但 LLM 的工具调用无法设置。
65
+
67
66
  ## 通过环境变量设置个人偏好
68
67
 
69
68
  在你的 shell 配置(`~/.zshrc`、`~/.bash_profile` 等)中设置以下变量来全局配置默认值:
@@ -78,11 +77,12 @@ export PI_ASK_USER_COMMENT_TOGGLE_KEY=alt+c
78
77
 
79
78
  生效优先级:
80
79
 
81
- 1. 每次调用时的 `displayMode` 参数(如提供)
82
- 2. `PI_ASK_USER_DISPLAY_MODE`(如设置为 `"overlay"` 或 `"inline"`)
83
- 3. 项目配置文件 `.pi/ask-user.json`(如存在)中的 `displayMode`
84
- 4. 用户配置文件 `~/.pi/agent/ask-user.json`(如存在)中的 `displayMode`
85
- 5. 回退默认值:`"overlay"`
80
+ 1. `PI_ASK_USER_DISPLAY_MODE`(如设置为 `"overlay"` 或 `"inline"`)
81
+ 2. 项目配置文件 `.pi/ask-user.json`(如存在)中的 `displayMode`
82
+ 3. 用户配置文件 `~/.pi/agent/ask-user.json`(如存在)中的 `displayMode`
83
+ 4. 回退默认值:`"overlay"`
84
+
85
+ (SDK/直接调用传入的 `displayMode` 仍会覆盖以上所有层级,但该字段已不在模型可见的 tool schema 里。)
86
86
 
87
87
  无法识别的值会被静默忽略并回退到 `"overlay"`。
88
88
 
@@ -94,7 +94,7 @@ export PI_ASK_USER_COMMENT_TOGGLE_KEY=alt+c
94
94
  - 用户级(全局):`~/.pi/agent/ask-user.json`
95
95
  - 项目级:`.pi/ask-user.json`
96
96
 
97
- 项目级文件中的字段会覆盖用户级文件中同名字段。配置优先级:调用参数 > 环境变量 > 项目配置 > 用户配置 > 默认值。
97
+ 项目级文件中的字段会覆盖用户级文件中同名字段。配置优先级:环境变量 > 项目配置 > 用户配置 > 默认值。
98
98
 
99
99
  **示例 `~/.pi/agent/ask-user.json`:**
100
100
 
@@ -112,10 +112,9 @@ export PI_ASK_USER_COMMENT_TOGGLE_KEY=alt+c
112
112
 
113
113
  `overlayToggleKey` 和 `commentToggleKey` 的生效优先级:
114
114
 
115
- 1. 每次调用时的参数(如提供)
116
- 2. 对应的环境变量(`PI_ASK_USER_OVERLAY_TOGGLE_KEY` / `PI_ASK_USER_COMMENT_TOGGLE_KEY`)
117
- 3. 项目配置文件 `.pi/ask-user.json`(如存在)
118
- 4. 用户配置文件 `~/.pi/agent/ask-user.json`(如存在)
115
+ 1. 对应的环境变量(`PI_ASK_USER_OVERLAY_TOGGLE_KEY` / `PI_ASK_USER_COMMENT_TOGGLE_KEY`)
116
+ 2. 项目配置文件 `.pi/ask-user.json`(如存在)
117
+ 3. 用户配置文件 `~/.pi/agent/ask-user.json`(如存在)
119
118
  5. 内置默认值:`alt+o` 和 `ctrl+g`
120
119
 
121
120
  传入 `"off"`、`"none"` 或 `"disabled"`(任意级别)可以完全禁用快捷键。无效的键位格式会被静默丢弃,使用下一个来源。键位格式遵循 Pi-TUI 的 [`KeyId`](https://github.com/earendil-works/pi-mono/blob/main/packages/tui/src/keys.ts) 格式:`[mod+]...key`,修饰键为 `ctrl`、`shift`、`alt`、`super`,可任意顺序用 `+` 连接(如 `ctrl+g`、`alt+shift+x`、`escape`、`tab`)。
@@ -132,7 +131,7 @@ export PI_ASK_USER_COMMENT_TOGGLE_KEY=alt+c
132
131
  | `esc` | 清除搜索过滤、退出自由文本/注释模式、或取消提示。 |
133
132
  | `↑` / `↓`、`ctrl+k` / `ctrl+j` | 导航选项。`ctrl+k` / `ctrl+j`(vim 风格)可在可搜索提示中打字时不干扰过滤输入。 |
134
133
 
135
- 如果你始终不希望看到叠加层,请每次调用时设置 `displayMode: "inline"`,或全局设置 `PI_ASK_USER_DISPLAY_MODE=inline`。
134
+ 如果你始终不希望看到叠加层,请全局设置 `PI_ASK_USER_DISPLAY_MODE=inline`,或在 `ask-user.json` 中设置 `"displayMode": "inline"`。
136
135
 
137
136
  ## 结果详情
138
137
 
package/index.ts CHANGED
@@ -32,30 +32,12 @@ import {
32
32
  truncateToWidth,
33
33
  wrapTextWithAnsi,
34
34
  } from "@earendil-works/pi-tui";
35
- import { type TUnsafe, Type } from "typebox";
35
+ import { Type } from "typebox";
36
36
  import { type QuestionOption, renderSingleSelectRows } from "./src/single-select-layout.js";
37
37
 
38
38
  const _require = createRequire(import.meta.url);
39
39
  const ASK_USER_VERSION: string = (_require("./package.json") as { version: string }).version;
40
40
 
41
- /**
42
- * Emit a flat `{ type: "string", enum: [...] }` JSON Schema instead of the
43
- * `anyOf`/`oneOf` shape that `Type.Union([Type.Literal()])` produces. Google's
44
- * function-calling API rejects the union form. Local copy of pi-ai's StringEnum
45
- * to avoid a peer dependency for one helper.
46
- */
47
- function StringEnum<const T extends readonly string[]>(
48
- values: T,
49
- options?: { description?: string; default?: T[number] },
50
- ): TUnsafe<T[number]> {
51
- return Type.Unsafe<T[number]>({
52
- type: "string",
53
- enum: [...values],
54
- ...(options?.description ? { description: options.description } : {}),
55
- ...(options?.default !== undefined ? { default: options.default } : {}),
56
- });
57
- }
58
-
59
41
  /**
60
42
  * `getMarkdownTheme()` returns a bag of closures that read through a Proxy
61
43
  * over the host's theme singleton. The Proxy only throws on property access,
@@ -1534,15 +1516,12 @@ export default function (pi: ExtensionAPI) {
1534
1516
  pi.registerTool({
1535
1517
  name: "ask_user",
1536
1518
  label: "Ask User",
1537
- description:
1538
- "Ask the user a question with optional multiple-choice answers. Use this to gather information interactively. Ask exactly one focused question per call. Before calling, gather context with tools (read/web/ref) and pass a short summary via the context field.",
1539
- promptSnippet:
1540
- "Ask the user one focused question with optional multiple-choice answers to gather information interactively",
1519
+ description: "Ask the user one focused question with optional multiple-choice answers.",
1520
+ promptSnippet: "Ask the user one focused question with optional multiple-choice answers",
1541
1521
  promptGuidelines: [
1542
- "Before calling ask_user, gather context with tools (read/web/ref) and pass a short summary via the context field.",
1522
+ "Before calling ask_user, gather context with your read/search/web tools and pass a short summary via the context field.",
1543
1523
  "Use ask_user when the user's intent is ambiguous, when a decision requires explicit user input, or when multiple valid options exist.",
1544
- "Ask exactly one focused question per ask_user call.",
1545
- "Do not combine multiple numbered, multipart, or unrelated questions into one ask_user prompt.",
1524
+ "Ask exactly one focused question per ask_user call — never combine multiple, numbered, or unrelated questions in one prompt.",
1546
1525
  ],
1547
1526
  // Block other tool calls in the same assistant turn until the user answers,
1548
1527
  // so the model can't batch ask_user with bash/edit/write and let those run
@@ -1570,28 +1549,14 @@ export default function (pi: ExtensionAPI) {
1570
1549
  allowMultiple: Type.Optional(Type.Boolean({ description: "Allow selecting multiple options. Default: false" })),
1571
1550
  allowFreeform: Type.Optional(Type.Boolean({ description: "Add a freeform text option. Default: true" })),
1572
1551
  allowComment: Type.Optional(
1573
- Type.Boolean({ description: "Collect an optional comment after selecting one or more options. Default: false" }),
1574
- ),
1575
- displayMode: Type.Optional(
1576
- StringEnum(["overlay", "inline"] as const, {
1577
- description:
1578
- "UI rendering mode. 'overlay' shows a centered modal, 'inline' renders in-place. Default: PI_ASK_USER_DISPLAY_MODE env var if set, otherwise 'overlay'. Omit to respect the user's configured preference.",
1579
- }),
1580
- ),
1581
- overlayToggleKey: Type.Optional(
1582
- Type.String({
1583
- description:
1584
- "Shortcut for hiding/showing the overlay popup (overlay mode only), e.g. 'alt+o' or 'ctrl+shift+h'. Pass 'off' to disable. Default: PI_ASK_USER_OVERLAY_TOGGLE_KEY env var if set, otherwise 'alt+o'.",
1585
- }),
1586
- ),
1587
- commentToggleKey: Type.Optional(
1588
- Type.String({
1589
- description:
1590
- "Shortcut for toggling the optional comment/extra-context row when allowComment is true, e.g. 'ctrl+g'. Pass 'off' to disable. Default: PI_ASK_USER_COMMENT_TOGGLE_KEY env var if set, otherwise 'ctrl+g'.",
1591
- }),
1552
+ Type.Boolean({ description: "Collect an optional comment after selecting one or more options. Default: true" }),
1592
1553
  ),
1554
+ // 外观类选项(displayMode / overlayToggleKey / commentToggleKey)不再暴露给模型:
1555
+ // 它们是**用户偏好**,优先级为 模型参数 > env > ask-user.json > 默认值,模型能直接
1556
+ // 盖掉用户的设置,且占了 782 字符的 schema。取值改由 env(PI_ASK_USER_DISPLAY_MODE 等)
1557
+ // 与 ask-user.json 提供,内部解析链与 `AskParams` 类型保留(SDK/内部调用仍可传)。
1593
1558
  timeout: Type.Optional(
1594
- Type.Number({ description: "Auto-dismiss after N milliseconds. Returns null (cancelled) when expired." }),
1559
+ Type.Number({ description: "Auto-dismiss after N ms; returns null when expired." }),
1595
1560
  ),
1596
1561
  }),
1597
1562
 
@@ -1609,7 +1574,7 @@ export default function (pi: ExtensionAPI) {
1609
1574
  options: rawOptions = [],
1610
1575
  allowMultiple = false,
1611
1576
  allowFreeform = true,
1612
- allowComment = false,
1577
+ allowComment = true,
1613
1578
  displayMode,
1614
1579
  overlayToggleKey,
1615
1580
  commentToggleKey,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.1",
6
+ "version": "0.4.0",
7
7
  "description": "Interactive ask_user tool for pi-coding-agent with searchable split-pane selection UI, multi-select, and freeform input",
8
8
  "license": "MIT",
9
9
  "repository": {
@@ -41,10 +41,10 @@
41
41
  "typebox": "*"
42
42
  },
43
43
  "devDependencies": {
44
- "@biomejs/biome": "^2.5.14",
45
- "@types/node": "^26.6.2",
46
- "typebox": "^1.3.34",
44
+ "@biomejs/biome": "^2.6.2",
45
+ "@types/node": "^26.6.5",
46
+ "typebox": "^1.4.3",
47
47
  "typescript": "~7.0.2",
48
- "vitest": "^5.0.1"
48
+ "vitest": "^5.0.5"
49
49
  }
50
50
  }