@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/CHANGELOG.md +67 -0
- package/CONTRIBUTING.md +13 -4
- package/README.md +22 -14
- package/constants/abbreviations.ts +6 -0
- package/constants/stopwords.ts +42 -0
- package/dist/index.html +413 -413
- package/index.ts +171 -52
- package/package.json +4 -2
- package/shared/ask-user.ts +6 -0
- package/tool/ask-user.ts +39 -291
- package/tool/response-formatter.ts +13 -9
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
|
-
|
|
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
|
|
403
|
-
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:
|
|
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:
|
|
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
|
-
|
|
82
|
-
if (response.comment) {
|
|
83
|
-
|
|
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(
|