@xynogen/pix-ask 0.2.3 → 0.2.6
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 +3 -3
- package/src/index.ts +1 -1
- package/src/once.ts +2 -1
- package/src/questionnaire.ts +9 -5
- package/src/rpc.ts +8 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-ask",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Pi tool
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "Pi tool — structured questionnaire UI (ask_user)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"typebox": "^1.1.38",
|
|
38
|
-
"@xynogen/pix-pretty": "
|
|
38
|
+
"@xynogen/pix-pretty": "^1.7.9"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@earendil-works/pi-coding-agent": "*",
|
package/src/index.ts
CHANGED
|
@@ -106,7 +106,7 @@ export default function registerAsk(pi: ExtensionAPI): void {
|
|
|
106
106
|
renderCall(args, theme) {
|
|
107
107
|
const questions = Array.isArray(args.questions) ? args.questions : [];
|
|
108
108
|
const count = questions.length;
|
|
109
|
-
const firstQ =
|
|
109
|
+
const firstQ = questions[0]?.question ?? "";
|
|
110
110
|
let text = theme.fg("toolTitle", theme.bold(`ask (${count}) `));
|
|
111
111
|
text += theme.fg("muted", firstQ);
|
|
112
112
|
if (count > 1) text += theme.fg("dim", ` +${count - 1} more`);
|
package/src/once.ts
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export function once(pi: object, key: string, fn: () => void): void {
|
|
16
16
|
const g = globalThis as { __pixOnce?: WeakMap<object, Set<string>> };
|
|
17
|
-
|
|
17
|
+
if (!g.__pixOnce) g.__pixOnce = new WeakMap<object, Set<string>>();
|
|
18
|
+
const registry = g.__pixOnce;
|
|
18
19
|
let loaded = registry.get(pi);
|
|
19
20
|
if (!loaded) {
|
|
20
21
|
loaded = new Set<string>();
|
package/src/questionnaire.ts
CHANGED
|
@@ -65,7 +65,9 @@ export class AskQuestionnaire extends Container {
|
|
|
65
65
|
// ── Accessors ──────────────────────────────────────────────────────
|
|
66
66
|
|
|
67
67
|
private get currentQ(): QuestionData {
|
|
68
|
-
|
|
68
|
+
const q = this.params.questions[this.currentIndex];
|
|
69
|
+
if (!q) throw new Error("currentIndex out of bounds");
|
|
70
|
+
return q;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
private get filteredOptions(): OptionData[] {
|
|
@@ -170,7 +172,7 @@ export class AskQuestionnaire extends Container {
|
|
|
170
172
|
} else if (item.kind === "next") {
|
|
171
173
|
const selected = Array.from(this.multiChecked)
|
|
172
174
|
.sort((a, b) => a - b)
|
|
173
|
-
.map((i) => this.currentQ.options[i]?.label);
|
|
175
|
+
.map((i) => this.currentQ.options[i]?.label ?? "");
|
|
174
176
|
if (selected.length === 0) {
|
|
175
177
|
this.cancel();
|
|
176
178
|
return;
|
|
@@ -209,7 +211,7 @@ export class AskQuestionnaire extends Container {
|
|
|
209
211
|
const q = this.currentQ;
|
|
210
212
|
if (prev.kind === "multi") {
|
|
211
213
|
for (let i = 0; i < q.options.length; i++) {
|
|
212
|
-
if (prev.selected?.includes(q.options[i]
|
|
214
|
+
if (prev.selected?.includes(q.options[i]?.label ?? "")) {
|
|
213
215
|
this.multiChecked.add(i);
|
|
214
216
|
}
|
|
215
217
|
}
|
|
@@ -354,7 +356,8 @@ export class AskQuestionnaire extends Container {
|
|
|
354
356
|
this.selectedOptionIndex = Math.min(idx, this.totalItems - 1);
|
|
355
357
|
this.refresh();
|
|
356
358
|
} else {
|
|
357
|
-
const opt = this.filteredOptions[idx]
|
|
359
|
+
const opt = this.filteredOptions[idx];
|
|
360
|
+
if (!opt) return;
|
|
358
361
|
this.recordAnswer("option", opt.label, undefined, opt.preview);
|
|
359
362
|
this.nextQuestion();
|
|
360
363
|
}
|
|
@@ -434,7 +437,8 @@ export class AskQuestionnaire extends Container {
|
|
|
434
437
|
const pad = " ".repeat(LABEL_COL);
|
|
435
438
|
|
|
436
439
|
for (let i = start; i < end; i++) {
|
|
437
|
-
const item = items[i]
|
|
440
|
+
const item = items[i];
|
|
441
|
+
if (!item) continue;
|
|
438
442
|
const sel = i === this.selectedOptionIndex;
|
|
439
443
|
const ptr = sel ? t.fg("accent", "→") : " ";
|
|
440
444
|
|
package/src/rpc.ts
CHANGED
|
@@ -6,14 +6,18 @@ import type { QuestionAnswer, QuestionnaireResult } from "./types.js";
|
|
|
6
6
|
// Used when ctx.hasUI is false (headless / JSON / print mode).
|
|
7
7
|
|
|
8
8
|
export async function rpcFallback(
|
|
9
|
-
ui: {
|
|
9
|
+
ui: {
|
|
10
|
+
select(title: string, options: string[]): Promise<string | undefined>;
|
|
11
|
+
input(title: string, placeholder?: string): Promise<string | undefined>;
|
|
12
|
+
},
|
|
10
13
|
params: Params,
|
|
11
14
|
): Promise<QuestionnaireResult> {
|
|
12
15
|
const answers: QuestionAnswer[] = [];
|
|
13
16
|
let cancelled = false;
|
|
14
17
|
|
|
15
18
|
for (let i = 0; i < params.questions.length; i++) {
|
|
16
|
-
const q = params.questions[i]
|
|
19
|
+
const q = params.questions[i];
|
|
20
|
+
if (!q) break;
|
|
17
21
|
const header = q.header;
|
|
18
22
|
|
|
19
23
|
if (q.multiSelect) {
|
|
@@ -32,7 +36,7 @@ export async function rpcFallback(
|
|
|
32
36
|
.split(",")
|
|
33
37
|
.map((s) => Number(s.trim()))
|
|
34
38
|
.filter((n) => n >= 1 && n <= q.options.length);
|
|
35
|
-
const selected = indices.map((n) => q.options[n - 1]?.label);
|
|
39
|
+
const selected = indices.map((n) => q.options[n - 1]?.label ?? "");
|
|
36
40
|
if (selected.length > 0) {
|
|
37
41
|
answers.push({
|
|
38
42
|
questionIndex: i,
|
|
@@ -69,7 +73,7 @@ export async function rpcFallback(
|
|
|
69
73
|
const opt = q.options.find(
|
|
70
74
|
(o) =>
|
|
71
75
|
chosen === o.label || `${o.label} — ${o.description}` === chosen,
|
|
72
|
-
)
|
|
76
|
+
);
|
|
73
77
|
answers.push({
|
|
74
78
|
questionIndex: i,
|
|
75
79
|
question: q.question,
|