@alexleekt/pi-ask-user-glimpse 0.3.1 → 0.4.1

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/tool/ask-user.ts CHANGED
@@ -5,299 +5,13 @@ import { fileURLToPath } from "node:url";
5
5
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
6
6
  import { prompt } from "glimpseui";
7
7
  import { terminalPrompt } from "../fallback/terminal-prompt.js";
8
- import type { AskUserPayload, Question } from "../shared/ask-user.js";
8
+ import type { AnimationLevel, AskUserPayload, Question, ThemeMode } from "../shared/ask-user.js";
9
9
  import { formatResponse } from "./response-formatter.js";
10
10
 
11
11
  const _require = createRequire(import.meta.url);
12
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
13
 
14
- /** ~100 common English stopwords for title extraction. */
15
- const STOPWORDS = new Set([
16
- "a",
17
- "an",
18
- "the",
19
- "is",
20
- "are",
21
- "was",
22
- "were",
23
- "be",
24
- "been",
25
- "being",
26
- "have",
27
- "has",
28
- "had",
29
- "do",
30
- "does",
31
- "did",
32
- "will",
33
- "would",
34
- "could",
35
- "should",
36
- "may",
37
- "might",
38
- "must",
39
- "shall",
40
- "can",
41
- "need",
42
- "ought",
43
- "used",
44
- "to",
45
- "of",
46
- "in",
47
- "for",
48
- "on",
49
- "with",
50
- "at",
51
- "by",
52
- "from",
53
- "as",
54
- "into",
55
- "through",
56
- "during",
57
- "before",
58
- "after",
59
- "above",
60
- "below",
61
- "between",
62
- "under",
63
- "again",
64
- "further",
65
- "then",
66
- "once",
67
- "here",
68
- "there",
69
- "when",
70
- "where",
71
- "why",
72
- "how",
73
- "all",
74
- "each",
75
- "few",
76
- "more",
77
- "most",
78
- "other",
79
- "some",
80
- "such",
81
- "no",
82
- "nor",
83
- "not",
84
- "only",
85
- "own",
86
- "same",
87
- "so",
88
- "than",
89
- "too",
90
- "very",
91
- "just",
92
- "and",
93
- "but",
94
- "if",
95
- "or",
96
- "because",
97
- "until",
98
- "while",
99
- "which",
100
- "what",
101
- "who",
102
- "whom",
103
- "this",
104
- "that",
105
- "these",
106
- "those",
107
- "am",
108
- "it",
109
- "its",
110
- "we",
111
- "our",
112
- "you",
113
- "your",
114
- "they",
115
- "their",
116
- "them",
117
- "he",
118
- "him",
119
- "his",
120
- "she",
121
- "her",
122
- "i",
123
- "me",
124
- "my",
125
- "mine",
126
- "us",
127
- "any",
128
- "both",
129
- "either",
130
- "neither",
131
- "one",
132
- "two",
133
- "first",
134
- "last",
135
- "another",
136
- "every",
137
- "many",
138
- "much",
139
- "several",
140
- "let",
141
- "new",
142
- "use",
143
- "using",
144
- "make",
145
- "made",
146
- "get",
147
- "got",
148
- "go",
149
- "going",
150
- "want",
151
- "wanted",
152
- "like",
153
- "liked",
154
- "know",
155
- "knew",
156
- "known",
157
- "think",
158
- "thought",
159
- "see",
160
- "saw",
161
- "seen",
162
- "come",
163
- "came",
164
- "give",
165
- "gave",
166
- "given",
167
- "take",
168
- "took",
169
- "taken",
170
- "find",
171
- "found",
172
- "say",
173
- "said",
174
- "tell",
175
- "told",
176
- "ask",
177
- "asked",
178
- "work",
179
- "worked",
180
- "seem",
181
- "seemed",
182
- "feel",
183
- "felt",
184
- "try",
185
- "tried",
186
- "leave",
187
- "left",
188
- "call",
189
- "called",
190
- "good",
191
- "well",
192
- "better",
193
- "best",
194
- "bad",
195
- "worse",
196
- "worst",
197
- "old",
198
- "long",
199
- "great",
200
- "little",
201
- "right",
202
- "left",
203
- "big",
204
- "high",
205
- "different",
206
- "important",
207
- "same",
208
- "able",
209
- "next",
210
- "early",
211
- "young",
212
- "public",
213
- "free",
214
- "real",
215
- "easy",
216
- "clear",
217
- "recent",
218
- "local",
219
- "social",
220
- "full",
221
- "small",
222
- "large",
223
- "possible",
224
- "particular",
225
- "available",
226
- "special",
227
- "certain",
228
- "personal",
229
- "open",
230
- "general",
231
- "enough",
232
- "probably",
233
- "actually",
234
- "especially",
235
- "finally",
236
- "usually",
237
- "perhaps",
238
- "almost",
239
- "simply",
240
- "quickly",
241
- "recently",
242
- "already",
243
- "eventually",
244
- "suddenly",
245
- "certainly",
246
- "definitely",
247
- "absolutely",
248
- "completely",
249
- "totally",
250
- "entirely",
251
- "exactly",
252
- "specifically",
253
- "particularly",
254
- "especially",
255
- "mainly",
256
- "mostly",
257
- "partly",
258
- "fully",
259
- "nearly",
260
- "quite",
261
- "rather",
262
- "pretty",
263
- "fairly",
264
- "really",
265
- "even",
266
- "still",
267
- "yet",
268
- "ever",
269
- "never",
270
- "always",
271
- "sometimes",
272
- "often",
273
- "usually",
274
- "frequently",
275
- "rarely",
276
- "generally",
277
- "typically",
278
- "normally",
279
- "largely",
280
- "potentially",
281
- "theoretically",
282
- "practically",
283
- "basically",
284
- "essentially",
285
- "fundamentally",
286
- "primarily",
287
- "chiefly",
288
- "principally",
289
- "partially",
290
- "half",
291
- "quarter",
292
- "double",
293
- "single",
294
- "multiple",
295
- "various",
296
- "hundred",
297
- "thousand",
298
- "million",
299
- "billion",
300
- ]);
14
+ import { STOPWORDS } from "../constants/stopwords.js";
301
15
 
302
16
  /** Extract a short title from a question by removing stopwords.
303
17
  * Falls back to first 5 words if nothing meaningful remains.
@@ -356,12 +70,20 @@ export interface AskUserParams {
356
70
  allowSkip?: boolean;
357
71
  displayMode?: string;
358
72
  followCursor?: boolean;
73
+ theme?: ThemeMode;
74
+ animationLevel?: AnimationLevel;
75
+ }
76
+
77
+ export interface AskUserMetadata {
78
+ theme?: string;
79
+ animationLevel?: string;
359
80
  }
360
81
 
361
82
  export async function askUserHandler(
362
83
  params: AskUserParams,
363
84
  signal: AbortSignal | undefined,
364
85
  ctx: ExtensionContext,
86
+ onMetadata?: (metadata: AskUserMetadata) => void,
365
87
  ) {
366
88
  if (signal?.aborted) {
367
89
  return {
@@ -397,16 +119,31 @@ export async function askUserHandler(
397
119
  payloadType = "single-select";
398
120
  }
399
121
 
122
+ // If the question is long and no separate context was provided, auto-split
123
+ // the first sentence into the question and the rest into context.
124
+ let question = params.question;
125
+ let context = params.context;
126
+ if (!context && params.question.length > 120) {
127
+ const match = params.question.match(/^(.+?[.?!])(\s+|$)/);
128
+ if (match && match[0].length < params.question.length) {
129
+ question = match[1].trim();
130
+ context = params.question.slice(match[0].length).trim();
131
+ }
132
+ }
133
+
400
134
  const payload: AskUserPayload = {
401
135
  type: payloadType,
402
- question: params.question,
403
- context: params.context,
136
+ question,
137
+ context,
404
138
  options: normalizedOptions,
405
139
  questions: params.questions,
406
140
  allowMultiple,
407
141
  allowFreeform,
408
142
  allowComment,
409
143
  allowSkip: params.allowSkip,
144
+ sessionName: ctx.sessionManager.getSessionName(),
145
+ theme: params.theme,
146
+ animationLevel: params.animationLevel,
410
147
  };
411
148
 
412
149
  let result: Record<string, unknown> | null = null;
@@ -423,10 +160,16 @@ export async function askUserHandler(
423
160
  .replace(/&/g, "\\u0026"),
424
161
  );
425
162
 
163
+ const sessionName = ctx.sessionManager.getSessionName();
164
+ const questionTitle = summarizeTitle(params.question);
165
+ const title = sessionName
166
+ ? `Pi · ${sessionName} · ${questionTitle}`
167
+ : `Pi · ${questionTitle}`;
168
+
426
169
  const options: Record<string, unknown> = {
427
170
  width: 1200,
428
171
  height: 900,
429
- title: summarizeTitle(params.question),
172
+ title: title.length > 60 ? `${title.slice(0, 57)}…` : title,
430
173
  };
431
174
 
432
175
  if (params.followCursor) {
@@ -440,6 +183,11 @@ export async function askUserHandler(
440
183
  if (result === null || result?.__cancelled === true) {
441
184
  cancelled = true;
442
185
  result = null;
186
+ } else if (result && onMetadata) {
187
+ onMetadata({
188
+ theme: result.__theme as string | undefined,
189
+ animationLevel: result.__animationLevel as string | undefined,
190
+ });
443
191
  }
444
192
  } catch (err) {
445
193
  // Glimpse unavailable — fall back to terminal prompt
@@ -11,6 +11,7 @@ export interface AskResponse {
11
11
  kind: "selection" | "freeform";
12
12
  comment?: string;
13
13
  }[];
14
+ additionalComments?: string;
14
15
  }
15
16
 
16
17
  export interface AskToolDetails {
@@ -22,6 +23,10 @@ export interface AskToolDetails {
22
23
  error?: string;
23
24
  }
24
25
 
26
+ function pickString(raw: unknown): string | undefined {
27
+ return raw ? String(raw) : undefined;
28
+ }
29
+
25
30
  function normalizeKind(raw: unknown): AskResponse["kind"] {
26
31
  if (raw === "freeform" || raw === "questionnaire") return raw;
27
32
  return "selection";
@@ -51,9 +56,7 @@ function buildResponse(
51
56
  entry.kind === "freeform"
52
57
  ? "freeform"
53
58
  : "selection",
54
- comment: entry.comment
55
- ? String(entry.comment)
56
- : undefined,
59
+ comment: pickString(entry.comment),
57
60
  };
58
61
  })
59
62
  : [],
@@ -69,7 +72,8 @@ function buildResponse(
69
72
  return {
70
73
  kind,
71
74
  selections,
72
- comment: result.comment ? String(result.comment) : undefined,
75
+ comment: pickString(result.comment),
76
+ additionalComments: pickString(result.additionalComments),
73
77
  };
74
78
  }
75
79
 
@@ -77,12 +81,12 @@ function responseToText(response: AskResponse): string {
77
81
  if (response.kind === "freeform") {
78
82
  return response.text ?? "";
79
83
  }
84
+ const lines: string[] = [];
80
85
  const selections = response.selections ?? [];
81
- let text = selections.join(", ");
82
- if (response.comment) {
83
- text += `\n\nComment: ${response.comment}`;
84
- }
85
- return text;
86
+ if (selections.length > 0) lines.push(selections.join(", "));
87
+ if (response.comment) lines.push(`Comment: ${response.comment}`);
88
+ if (response.additionalComments) lines.push(`Additional Comments: ${response.additionalComments}`);
89
+ return lines.join("\n\n");
86
90
  }
87
91
 
88
92
  export function formatResponse(