pi-ask-user 0.5.1 → 0.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ask-user",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Interactive ask_user tool for pi-coding-agent with searchable split-pane selection UI, multi-select, and freeform input",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -3,11 +3,18 @@ export interface QuestionOption {
3
3
  description?: string;
4
4
  }
5
5
 
6
+ export interface AnnotatedRow {
7
+ line: string;
8
+ selected: boolean;
9
+ }
10
+
6
11
  export interface RenderSingleSelectRowsParams {
7
12
  options: QuestionOption[];
8
13
  selectedIndex: number;
9
14
  width: number;
10
15
  allowFreeform: boolean;
16
+ allowComment?: boolean;
17
+ commentEnabled?: boolean;
11
18
  maxRows?: number;
12
19
  hideDescriptions?: boolean;
13
20
  }
@@ -65,25 +72,36 @@ interface ItemBlock {
65
72
  lines: string[];
66
73
  }
67
74
 
75
+ type ListItem =
76
+ | { type: "option"; option: QuestionOption }
77
+ | { type: "comment-toggle"; option: QuestionOption }
78
+ | { type: "freeform"; option: QuestionOption };
79
+
68
80
  function buildItemBlocks(
69
81
  options: QuestionOption[],
70
82
  width: number,
71
83
  allowFreeform: boolean,
84
+ allowComment: boolean,
85
+ commentEnabled: boolean,
72
86
  selectedIndex: number,
73
87
  hideDescriptions = false,
74
88
  ): ItemBlock[] {
75
89
  const normalizedWidth = Math.max(12, width);
76
90
  const freeformLabel = "Type something. — Enter a custom response";
77
- const allItems = options.map((option) => ({ type: "option" as const, option }));
91
+ const commentToggleLabel = `${commentEnabled ? "[✓]" : "[ ]"} Add extra context after selection`;
92
+ const allItems: ListItem[] = options.map((option) => ({ type: "option", option }));
93
+ if (allowComment) {
94
+ allItems.push({ type: "comment-toggle", option: { title: commentToggleLabel } });
95
+ }
78
96
  if (allowFreeform) {
79
- allItems.push({ type: "freeform" as const, option: { title: freeformLabel } });
97
+ allItems.push({ type: "freeform", option: { title: freeformLabel } });
80
98
  }
81
99
 
82
100
  return allItems.map((item, itemIndex) => {
83
101
  const pointer = itemIndex === selectedIndex ? "→" : " ";
84
102
  const lines: string[] = [];
85
103
 
86
- if (item.type === "freeform") {
104
+ if (item.type === "comment-toggle" || item.type === "freeform") {
87
105
  const prefix = `${pointer} `;
88
106
  const wrapped = wrapText(item.option.title, Math.max(8, normalizedWidth - prefix.length));
89
107
  wrapped.forEach((line, lineIndex) => {
@@ -114,8 +132,13 @@ function buildItemBlocks(
114
132
  });
115
133
  }
116
134
 
117
- function flatten(blocks: ItemBlock[]): string[] {
118
- return blocks.flatMap((block) => block.lines);
135
+ function flatten(blocks: ItemBlock[], selectedIndex: number): AnnotatedRow[] {
136
+ return blocks.flatMap((block) =>
137
+ block.lines.map((line) => ({
138
+ line,
139
+ selected: block.itemIndex === selectedIndex,
140
+ })),
141
+ );
119
142
  }
120
143
 
121
144
  export function renderSingleSelectRows({
@@ -123,12 +146,14 @@ export function renderSingleSelectRows({
123
146
  selectedIndex,
124
147
  width,
125
148
  allowFreeform,
149
+ allowComment = false,
150
+ commentEnabled = false,
126
151
  maxRows,
127
152
  hideDescriptions,
128
- }: RenderSingleSelectRowsParams): string[] {
129
- const itemCount = options.length + (allowFreeform ? 1 : 0);
130
- const blocks = buildItemBlocks(options, width, allowFreeform, selectedIndex, hideDescriptions);
131
- const allRows = flatten(blocks);
153
+ }: RenderSingleSelectRowsParams): AnnotatedRow[] {
154
+ const itemCount = options.length + (allowComment ? 1 : 0) + (allowFreeform ? 1 : 0);
155
+ const blocks = buildItemBlocks(options, width, allowFreeform, allowComment, commentEnabled, selectedIndex, hideDescriptions);
156
+ const allRows = flatten(blocks, selectedIndex);
132
157
 
133
158
  if (!Number.isFinite(maxRows) || !maxRows || maxRows <= 0 || allRows.length <= maxRows) {
134
159
  return allRows;
@@ -142,8 +167,11 @@ export function renderSingleSelectRows({
142
167
  const availableRows = safeMaxRows > 1 ? safeMaxRows - 1 : 1;
143
168
 
144
169
  if (selectedBlock.lines.length >= availableRows) {
145
- const visible = selectedBlock.lines.slice(0, availableRows);
146
- if (safeMaxRows > 1) visible.push(indicator);
170
+ const visible = selectedBlock.lines.slice(0, availableRows).map((line) => ({
171
+ line,
172
+ selected: true,
173
+ }));
174
+ if (safeMaxRows > 1) visible.push({ line: indicator, selected: false });
147
175
  return visible.slice(0, safeMaxRows);
148
176
  }
149
177
 
@@ -169,7 +197,7 @@ export function renderSingleSelectRows({
169
197
  break;
170
198
  }
171
199
 
172
- const visible = flatten(blocks.slice(start, end));
173
- visible.push(indicator);
200
+ const visible = flatten(blocks.slice(start, end), selectedIndex);
201
+ visible.push({ line: indicator, selected: false });
174
202
  return visible.slice(0, safeMaxRows);
175
203
  }