@xynogen/pix-ask 0.2.23 → 0.2.26
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 +2 -2
- package/src/components.ts +1 -1
- package/src/index.ts +16 -5
- package/src/questionnaire.ts +14 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-ask",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.26",
|
|
4
4
|
"description": "Pi tool — structured questionnaire UI (ask_user)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@xynogen/pix-pretty": "^1.
|
|
38
|
+
"@xynogen/pix-pretty": "^1.19.0",
|
|
39
39
|
"@xynogen/pix-runtime": "^0.8.0",
|
|
40
40
|
"typebox": "^1.1.38"
|
|
41
41
|
},
|
package/src/components.ts
CHANGED
|
@@ -38,7 +38,7 @@ export class TabBar implements Component {
|
|
|
38
38
|
const tag = `${num}.${this.questions[i]?.header}`;
|
|
39
39
|
parts.push(active ? t.fg("accent", t.bold(tag)) : t.fg("dim", tag));
|
|
40
40
|
}
|
|
41
|
-
const line = parts.join(t.fg("
|
|
41
|
+
const line = parts.join(t.fg("muted", " "));
|
|
42
42
|
return [
|
|
43
43
|
truncateToWidth(
|
|
44
44
|
t.fg("accent", "╭─") +
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { modalOverlayOptions } from "@xynogen/pix-pretty/modal-frame";
|
|
|
4
4
|
import {
|
|
5
5
|
dotJoin,
|
|
6
6
|
formatCollapsedToolRow,
|
|
7
|
+
frameToolResult,
|
|
7
8
|
hideCollapsedToolCall,
|
|
8
9
|
pluralize,
|
|
9
10
|
} from "@xynogen/pix-pretty/utils";
|
|
@@ -43,8 +44,7 @@ export default function registerAsk(pi: ExtensionAPI): void {
|
|
|
43
44
|
description: `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous.`,
|
|
44
45
|
promptSnippet: `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous`,
|
|
45
46
|
promptGuidelines: [
|
|
46
|
-
|
|
47
|
-
"Do not stack multiple ask calls back-to-back — group all clarifying questions into one invocation.",
|
|
47
|
+
"Do not stack multiple ask_user calls back-to-back — group all clarifying questions into one invocation.",
|
|
48
48
|
],
|
|
49
49
|
executionMode: "sequential",
|
|
50
50
|
parameters: ParamsSchema,
|
|
@@ -57,6 +57,7 @@ export default function registerAsk(pi: ExtensionAPI): void {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// SAFETY: ParamsSchema validates tool input before execute receives it.
|
|
60
61
|
const typed = params as unknown as Params;
|
|
61
62
|
|
|
62
63
|
if (!Array.isArray(typed.questions) || typed.questions.length === 0) {
|
|
@@ -113,9 +114,11 @@ export default function registerAsk(pi: ExtensionAPI): void {
|
|
|
113
114
|
const questions = Array.isArray(args.questions) ? args.questions : [];
|
|
114
115
|
const count = questions.length;
|
|
115
116
|
const firstQ = questions[0]?.question ?? "";
|
|
116
|
-
const head = `${theme.fg("toolTitle", theme.bold("ask_user"))} ${theme.fg("
|
|
117
|
+
const head = `${theme.fg("toolTitle", theme.bold("ask_user"))} ${theme.fg("dim", firstQ)}`;
|
|
117
118
|
text.setText(
|
|
118
|
-
dotJoin([head, theme.fg("
|
|
119
|
+
dotJoin([head, theme.fg("muted", pluralize(count, "question"))], (s) =>
|
|
120
|
+
theme.fg("muted", s),
|
|
121
|
+
),
|
|
119
122
|
);
|
|
120
123
|
return text;
|
|
121
124
|
},
|
|
@@ -130,6 +133,14 @@ export default function registerAsk(pi: ExtensionAPI): void {
|
|
|
130
133
|
text.setText(theme.fg("muted", "Waiting for user input…"));
|
|
131
134
|
return text;
|
|
132
135
|
}
|
|
136
|
+
if (renderCtx.isError) {
|
|
137
|
+
const error = result.content
|
|
138
|
+
.filter((part) => part.type === "text")
|
|
139
|
+
.map((part) => part.text)
|
|
140
|
+
.join("\n");
|
|
141
|
+
text.setText(error || "Error");
|
|
142
|
+
return frameToolResult(text, theme, true);
|
|
143
|
+
}
|
|
133
144
|
if (!details || details.cancelled || !details.answers?.length) {
|
|
134
145
|
// Cancellation is a warning outcome, not success — shared row keeps the
|
|
135
146
|
// tool identity and a text label (never color alone) for accessibility.
|
|
@@ -164,7 +175,7 @@ export default function registerAsk(pi: ExtensionAPI): void {
|
|
|
164
175
|
);
|
|
165
176
|
const header = `${theme.fg("success", "✓")} ${theme.fg("toolTitle", theme.bold("ask_user"))}`;
|
|
166
177
|
text.setText([header, ...lines].join("\n"));
|
|
167
|
-
return text;
|
|
178
|
+
return frameToolResult(text, theme, false);
|
|
168
179
|
},
|
|
169
180
|
});
|
|
170
181
|
});
|
package/src/questionnaire.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
terminalModalHeight,
|
|
19
19
|
} from "@xynogen/pix-pretty/modal-frame";
|
|
20
20
|
import { ChipEditor } from "./chip-editor.js";
|
|
21
|
-
import { dim } from "./components.js";
|
|
22
21
|
import { checkboxGlyphs, selectionGlyph } from "./glyphs.js";
|
|
23
22
|
import { safeMarkdownTheme, sentinelsFor } from "./helpers.js";
|
|
24
23
|
import type { OptionData, Params, QuestionData } from "./schema.js";
|
|
@@ -127,8 +126,8 @@ export class AskQuestionnaire extends Container {
|
|
|
127
126
|
selectList: {
|
|
128
127
|
selectedPrefix: (s: string) => this.theme.fg("accent", s),
|
|
129
128
|
selectedText: (s: string) => this.theme.fg("accent", s),
|
|
130
|
-
description: (s: string) => this.theme.fg("
|
|
131
|
-
scrollInfo: (s: string) => this.theme.fg("
|
|
129
|
+
description: (s: string) => this.theme.fg("dim", s),
|
|
130
|
+
scrollInfo: (s: string) => this.theme.fg("muted", s),
|
|
132
131
|
noMatch: (s: string) => this.theme.fg("warning", s),
|
|
133
132
|
},
|
|
134
133
|
},
|
|
@@ -470,19 +469,19 @@ export class AskQuestionnaire extends Container {
|
|
|
470
469
|
Math.max(10, inner - LABEL_COL),
|
|
471
470
|
);
|
|
472
471
|
for (const w of wrapped) {
|
|
473
|
-
lines.push(truncateToWidth(`${pad}${t.fg("
|
|
472
|
+
lines.push(truncateToWidth(`${pad}${t.fg("dim", w)}`, inner, ""));
|
|
474
473
|
}
|
|
475
474
|
}
|
|
476
475
|
} else if (item.kind === "other") {
|
|
477
476
|
const label = sel
|
|
478
477
|
? t.fg("accent", t.bold(SENTINEL_FREEFORM))
|
|
479
478
|
: t.fg("text", t.bold(SENTINEL_FREEFORM));
|
|
480
|
-
lines.push(truncateToWidth(`${ptr} ${t.fg("
|
|
479
|
+
lines.push(truncateToWidth(`${ptr} ${t.fg("muted", "✎")} ${label}`, inner, ""));
|
|
481
480
|
} else if (item.kind === "next") {
|
|
482
481
|
const label = sel
|
|
483
482
|
? t.fg("accent", t.bold(SENTINEL_NEXT))
|
|
484
483
|
: t.fg("text", t.bold(SENTINEL_NEXT));
|
|
485
|
-
lines.push(truncateToWidth(`${ptr} ${t.fg("
|
|
484
|
+
lines.push(truncateToWidth(`${ptr} ${t.fg("muted", "→")} ${label}`, inner, ""));
|
|
486
485
|
}
|
|
487
486
|
if (sel)
|
|
488
487
|
this.selectedOptionRows = { start: itemStart, end: Math.max(itemStart + 1, lines.length) };
|
|
@@ -494,7 +493,7 @@ export class AskQuestionnaire extends Container {
|
|
|
494
493
|
private renderPreview(width: number): string[] {
|
|
495
494
|
const item = this.selectedItem;
|
|
496
495
|
if (item?.kind !== "option" || !item.option?.preview) {
|
|
497
|
-
return [this.theme.fg("
|
|
496
|
+
return [this.theme.fg("muted", "No preview")];
|
|
498
497
|
}
|
|
499
498
|
|
|
500
499
|
const mdText = item.option.preview;
|
|
@@ -506,7 +505,7 @@ export class AskQuestionnaire extends Container {
|
|
|
506
505
|
}
|
|
507
506
|
|
|
508
507
|
const lines = wrapTextWithAnsi(mdText, mdWidth);
|
|
509
|
-
return lines.map((l) => truncateToWidth(this.theme.fg("
|
|
508
|
+
return lines.map((l) => truncateToWidth(this.theme.fg("dim", l), mdWidth, ""));
|
|
510
509
|
}
|
|
511
510
|
|
|
512
511
|
override render(termWidth: number): string[] {
|
|
@@ -528,8 +527,8 @@ export class AskQuestionnaire extends Container {
|
|
|
528
527
|
let footer: string[] = [];
|
|
529
528
|
|
|
530
529
|
const row = (content: string): string => truncateToWidth(content, width, "");
|
|
531
|
-
const guide = (key: string, action: string) => t.fg("
|
|
532
|
-
const guideSep = t.fg("
|
|
530
|
+
const guide = (key: string, action: string) => t.fg("muted", `${key} ${action}`);
|
|
531
|
+
const guideSep = t.fg("muted", " • ");
|
|
533
532
|
|
|
534
533
|
// Tab bar — rendered as the framed top edge (frameLines `top`).
|
|
535
534
|
let top: string | undefined;
|
|
@@ -540,14 +539,14 @@ export class AskQuestionnaire extends Container {
|
|
|
540
539
|
const tag = `${i + 1}.${this.params.questions[i]?.header}`;
|
|
541
540
|
tabParts.push(active ? t.fg("accent", t.bold(tag)) : t.fg("dim", tag));
|
|
542
541
|
}
|
|
543
|
-
top = truncateToWidth(tabParts.join(t.fg("
|
|
542
|
+
top = truncateToWidth(tabParts.join(t.fg("muted", " ")), width, "");
|
|
544
543
|
}
|
|
545
544
|
|
|
546
545
|
// Header chip
|
|
547
546
|
const chip = t.fg("accent", t.bold(this.currentQ.header));
|
|
548
547
|
const prog =
|
|
549
548
|
this.params.questions.length > 1
|
|
550
|
-
?
|
|
549
|
+
? t.fg("muted", ` ${this.currentIndex + 1}/${this.params.questions.length}`)
|
|
551
550
|
: "";
|
|
552
551
|
header.push(row(`${chip}${prog}`));
|
|
553
552
|
|
|
@@ -583,7 +582,7 @@ export class AskQuestionnaire extends Container {
|
|
|
583
582
|
if (!isMulti) {
|
|
584
583
|
const searchVal = this.searchQuery
|
|
585
584
|
? t.fg("text", this.searchQuery)
|
|
586
|
-
: t.fg("
|
|
585
|
+
: t.fg("muted", "type to filter");
|
|
587
586
|
header.push(row(`${t.fg("accent", "Filter:")} ${searchVal}`));
|
|
588
587
|
}
|
|
589
588
|
|
|
@@ -593,7 +592,7 @@ export class AskQuestionnaire extends Container {
|
|
|
593
592
|
const maxOptLines = Math.max(optionLines.length, previewLines.length);
|
|
594
593
|
|
|
595
594
|
if (useSplit) {
|
|
596
|
-
const sep = t.fg("
|
|
595
|
+
const sep = t.fg("muted", SEPARATOR);
|
|
597
596
|
for (let i = 0; i < maxOptLines; i++) {
|
|
598
597
|
const left = truncateToWidth(optionLines[i] ?? "", leftWidth, "", true);
|
|
599
598
|
const right = truncateToWidth(previewLines[i] ?? "", previewWidth, "");
|
|
@@ -643,7 +642,7 @@ export class AskQuestionnaire extends Container {
|
|
|
643
642
|
color: (s) => t.fg("accent", s),
|
|
644
643
|
bg: (s) => t.bg("customMessageBg", s),
|
|
645
644
|
overflowLine: ({ page, totalPages }) =>
|
|
646
|
-
t.fg("
|
|
645
|
+
t.fg("muted", `PgUp/PgDn inspect • ${page}/${totalPages}`),
|
|
647
646
|
});
|
|
648
647
|
this.pager.sync(result);
|
|
649
648
|
return result.lines;
|