@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xynogen/pix-ask",
3
- "version": "0.2.3",
4
- "description": "Pi tool \u2014 structured questionnaire UI (ask_user)",
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 = (questions[0]?.question ?? "") as string;
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
- const registry = (g.__pixOnce ??= new WeakMap<object, Set<string>>());
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>();
@@ -65,7 +65,9 @@ export class AskQuestionnaire extends Container {
65
65
  // ── Accessors ──────────────────────────────────────────────────────
66
66
 
67
67
  private get currentQ(): QuestionData {
68
- return this.params.questions[this.currentIndex]!;
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]!.label)) {
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: { select: Function; input: Function },
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,