@bastani/atomic 0.9.14-alpha.2 → 0.9.14-alpha.4

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  4. package/dist/builtin/mcp/package.json +2 -2
  5. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  6. package/dist/builtin/subagents/agents/code-simplifier.md +1 -1
  7. package/dist/builtin/subagents/agents/codebase-analyzer.md +1 -1
  8. package/dist/builtin/subagents/agents/codebase-locator.md +1 -1
  9. package/dist/builtin/subagents/agents/codebase-online-researcher.md +1 -1
  10. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +1 -1
  11. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +1 -1
  12. package/dist/builtin/subagents/agents/codebase-research-locator.md +1 -1
  13. package/dist/builtin/subagents/agents/debugger.md +1 -1
  14. package/dist/builtin/subagents/agents/worker.md +1 -1
  15. package/dist/builtin/subagents/package.json +1 -1
  16. package/dist/builtin/web-access/CHANGELOG.md +16 -0
  17. package/dist/builtin/web-access/README.md +3 -6
  18. package/dist/builtin/web-access/index.ts +1 -2
  19. package/dist/builtin/web-access/package.json +2 -2
  20. package/dist/builtin/web-access/web-search-config.ts +2 -8
  21. package/dist/builtin/web-access/web-search-tool.ts +2 -7
  22. package/dist/builtin/web-access/web-search-workflow.ts +10 -0
  23. package/dist/builtin/workflows/CHANGELOG.md +18 -0
  24. package/dist/builtin/workflows/README.md +1 -1
  25. package/dist/builtin/workflows/builtin/goal-models.ts +4 -2
  26. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +2 -1
  27. package/dist/builtin/workflows/builtin/ralph-models.ts +10 -5
  28. package/dist/builtin/workflows/package.json +1 -1
  29. package/dist/builtin/workflows/src/extension/index.bundle.mjs +125 -66
  30. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +32 -4
  31. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +3 -9
  32. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +27 -20
  33. package/dist/builtin/workflows/src/runs/foreground/executor.ts +4 -0
  34. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +6 -1
  35. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +6 -1
  36. package/dist/builtin/workflows/src/tui/graph-theme.ts +11 -0
  37. package/dist/builtin/workflows/src/tui/graph-view-render.ts +19 -10
  38. package/dist/builtin/workflows/src/tui/stage-chat-view-custom-ui.ts +7 -8
  39. package/dist/builtin/workflows/src/tui/tool-detail.ts +45 -26
  40. package/dist/core/tools/ask-user-question/ask-user-question.d.ts +3 -1
  41. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  42. package/dist/core/tools/ask-user-question/ask-user-question.js +2 -1
  43. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
  44. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts +1 -0
  45. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  46. package/dist/core/tools/ask-user-question/state/build-questionnaire.js +1 -0
  47. package/dist/core/tools/ask-user-question/state/build-questionnaire.js.map +1 -1
  48. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  49. package/dist/core/tools/ask-user-question/state/key-router.js +3 -1
  50. package/dist/core/tools/ask-user-question/state/key-router.js.map +1 -1
  51. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +3 -0
  52. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  53. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +3 -0
  54. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  55. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -0
  56. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  57. package/dist/core/tools/ask-user-question/state/state-reducer.js +3 -1
  58. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  59. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts +2 -0
  60. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  61. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js +1 -0
  62. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -1
  63. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +3 -0
  64. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  65. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +2 -1
  66. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  67. package/dist/core/tools/edit-batch.d.ts +33 -0
  68. package/dist/core/tools/edit-batch.d.ts.map +1 -0
  69. package/dist/core/tools/edit-batch.js +89 -0
  70. package/dist/core/tools/edit-batch.js.map +1 -0
  71. package/dist/core/tools/edit.d.ts +1 -1
  72. package/dist/core/tools/edit.d.ts.map +1 -1
  73. package/dist/core/tools/edit.js +98 -42
  74. package/dist/core/tools/edit.js.map +1 -1
  75. package/docs/providers.md +1 -1
  76. package/docs/tools.md +1 -1
  77. package/docs/workflows.md +4 -4
  78. package/npm-shrinkwrap.json +65 -50
  79. package/package.json +5 -5
@@ -31,6 +31,7 @@ const MOCHA = {
31
31
  red: "#f38ba8",
32
32
  mauve: "#cba6f7",
33
33
  sky: "#89dceb",
34
+ lavender: "#b4befe",
34
35
  } as const;
35
36
 
36
37
  /**
@@ -51,6 +52,8 @@ export interface GenericTheme {
51
52
  text?: string;
52
53
  textMuted?: string;
53
54
  dim?: string;
55
+ toolTitle?: string;
56
+ toolOutput?: string;
54
57
  accent?: string;
55
58
  mauve?: string;
56
59
  success?: string;
@@ -87,6 +90,10 @@ export interface GraphTheme {
87
90
  text: string;
88
91
  textMuted: string;
89
92
  dim: string;
93
+ /** Host tool-card title (`toolTitle`, Catppuccin lavender). */
94
+ toolTitle: string;
95
+ /** Host tool-card body (`toolOutput`, usually the same as `text`). */
96
+ toolOutput: string;
90
97
 
91
98
  /** Accents */
92
99
  accent: string;
@@ -116,6 +123,8 @@ export function deriveGraphTheme(theme: GenericTheme = {}): GraphTheme {
116
123
  text: theme.text ?? MOCHA.text,
117
124
  textMuted: theme.textMuted ?? MOCHA.subtext0,
118
125
  dim: theme.dim ?? MOCHA.overlay1,
126
+ toolTitle: theme.toolTitle ?? MOCHA.lavender,
127
+ toolOutput: theme.toolOutput ?? MOCHA.text,
119
128
 
120
129
  accent: theme.accent ?? MOCHA.blue,
121
130
  mauve: theme.mauve ?? MOCHA.mauve,
@@ -262,6 +271,8 @@ export function deriveGraphThemeFromPiTheme(theme: unknown): GraphTheme {
262
271
  text: fgHex(t, "text") ?? fgHex(t, "customMessageText"),
263
272
  textMuted: fgHex(t, "muted"),
264
273
  dim: fgHex(t, "dim"),
274
+ toolTitle: fgHex(t, "toolTitle"),
275
+ toolOutput: fgHex(t, "toolOutput") ?? fgHex(t, "text") ?? fgHex(t, "customMessageText"),
265
276
 
266
277
  accent,
267
278
  success: fgHex(t, "success"),
@@ -26,6 +26,13 @@ function stripLayoutWrapper(line: string): string {
26
26
  const end = endsWithWrapper ? line.length - LAYOUT_OSC_RESET.length : line.length;
27
27
  return line.slice(start, Math.max(start, end));
28
28
  }
29
+ /** Unpainted canvas between the orchestrator bars and the painted card. */
30
+ const TOOL_DETAIL_VIEWPORT_PAD = 1;
31
+
32
+ function toolDetailViewportPad(rows: number): number {
33
+ return rows >= TOOL_DETAIL_VIEWPORT_PAD * 2 + 1 ? TOOL_DETAIL_VIEWPORT_PAD : 0;
34
+ }
35
+
29
36
  /** One frame's tool-message geometry, derived once and shared by both callbacks. */
30
37
  interface ToolDetailFrame {
31
38
  readonly width: number;
@@ -159,17 +166,15 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
159
166
  const safeWidth = Math.max(1, Math.floor(width));
160
167
  const cached = this.detailFrame;
161
168
  if (cached !== null && cached.width === safeWidth) return cached;
162
- // Match the flat message surface used by agent tool calls. The one-cell
163
- // leading inset gives the block the same breathing room without drawing a
164
- // standalone rounded panel or hiding content behind a border.
165
- const blockWidth = Math.max(1, safeWidth - 1);
169
+ // Span the same columns as the header/footer bars. Main-chat tool
170
+ // blocks are full width; only the Box(1,1) pad insets the `$` text.
166
171
  const frame: ToolDetailFrame = {
167
172
  width: safeWidth,
168
- blockWidth,
169
- leftPad: safeWidth > 1 ? 1 : 0,
173
+ blockWidth: safeWidth,
174
+ leftPad: 0,
170
175
  lines: renderToolDetailLines(node, {
171
176
  theme: this.graphTheme,
172
- width: blockWidth,
177
+ width: safeWidth,
173
178
  expanded: this.toolDetailExpanded,
174
179
  expandKey: toolExpandKey(isKeybindingsLike(this.piKeybindings) ? this.piKeybindings : undefined),
175
180
  }),
@@ -181,7 +186,8 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
181
186
  private _graphBodyContentHeight(width: number, viewportRows: number): number {
182
187
  const rows = Math.max(0, Math.floor(viewportRows));
183
188
  if (this.toolDetail !== null) {
184
- return Math.max(rows, this._toolDetailFrame(this.toolDetail, width).lines.length);
189
+ const pad = toolDetailViewportPad(rows);
190
+ return Math.max(rows, this._toolDetailFrame(this.toolDetail, width).lines.length + pad * 2);
185
191
  }
186
192
  if (!this._currentRenderRun() || this.cachedRenderGeometry.totalRows <= 0) return rows;
187
193
  return Math.max(rows, this.cachedRenderGeometry.totalRows);
@@ -249,9 +255,12 @@ export abstract class GraphViewRenderer extends GraphViewGraphRenderer {
249
255
  private _renderBody(width: number, top: number, rows: number, _contentRows: number): string[] {
250
256
  if (this.toolDetail !== null) {
251
257
  const detail = this._toolDetailFrame(this.toolDetail, width);
252
- return detail.lines
253
- .slice(top, top + rows)
258
+ const pad = toolDetailViewportPad(rows);
259
+ const inner = Math.max(0, rows - pad * 2);
260
+ const painted = detail.lines
261
+ .slice(top, top + inner)
254
262
  .map((line) => this._canvasRow(`${" ".repeat(detail.leftPad)}${line}`, width));
263
+ return [...Array.from({ length: pad }, () => ""), ...painted, ...Array.from({ length: pad }, () => "")];
255
264
  }
256
265
  const run = this._currentRenderRun();
257
266
  const raw = run ? this._renderGraph(width, top, rows, run) : this._renderEmptyBody(width, rows);
@@ -21,14 +21,13 @@ export async function showCustomUi(ctx: StageChatViewContext, request: StageCust
21
21
  );
22
22
  return;
23
23
  }
24
- if (request.options?.overlay === true) {
25
- ctx.mountingRequestId = null;
26
- ctx.stageUiBroker.reject(
27
- request,
28
- new Error("atomic-workflows: ctx.ui.custom overlay mode is unavailable in the workflow graph viewer"),
29
- );
30
- return;
31
- }
24
+ // `overlay: true` (and its `overlayOptions` / `reserveTranscriptRows`
25
+ // companions) is a placement hint for the main-chat host, not a capability
26
+ // request. The stage-chat custom-UI slot already gives the widget what an
27
+ // overlay buys it there — focus, and a transcript that stays visible and
28
+ // scrollable behind it so an overlay request mounts on this same path
29
+ // rather than failing the prompt. `ask_user_question` always asks for an
30
+ // overlay, so rejecting it made every in-stage questionnaire fail.
32
31
  try {
33
32
  const mounted = await mountStageCustomUi(
34
33
  request,
@@ -19,7 +19,14 @@ export const TOOL_DETAIL_VALUE_LIMIT = TOOL_PAYLOAD_VALUE_LIMIT;
19
19
  const COLLAPSED_ARGS_LIMIT = 240;
20
20
  /** Match the host tool renderer's collapsed fallback preview row bound. */
21
21
  const COLLAPSED_RESULT_LINES = 10;
22
- const BODY_INDENT = " ";
22
+ /** Match pi-tui `Box(paddingX=1, paddingY=1)` around the host tool card. */
23
+ const BOX_PAD = 1;
24
+
25
+ function boxMetrics(width: number): { pad: string; inner: number } {
26
+ const safeWidth = Math.max(1, Math.floor(width));
27
+ const inset = safeWidth >= BOX_PAD * 2 + 1 ? BOX_PAD : 0;
28
+ return { pad: " ".repeat(inset), inner: Math.max(1, safeWidth - inset * 2) };
29
+ }
23
30
 
24
31
  export interface RenderToolDetailOpts {
25
32
  /** Provide for ANSI output; omit for plain text. */
@@ -79,17 +86,31 @@ function styledStatus(status: ToolNodeStatus, theme: GraphTheme | undefined): st
79
86
  }
80
87
 
81
88
  function styledTitle(text: string, theme: GraphTheme | undefined): string {
82
- return theme === undefined ? text : `${hexToAnsi(theme.text)}${BOLD}${text}${RESET}`;
89
+ return theme === undefined ? text : `${hexToAnsi(theme.toolTitle)}${BOLD}${text}${RESET}`;
90
+ }
91
+
92
+ function styledOutput(text: string, theme: GraphTheme | undefined): string {
93
+ return theme === undefined ? text : `${hexToAnsi(theme.toolOutput)}${text}${RESET}`;
83
94
  }
84
95
 
85
96
  function styledMuted(text: string, theme: GraphTheme | undefined): string {
86
97
  return theme === undefined ? text : `${hexToAnsi(theme.textMuted)}${text}${RESET}`;
87
98
  }
88
99
 
100
+ function styledDim(text: string, theme: GraphTheme | undefined): string {
101
+ return theme === undefined ? text : `${hexToAnsi(theme.dim)}${text}${RESET}`;
102
+ }
103
+
89
104
  function styledError(text: string, theme: GraphTheme | undefined): string {
90
105
  return theme === undefined ? text : `${hexToAnsi(theme.error)}${text}${RESET}`;
91
106
  }
92
107
 
108
+ function expandHintLine(earlier: number, expandKey: string, pad: string, theme: GraphTheme | undefined): string {
109
+ const lead = `... (${earlier} earlier lines`;
110
+ if (expandKey.length === 0) return `${pad}${styledMuted(`${lead})`, theme)}`;
111
+ return `${pad}${styledMuted(`${lead}, `, theme)}${styledDim(expandKey, theme)}${styledMuted(" Expand)", theme)}`;
112
+ }
113
+
93
114
  function toolBackground(status: ToolNodeStatus, theme: GraphTheme): string {
94
115
  switch (status) {
95
116
  case "completed":
@@ -249,7 +270,7 @@ function messageHeaderRows(
249
270
  theme: GraphTheme | undefined,
250
271
  expanded: boolean,
251
272
  ): string[] {
252
- const safeWidth = Math.max(1, Math.floor(width));
273
+ const { pad, inner } = boxMetrics(width);
253
274
  const name = sanitizeToolTitleText(tool.name);
254
275
  const glyph = headerGlyph(tool.status);
255
276
  const glyphSuffix = glyph === undefined ? "" : ` ${glyph}`;
@@ -258,38 +279,36 @@ function messageHeaderRows(
258
279
  ? ""
259
280
  : boundedSerializedValue(tool.args, expanded ? TOOL_DETAIL_VALUE_LIMIT : COLLAPSED_ARGS_LIMIT);
260
281
  const withArgs = `$ ${name}${args ? ` ${args}` : ""}${glyphSuffix}`;
261
- const plain = !expanded && visibleWidth(withArgs) > safeWidth ? `$ ${name}${glyphSuffix}` : withArgs;
282
+ const plain = !expanded && visibleWidth(withArgs) > inner ? `$ ${name}${glyphSuffix}` : withArgs;
262
283
  const titleEnd = `$ ${name}`;
263
- const continuationIndent = BODY_INDENT;
264
- const continuationWidth = Math.max(1, safeWidth - visibleWidth(continuationIndent));
265
- return wrapFieldValue(plain, safeWidth, continuationWidth).map((chunk, index, chunks) => {
266
- const prefix = index === 0 ? "" : continuationIndent;
267
- if (index !== 0 || !chunk.startsWith(titleEnd)) return `${prefix}${styledMuted(chunk, theme)}`;
284
+ return wrapFieldValue(plain, inner, inner).map((chunk, index, chunks) => {
285
+ if (index !== 0 || !chunk.startsWith(titleEnd)) return `${pad}${styledMuted(chunk, theme)}`;
268
286
  const remainder = chunk.slice(titleEnd.length);
269
287
  const hasStatus = glyphSuffix.length > 0 && index === chunks.length - 1 && remainder.endsWith(glyphSuffix);
270
288
  const summary = hasStatus ? remainder.slice(0, -glyphSuffix.length) : remainder;
271
289
  const suffix = hasStatus ? ` ${styledStatus(tool.status, theme)}` : "";
272
- return `${styledTitle(titleEnd, theme)}${summary.trim().length > 0 ? styledMuted(summary, theme) : ""}${suffix}`;
290
+ const title = styledTitle(titleEnd, theme);
291
+ const rest = summary.trim().length > 0 ? styledMuted(summary, theme) : "";
292
+ return `${pad}${title}${rest}${suffix}`;
273
293
  });
274
294
  }
275
295
 
276
296
  function bodyRows(text: string, error: boolean, width: number, theme: GraphTheme | undefined): string[] {
277
- const contentWidth = Math.max(1, width - visibleWidth(BODY_INDENT));
278
- return wrapFieldValue(text, contentWidth).map((line) => {
279
- const styled = error ? styledError(line, theme) : styledMuted(line, theme);
280
- return `${BODY_INDENT}${styled}`;
297
+ const { pad, inner } = boxMetrics(width);
298
+ return wrapFieldValue(text, inner).map((line) => {
299
+ const styled = error ? styledError(line, theme) : styledOutput(line, theme);
300
+ return `${pad}${styled}`;
281
301
  });
282
302
  }
283
303
 
284
304
  function footerText(tool: ToolNodeSnapshot, elapsed: number | undefined): string | undefined {
285
305
  if (elapsed === undefined) return undefined;
286
306
  const label = tool.status === "running" ? "Elapsed" : "Took";
287
- const duration = elapsed < 1000 ? `${Math.round(elapsed)}ms` : fmtDuration(elapsed);
288
307
  const markers = [
289
308
  tool.status === "cached" ? "cached" : undefined,
290
309
  tool.replayed === true ? "replayed" : undefined,
291
310
  ].filter((marker): marker is string => marker !== undefined);
292
- return `${label} ${duration}${markers.length > 0 ? ` · ${markers.join(" · ")}` : ""}`;
311
+ return `${label} ${fmtDuration(elapsed)}${markers.length > 0 ? ` · ${markers.join(" · ")}` : ""}`;
293
312
  }
294
313
 
295
314
  function renderMessageLines(
@@ -301,36 +320,36 @@ function renderMessageLines(
301
320
  now: number,
302
321
  ): string[] {
303
322
  const safeWidth = Math.max(1, Math.floor(width));
323
+ const { pad, inner } = boxMetrics(safeWidth);
304
324
  const result = resultText(tool, TOOL_DETAIL_VALUE_LIMIT);
305
325
  const rows: string[] = messageHeaderRows(tool, safeWidth, theme, expanded);
326
+ // Host bash/result renderers put a blank row between the `$` call and the body.
327
+ rows.push("");
306
328
  const wrappedBody = bodyRows(result.text, result.error, safeWidth, theme);
307
329
  if (expanded) {
308
330
  rows.push(...wrappedBody);
309
331
  if (tool.source !== undefined && tool.source.length > 0) {
310
- const sourceWidth = Math.max(1, safeWidth - visibleWidth(BODY_INDENT));
311
332
  rows.push(
312
- ...wrapFieldValue(boundedToolText(sanitizeToolDisplayText(tool.source)), sourceWidth).map(
313
- (line) => `${BODY_INDENT}${styledMuted(line, theme)}`,
333
+ ...wrapFieldValue(boundedToolText(sanitizeToolDisplayText(tool.source)), inner).map(
334
+ (line) => `${pad}${styledMuted(line, theme)}`,
314
335
  ),
315
336
  );
316
337
  }
317
338
  } else {
318
339
  const earlier = Math.max(0, wrappedBody.length - COLLAPSED_RESULT_LINES);
319
340
  if (earlier > 0) {
320
- const hint =
321
- expandKey.length > 0
322
- ? `... (${earlier} earlier lines, ${expandKey} Expand)`
323
- : `... (${earlier} earlier lines)`;
324
- rows.push(` ${styledMuted(hint, theme)}`);
341
+ rows.push(expandHintLine(earlier, expandKey, pad, theme));
325
342
  }
326
343
  rows.push(...wrappedBody.slice(-COLLAPSED_RESULT_LINES));
327
344
  }
328
345
  const footer = footerText(tool, durationMs(tool, now));
329
346
  if (footer !== undefined) {
330
347
  rows.push("");
331
- rows.push(styledMuted(footer, theme));
348
+ rows.push(`${pad}${styledMuted(footer, theme)}`);
332
349
  }
333
- return rows.map((row) => paintRow(row, safeWidth, theme, tool.status));
350
+ const painted = rows.map((row) => paintRow(row, safeWidth, theme, tool.status));
351
+ const padRow = paintRow("", safeWidth, theme, tool.status);
352
+ return [padRow, ...painted, padRow];
334
353
  }
335
354
 
336
355
  /** Render one read-only host-style operator tool message block. */
@@ -28,6 +28,8 @@ export declare const askUserQuestionToolSystemPromptContribution: Readonly<{
28
28
  readonly snippet: "Ask the user up to 4 structured questions (2-4 options each) when requirements are ambiguous";
29
29
  readonly guidelines: readonly ["Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions — you can ask up to 4 questions per invocation.", "Each question MUST have 2-4 options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer (\"Type something.\" row is appended automatically to single-select questions) or pick \"Chat about this\" to abandon the questionnaire.", "Set multiSelect: true when multiple answers are valid; this suppresses the \"Type something.\" row. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs) — single-select only. NOTE: any non-empty preview on a single-select question ALSO suppresses the \"Type something.\" row (no room in the side-by-side layout); \"Chat about this\" remains the escape hatch. If you recommend a specific option, make it the first option and append \"(Recommended)\" to its label.", "Do not stack multiple ask_user_question calls back-to-back — group all clarifying questions into one invocation."];
30
30
  }>;
31
- export declare function createAskUserQuestionToolDefinition(): ToolDefinition<typeof QuestionParamsSchema, unknown>;
31
+ export declare function createAskUserQuestionToolDefinition(options?: {
32
+ readonly chatAsOption?: boolean;
33
+ }): ToolDefinition<typeof QuestionParamsSchema, unknown>;
32
34
  export { buildQuestionnaireResponse, buildToolResult };
33
35
  //# sourceMappingURL=ask-user-question.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ask-user-question.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/ask-user-question.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAIN,KAAK,YAAY,EAGjB,oBAAoB,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAI/E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,EAAE,cAG3C,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,kBAAkB,EAAE,CAWlF;AAED,eAAO,MAAM,2CAA2C;;;EAQ7C,CAAC;AAEZ,wBAAgB,mCAAmC,IAAI,cAAc,CAAC,OAAO,oBAAoB,EAAE,OAAO,CAAC,CAkF1G;AAED,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"ask-user-question.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/ask-user-question.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAIN,KAAK,YAAY,EAGjB,oBAAoB,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAI/E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,EAAE,cAG3C,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,kBAAkB,EAAE,CAWlF;AAED,eAAO,MAAM,2CAA2C;;;EAQ7C,CAAC;AAEZ,wBAAgB,mCAAmC,CAAC,OAAO,CAAC,EAAE;IAC7D,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CAChC,GAAG,cAAc,CAAC,OAAO,oBAAoB,EAAE,OAAO,CAAC,CAmFvD;AAED,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,CAAC"}
@@ -50,7 +50,7 @@ export const askUserQuestionToolSystemPromptContribution = Object.freeze({
50
50
  "Do not stack multiple ask_user_question calls back-to-back — group all clarifying questions into one invocation.",
51
51
  ]),
52
52
  });
53
- export function createAskUserQuestionToolDefinition() {
53
+ export function createAskUserQuestionToolDefinition(options) {
54
54
  const guidance = validateGuidanceFields(loadConfig().guidance);
55
55
  return {
56
56
  name: "ask_user_question",
@@ -111,6 +111,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
111
111
  params: typed,
112
112
  itemsByTab,
113
113
  done,
114
+ ...(options?.chatAsOption === true ? { chatAsOption: true } : {}),
114
115
  });
115
116
  return session.component;
116
117
  }, {
@@ -1 +1 @@
1
- {"version":3,"file":"ask-user-question.js","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/ask-user-question.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OverlayOptions } from \"@earendil-works/pi-tui\";\nimport { experimentalToolSamplingProperty } from \"../../experimental.ts\";\nimport type { ToolDefinition } from \"../../extensions/types.ts\";\nimport { loadConfig, validateGuidanceFields } from \"./config.ts\";\nimport { QuestionnaireSession } from \"./state/questionnaire-session.ts\";\nimport { ROW_INTENT_META, sentinelsToAppend } from \"./state/row-intent.ts\";\nimport { buildQuestionnaireResponse, buildToolResult } from \"./tool/response-envelope.ts\";\nimport {\n\tMAX_OPTIONS,\n\tMAX_QUESTIONS,\n\tMIN_OPTIONS,\n\ttype QuestionData,\n\ttype QuestionnaireResult,\n\ttype QuestionParams,\n\tQuestionParamsSchema,\n} from \"./tool/types.ts\";\nimport { validateQuestionnaire } from \"./tool/validate-questionnaire.ts\";\nimport type { WrappingSelectItem } from \"./view/components/wrapping-select.ts\";\n\nconst ERROR_NO_UI = \"Error: UI not available (running in non-interactive mode)\";\n\n/**\n * Mount options for the blocking questionnaire (#2378).\n *\n * The dialog used to mount inline, inside the fullscreen dock, where it is a\n * flex sibling of the transcript `ScrollView`. A tall side-by-side dialog then\n * took the transcript's rows: on a 40-row terminal the transcript viewport\n * collapsed from 34 rows to 6, and pi-tui derives its page step from that\n * viewport (`viewportHeight - PAGE_SCROLL_OVERLAP`), so PageUp crawled two\n * lines at a time through a six-line window.\n *\n * A bottom-anchored overlay is composited over the bottom rows instead of being\n * measured into the layout, so the transcript keeps its full viewport height\n * and its full page step. `reserveTranscriptRows` is what makes those rows\n * observable rather than merely addressable: the host bounds this overlay so a\n * transcript strip always survives, and extends the transcript's scroll extent\n * by the rows the overlay still covers, so the newest output can be raised into\n * that strip. No `maxHeight` — the host's bound is tighter, and letting pi-tui\n * slice as well would make the measured overlay height wrong.\n */\nexport const QUESTIONNAIRE_OVERLAY_OPTIONS: OverlayOptions = {\n\tanchor: \"bottom-center\",\n\twidth: \"100%\",\n};\n\nexport function buildItemsForQuestion(question: QuestionData): WrappingSelectItem[] {\n\tconst items: WrappingSelectItem[] = question.options.map((o) => ({\n\t\tkind: \"option\",\n\t\tlabel: o.label,\n\t\tdescription: o.description,\n\t}));\n\tconst hasAnyPreview = question.options.some((o) => typeof o.preview === \"string\" && o.preview.length > 0);\n\tfor (const kind of sentinelsToAppend(question, hasAnyPreview)) {\n\t\titems.push({ kind, label: ROW_INTENT_META[kind].label });\n\t}\n\treturn items;\n}\n\nexport const askUserQuestionToolSystemPromptContribution = Object.freeze({\n\tsnippet: `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous`,\n\tguidelines: Object.freeze([\n\t\t`Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions — you can ask up to ${MAX_QUESTIONS} questions per invocation.`,\n\t\t`Each question MUST have ${MIN_OPTIONS}-${MAX_OPTIONS} options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer (\"Type something.\" row is appended automatically to single-select questions) or pick \"Chat about this\" to abandon the questionnaire.`,\n\t\t`Set multiSelect: true when multiple answers are valid; this suppresses the \"Type something.\" row. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs) — single-select only. NOTE: any non-empty preview on a single-select question ALSO suppresses the \"Type something.\" row (no room in the side-by-side layout); \"Chat about this\" remains the escape hatch. If you recommend a specific option, make it the first option and append \"(Recommended)\" to its label.`,\n\t\t\"Do not stack multiple ask_user_question calls back-to-back — group all clarifying questions into one invocation.\",\n\t] as const),\n} as const);\n\nexport function createAskUserQuestionToolDefinition(): ToolDefinition<typeof QuestionParamsSchema, unknown> {\n\tconst guidance = validateGuidanceFields(loadConfig().guidance);\n\treturn {\n\t\tname: \"ask_user_question\",\n\t\tlabel: \"Ask User Question\",\n\t\tdescription: `Ask the user one or more structured questions during execution. Use when you need to:\n1. Gather user preferences or requirements\n2. Clarify ambiguous instructions\n3. Get decisions on implementation choices as you work\n4. Offer choices to the user about what direction to take\n\nUsage notes:\n- Users will always be able to type a custom answer (\"Type something.\" row is appended automatically to every single-select question) or pick \"Chat about this\" to abandon the questionnaire and continue in free-form conversation. Do NOT author \"Other\" / \"Type something.\" / \"Chat about this\" labels yourself — duplicates are rejected at runtime.\n- Use multiSelect: true to allow multiple answers to be selected for a question. The \"Type something.\" row is suppressed on multi-select questions, and is ALSO suppressed on single-select questions where any option carries a \\`preview\\` (the side-by-side layout has no room for inline custom text — \"Chat about this\" remains as the free-form escape hatch).\n- If you recommend a specific option, make that the first option in the list and add \"(Recommended)\" at the end of the label.\n\nPreview feature:\nUse the optional \\`preview\\` field on options when presenting concrete artifacts that users need to visually compare:\n- ASCII mockups of UI layouts or components\n- Code snippets showing different implementations\n- Diagram variations\n- Configuration examples\n\nPreview content is rendered as markdown in a monospace box. Multi-line text with newlines is supported. When any option has a preview, the UI switches to a side-by-side layout with a vertical option list on the left and preview on the right. Do not use previews for simple preference questions where labels and descriptions suffice. Note: previews are only supported for single-select questions (not multiSelect).`,\n\t\tpromptSnippet: guidance.promptSnippet ?? askUserQuestionToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: guidance.promptGuidelines ?? [...askUserQuestionToolSystemPromptContribution.guidelines],\n\t\t...experimentalToolSamplingProperty(),\n\t\tparameters: QuestionParamsSchema,\n\n\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\tconst typed = params as unknown as QuestionParams;\n\t\t\tif (!ctx.hasUI) return buildToolResult(ERROR_NO_UI, { answers: [], cancelled: true, error: \"no_ui\" });\n\n\t\t\tconst validation = validateQuestionnaire(typed);\n\t\t\tif (!validation.ok) {\n\t\t\t\treturn buildToolResult(validation.message, {\n\t\t\t\t\tanswers: [],\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\terror: validation.error,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst itemsByTab: WrappingSelectItem[][] = typed.questions.map((q) => buildItemsForQuestion(q));\n\n\t\t\t// Suspend the animated working loader for the lifetime of the blocking dialog.\n\t\t\t//\n\t\t\t// The loader ticks every ~88ms and calls `requestRender()` on each frame, and it\n\t\t\t// conveys nothing while we are blocked on human input. Hiding it for the duration\n\t\t\t// keeps the frame behind the dialog static and avoids the differential renderer\n\t\t\t// falling back to a full clear+replay (`\\x1b[2J\\x1b[H\\x1b[3J`) on every tick.\n\t\t\t// Restored once the dialog closes (on every path).\n\t\t\t//\n\t\t\t// Guarded: some hosts (e.g. the workflow stage-UI broker) pass a minimal UI\n\t\t\t// context that only implements `custom`, so treat a missing loader control as a\n\t\t\t// no-op rather than throwing.\n\t\t\tctx.ui.setWorkingVisible?.(false);\n\t\t\ttry {\n\t\t\t\tconst result = await ctx.ui.custom<QuestionnaireResult>(\n\t\t\t\t\t(tui, theme, _kb, done) => {\n\t\t\t\t\t\tconst session = new QuestionnaireSession({\n\t\t\t\t\t\t\ttui,\n\t\t\t\t\t\t\ttheme,\n\t\t\t\t\t\t\tparams: typed,\n\t\t\t\t\t\t\titemsByTab,\n\t\t\t\t\t\t\tdone,\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn session.component;\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\toverlay: true,\n\t\t\t\t\t\treserveTranscriptRows: true,\n\t\t\t\t\t\toverlayOptions: QUESTIONNAIRE_OVERLAY_OPTIONS,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\treturn buildQuestionnaireResponse(result, typed);\n\t\t\t} finally {\n\t\t\t\tctx.ui.setWorkingVisible?.(true);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport { buildQuestionnaireResponse, buildToolResult };\n"]}
1
+ {"version":3,"file":"ask-user-question.js","sourceRoot":"","sources":["../../../../src/core/tools/ask-user-question/ask-user-question.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OverlayOptions } from \"@earendil-works/pi-tui\";\nimport { experimentalToolSamplingProperty } from \"../../experimental.ts\";\nimport type { ToolDefinition } from \"../../extensions/types.ts\";\nimport { loadConfig, validateGuidanceFields } from \"./config.ts\";\nimport { QuestionnaireSession } from \"./state/questionnaire-session.ts\";\nimport { ROW_INTENT_META, sentinelsToAppend } from \"./state/row-intent.ts\";\nimport { buildQuestionnaireResponse, buildToolResult } from \"./tool/response-envelope.ts\";\nimport {\n\tMAX_OPTIONS,\n\tMAX_QUESTIONS,\n\tMIN_OPTIONS,\n\ttype QuestionData,\n\ttype QuestionnaireResult,\n\ttype QuestionParams,\n\tQuestionParamsSchema,\n} from \"./tool/types.ts\";\nimport { validateQuestionnaire } from \"./tool/validate-questionnaire.ts\";\nimport type { WrappingSelectItem } from \"./view/components/wrapping-select.ts\";\n\nconst ERROR_NO_UI = \"Error: UI not available (running in non-interactive mode)\";\n\n/**\n * Mount options for the blocking questionnaire (#2378).\n *\n * The dialog used to mount inline, inside the fullscreen dock, where it is a\n * flex sibling of the transcript `ScrollView`. A tall side-by-side dialog then\n * took the transcript's rows: on a 40-row terminal the transcript viewport\n * collapsed from 34 rows to 6, and pi-tui derives its page step from that\n * viewport (`viewportHeight - PAGE_SCROLL_OVERLAP`), so PageUp crawled two\n * lines at a time through a six-line window.\n *\n * A bottom-anchored overlay is composited over the bottom rows instead of being\n * measured into the layout, so the transcript keeps its full viewport height\n * and its full page step. `reserveTranscriptRows` is what makes those rows\n * observable rather than merely addressable: the host bounds this overlay so a\n * transcript strip always survives, and extends the transcript's scroll extent\n * by the rows the overlay still covers, so the newest output can be raised into\n * that strip. No `maxHeight` — the host's bound is tighter, and letting pi-tui\n * slice as well would make the measured overlay height wrong.\n */\nexport const QUESTIONNAIRE_OVERLAY_OPTIONS: OverlayOptions = {\n\tanchor: \"bottom-center\",\n\twidth: \"100%\",\n};\n\nexport function buildItemsForQuestion(question: QuestionData): WrappingSelectItem[] {\n\tconst items: WrappingSelectItem[] = question.options.map((o) => ({\n\t\tkind: \"option\",\n\t\tlabel: o.label,\n\t\tdescription: o.description,\n\t}));\n\tconst hasAnyPreview = question.options.some((o) => typeof o.preview === \"string\" && o.preview.length > 0);\n\tfor (const kind of sentinelsToAppend(question, hasAnyPreview)) {\n\t\titems.push({ kind, label: ROW_INTENT_META[kind].label });\n\t}\n\treturn items;\n}\n\nexport const askUserQuestionToolSystemPromptContribution = Object.freeze({\n\tsnippet: `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous`,\n\tguidelines: Object.freeze([\n\t\t`Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions — you can ask up to ${MAX_QUESTIONS} questions per invocation.`,\n\t\t`Each question MUST have ${MIN_OPTIONS}-${MAX_OPTIONS} options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer (\"Type something.\" row is appended automatically to single-select questions) or pick \"Chat about this\" to abandon the questionnaire.`,\n\t\t`Set multiSelect: true when multiple answers are valid; this suppresses the \"Type something.\" row. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs) — single-select only. NOTE: any non-empty preview on a single-select question ALSO suppresses the \"Type something.\" row (no room in the side-by-side layout); \"Chat about this\" remains the escape hatch. If you recommend a specific option, make it the first option and append \"(Recommended)\" to its label.`,\n\t\t\"Do not stack multiple ask_user_question calls back-to-back — group all clarifying questions into one invocation.\",\n\t] as const),\n} as const);\n\nexport function createAskUserQuestionToolDefinition(options?: {\n\treadonly chatAsOption?: boolean;\n}): ToolDefinition<typeof QuestionParamsSchema, unknown> {\n\tconst guidance = validateGuidanceFields(loadConfig().guidance);\n\treturn {\n\t\tname: \"ask_user_question\",\n\t\tlabel: \"Ask User Question\",\n\t\tdescription: `Ask the user one or more structured questions during execution. Use when you need to:\n1. Gather user preferences or requirements\n2. Clarify ambiguous instructions\n3. Get decisions on implementation choices as you work\n4. Offer choices to the user about what direction to take\n\nUsage notes:\n- Users will always be able to type a custom answer (\"Type something.\" row is appended automatically to every single-select question) or pick \"Chat about this\" to abandon the questionnaire and continue in free-form conversation. Do NOT author \"Other\" / \"Type something.\" / \"Chat about this\" labels yourself — duplicates are rejected at runtime.\n- Use multiSelect: true to allow multiple answers to be selected for a question. The \"Type something.\" row is suppressed on multi-select questions, and is ALSO suppressed on single-select questions where any option carries a \\`preview\\` (the side-by-side layout has no room for inline custom text — \"Chat about this\" remains as the free-form escape hatch).\n- If you recommend a specific option, make that the first option in the list and add \"(Recommended)\" at the end of the label.\n\nPreview feature:\nUse the optional \\`preview\\` field on options when presenting concrete artifacts that users need to visually compare:\n- ASCII mockups of UI layouts or components\n- Code snippets showing different implementations\n- Diagram variations\n- Configuration examples\n\nPreview content is rendered as markdown in a monospace box. Multi-line text with newlines is supported. When any option has a preview, the UI switches to a side-by-side layout with a vertical option list on the left and preview on the right. Do not use previews for simple preference questions where labels and descriptions suffice. Note: previews are only supported for single-select questions (not multiSelect).`,\n\t\tpromptSnippet: guidance.promptSnippet ?? askUserQuestionToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: guidance.promptGuidelines ?? [...askUserQuestionToolSystemPromptContribution.guidelines],\n\t\t...experimentalToolSamplingProperty(),\n\t\tparameters: QuestionParamsSchema,\n\n\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\tconst typed = params as unknown as QuestionParams;\n\t\t\tif (!ctx.hasUI) return buildToolResult(ERROR_NO_UI, { answers: [], cancelled: true, error: \"no_ui\" });\n\n\t\t\tconst validation = validateQuestionnaire(typed);\n\t\t\tif (!validation.ok) {\n\t\t\t\treturn buildToolResult(validation.message, {\n\t\t\t\t\tanswers: [],\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\terror: validation.error,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst itemsByTab: WrappingSelectItem[][] = typed.questions.map((q) => buildItemsForQuestion(q));\n\n\t\t\t// Suspend the animated working loader for the lifetime of the blocking dialog.\n\t\t\t//\n\t\t\t// The loader ticks every ~88ms and calls `requestRender()` on each frame, and it\n\t\t\t// conveys nothing while we are blocked on human input. Hiding it for the duration\n\t\t\t// keeps the frame behind the dialog static and avoids the differential renderer\n\t\t\t// falling back to a full clear+replay (`\\x1b[2J\\x1b[H\\x1b[3J`) on every tick.\n\t\t\t// Restored once the dialog closes (on every path).\n\t\t\t//\n\t\t\t// Guarded: some hosts (e.g. the workflow stage-UI broker) pass a minimal UI\n\t\t\t// context that only implements `custom`, so treat a missing loader control as a\n\t\t\t// no-op rather than throwing.\n\t\t\tctx.ui.setWorkingVisible?.(false);\n\t\t\ttry {\n\t\t\t\tconst result = await ctx.ui.custom<QuestionnaireResult>(\n\t\t\t\t\t(tui, theme, _kb, done) => {\n\t\t\t\t\t\tconst session = new QuestionnaireSession({\n\t\t\t\t\t\t\ttui,\n\t\t\t\t\t\t\ttheme,\n\t\t\t\t\t\t\tparams: typed,\n\t\t\t\t\t\t\titemsByTab,\n\t\t\t\t\t\t\tdone,\n\t\t\t\t\t\t\t...(options?.chatAsOption === true ? { chatAsOption: true } : {}),\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn session.component;\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\toverlay: true,\n\t\t\t\t\t\treserveTranscriptRows: true,\n\t\t\t\t\t\toverlayOptions: QUESTIONNAIRE_OVERLAY_OPTIONS,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\treturn buildQuestionnaireResponse(result, typed);\n\t\t\t} finally {\n\t\t\t\tctx.ui.setWorkingVisible?.(true);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport { buildQuestionnaireResponse, buildToolResult };\n"]}
@@ -17,6 +17,7 @@ export interface QuestionnaireBuildConfig {
17
17
  isMulti: boolean;
18
18
  initialState: QuestionnaireState;
19
19
  getCurrentTab: () => number;
20
+ chatAsOption?: boolean;
20
21
  }
21
22
  export interface QuestionnaireBuilt {
22
23
  adapter: QuestionnairePropsAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"build-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/build-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgBrD,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,uCAAuC,CAAC;AAErG,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAerE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE;QAAE,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,aAAa,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,yBAAyB,CAAC;IACnC,UAAU,EAAE,KAAK,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,IAAI,CAAC;CACvB;AAwBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,kBAAkB,CAEvF"}
1
+ {"version":3,"file":"build-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/build-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgBrD,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,uCAAuC,CAAC;AAErG,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAerE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE;QAAE,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,kBAAkB,CAAC;IACjC,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,yBAAyB,CAAC;IACnC,UAAU,EAAE,KAAK,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,IAAI,CAAC;CACvB;AAwBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,kBAAkB,CAEvF"}
@@ -59,6 +59,7 @@ class QuestionnaireBuilder {
59
59
  this.chatRow = new ChatRowView({
60
60
  item: { kind: "chat", label: ROW_INTENT_META.chat.label },
61
61
  theme: this.selectTheme,
62
+ inlineInputEnabled: config.chatAsOption !== true,
62
63
  });
63
64
  }
64
65
  build() {
@@ -1 +1 @@
1
- {"version":3,"file":"build-questionnaire.js","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/build-questionnaire.ts"],"names":[],"mappings":"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","sourcesContent":["import { Input } from \"@earendil-works/pi-tui\";\nimport { getMarkdownTheme, type Theme } from \"../../../../modes/interactive/theme/theme.ts\";\nimport type { QuestionData } from \"../tool/types.ts\";\nimport {\n\ttype BoundGlobalBinding,\n\ttype BoundPerTabBinding,\n\tglobalBinding,\n\tperTabBinding,\n} from \"../view/component-binding.ts\";\nimport { ChatRowView } from \"../view/components/chat-row-view.ts\";\nimport { MultiSelectView } from \"../view/components/multi-select-view.ts\";\nimport { OptionListView } from \"../view/components/option-list-view.ts\";\nimport { PreviewBlockRenderer } from \"../view/components/preview/preview-block-renderer.ts\";\nimport { crossTabLeftWidthWithDonation } from \"../view/components/preview/preview-layout-decider.ts\";\nimport type { PreviewPaneProps } from \"../view/components/preview/preview-pane.ts\";\nimport { PreviewPane } from \"../view/components/preview/preview-pane.ts\";\nimport { SubmitPicker } from \"../view/components/submit-picker.ts\";\nimport { TabBar } from \"../view/components/tab-bar.ts\";\nimport type { WrappingSelectItem, WrappingSelectTheme } from \"../view/components/wrapping-select.ts\";\nimport { DialogView } from \"../view/dialog-builder.ts\";\nimport { QuestionnairePropsAdapter } from \"../view/props-adapter.ts\";\nimport type { StatefulView } from \"../view/stateful-view.ts\";\nimport type { TabBodyHeights, TabComponents } from \"../view/tab-components.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { PerTabSelector } from \"./selectors/contract.ts\";\nimport { selectActivePreviewPaneIndex } from \"./selectors/derivations.ts\";\nimport {\n\tselectChatRowProps,\n\tselectDialogProps,\n\tselectMultiSelectProps,\n\tselectOptionListProps,\n\tselectPreviewPaneProps,\n\tselectSubmitPickerProps,\n\tselectTabBarProps,\n} from \"./selectors/projections.ts\";\nimport type { QuestionnaireState } from \"./state.ts\";\n\nexport interface QuestionnaireBuildConfig {\n\ttui: { terminal: { columns: number }; requestRender(): void };\n\ttheme: Theme;\n\tquestions: readonly QuestionData[];\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\tisMulti: boolean;\n\tinitialState: QuestionnaireState;\n\tgetCurrentTab: () => number;\n}\n\nexport interface QuestionnaireBuilt {\n\tadapter: QuestionnairePropsAdapter;\n\tnotesInput: Input;\n\tinlineInput: Input;\n\trender: (width: number) => string[];\n\tinvalidate: () => void;\n}\n\ninterface HeightComputers {\n\tglobal: (width: number) => number;\n\tcurrent: (width: number) => number;\n}\n\nfunction previewBodyHeights(pane: PreviewPane): (width: number) => TabBodyHeights {\n\treturn (width) => ({\n\t\tcurrent: pane.naturalHeight(width),\n\t\tmax: pane.maxNaturalHeight(width),\n\t});\n}\n\nfunction multiSelectBodyHeights(view: MultiSelectView): (width: number) => TabBodyHeights {\n\treturn (width) => {\n\t\tconst h = view.naturalHeight(width);\n\t\treturn { current: h, max: h };\n\t};\n}\n\nconst isActiveTab: PerTabSelector<boolean> = (s, ctx) =>\n\tctx.i === selectActivePreviewPaneIndex(s.currentTab, ctx.totalQuestions);\n\n/**\n * Pure factory: assembles every TUI component, the props adapter, and a\n * lifecycle handle. Session-state dependencies arrive via `getCurrentTab` and\n * the `inputBuffer` cell. Initial paint is delegated to\n * `adapter.apply(initialState)` (called by the session at construction-end);\n * no selector is invoked here.\n */\nexport function buildQuestionnaire(config: QuestionnaireBuildConfig): QuestionnaireBuilt {\n\treturn new QuestionnaireBuilder(config).build();\n}\n\n/**\n * Programming-by-intention assembly: each private method names one\n * construction step from `build()`. Read top-down; the class is build-only and\n * discarded once the handle is returned.\n */\nclass QuestionnaireBuilder {\n\tprivate readonly tui: QuestionnaireBuildConfig[\"tui\"];\n\tprivate readonly theme: Theme;\n\tprivate readonly questions: readonly QuestionData[];\n\tprivate readonly itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\tprivate readonly isMulti: boolean;\n\tprivate readonly initialState: QuestionnaireState;\n\tprivate readonly getCurrentTab: () => number;\n\n\tprivate readonly selectTheme: WrappingSelectTheme;\n\tprivate readonly markdownTheme = getMarkdownTheme();\n\tprivate readonly chatRow: ChatRowView;\n\tprivate readonly notesInput = new Input();\n\tprivate readonly inlineInput = new Input();\n\tprivate readonly getTerminalWidth = () => this.tui.terminal.columns;\n\n\tconstructor(config: QuestionnaireBuildConfig) {\n\t\tthis.tui = config.tui;\n\t\tthis.theme = config.theme;\n\t\tthis.questions = config.questions;\n\t\tthis.itemsByTab = config.itemsByTab;\n\t\tthis.isMulti = config.isMulti;\n\t\tthis.initialState = config.initialState;\n\t\tthis.getCurrentTab = config.getCurrentTab;\n\n\t\tthis.selectTheme = this.makeSelectTheme();\n\t\tthis.chatRow = new ChatRowView({\n\t\t\titem: { kind: \"chat\", label: ROW_INTENT_META.chat.label },\n\t\t\ttheme: this.selectTheme,\n\t\t});\n\t}\n\n\tbuild(): QuestionnaireBuilt {\n\t\tconst tabs = this.buildTabComponents();\n\t\tthis.injectGlobalLeftWidth(tabs);\n\t\tconst submitPicker = this.buildSubmitPicker();\n\t\tconst tabBar = this.buildTabBar();\n\t\tconst heights = this.buildHeightComputers(tabs);\n\t\tconst dialog = this.buildDialog(tabs, submitPicker, tabBar, heights);\n\t\tconst globalBindings = this.buildGlobalBindings(dialog, submitPicker, tabBar);\n\t\tconst perTabBindings = this.buildPerTabBindings();\n\t\tconst adapter = this.buildAdapter(tabs, globalBindings, perTabBindings);\n\t\treturn this.handle(adapter, dialog);\n\t}\n\n\tprivate makeSelectTheme(): WrappingSelectTheme {\n\t\tconst t = this.theme;\n\t\treturn {\n\t\t\tselectedText: (s) => t.fg(\"accent\", t.bold(s)),\n\t\t\tdescription: (s) => t.fg(\"muted\", s),\n\t\t\tscrollInfo: (s) => t.fg(\"dim\", s),\n\t\t};\n\t}\n\n\tprivate buildTabComponents(): ReadonlyArray<TabComponents> {\n\t\treturn this.questions.map((q, i) => this.buildTabFor(q, i));\n\t}\n\n\tprivate buildTabFor(question: QuestionData, index: number): TabComponents {\n\t\tconst optionList = new OptionListView({\n\t\t\titems: this.itemsByTab[index] ?? [],\n\t\t\ttheme: this.selectTheme,\n\t\t});\n\t\tconst previewBlock = new PreviewBlockRenderer({\n\t\t\tquestion,\n\t\t\ttheme: this.theme,\n\t\t\tmarkdownTheme: this.markdownTheme,\n\t\t});\n\t\tconst preview = new PreviewPane({\n\t\t\tquestion,\n\t\t\tgetTerminalWidth: this.getTerminalWidth,\n\t\t\toptionListView: optionList,\n\t\t\tpreviewBlock,\n\t\t});\n\t\tconst multiSelect = question.multiSelect ? new MultiSelectView(this.theme, question) : undefined;\n\t\tconst bodyHeights = this.buildBodyHeights(question, preview, multiSelect);\n\t\treturn { optionList, preview, multiSelect, bodyHeights };\n\t}\n\n\tprivate buildBodyHeights(\n\t\tquestion: QuestionData,\n\t\tpreview: PreviewPane,\n\t\tmultiSelect: MultiSelectView | undefined,\n\t): (width: number) => TabBodyHeights {\n\t\treturn question.multiSelect ? multiSelectBodyHeights(multiSelect!) : previewBodyHeights(preview);\n\t}\n\n\t/**\n\t * Compute cross-tab max adaptive left width and inject into each PreviewPane.\n\t * Mirrors buildHeightComputers pattern — iterates all tabs, takes max.\n\t * Called after buildTabComponents, before buildDialog (so setGlobalLeftWidth\n\t * is set before any rendering occurs).\n\t */\n\tprivate injectGlobalLeftWidth(tabs: ReadonlyArray<TabComponents>): void {\n\t\tconst questions = this.questions;\n\t\tconst itemsByTab = this.itemsByTab;\n\t\tconst tabsDescriptor = questions.map((q) => ({ multiSelect: q.multiSelect }));\n\t\tconst globalLeftWidth = (paneWidth: number): number =>\n\t\t\tcrossTabLeftWidthWithDonation(tabsDescriptor, itemsByTab, questions, paneWidth);\n\t\tfor (const tab of tabs) {\n\t\t\ttab.preview.setGlobalLeftWidth(globalLeftWidth);\n\t\t}\n\t}\n\n\tprivate buildSubmitPicker(): SubmitPicker | undefined {\n\t\treturn this.isMulti ? new SubmitPicker(this.theme) : undefined;\n\t}\n\n\tprivate buildTabBar(): TabBar | undefined {\n\t\treturn this.isMulti ? new TabBar(this.theme) : undefined;\n\t}\n\n\tprivate buildHeightComputers(tabs: ReadonlyArray<TabComponents>): HeightComputers {\n\t\tconst global = (width: number): number => {\n\t\t\tlet max = 0;\n\t\t\tfor (const tab of tabs) {\n\t\t\t\tconst h = tab.bodyHeights(width).max;\n\t\t\t\tif (h > max) max = h;\n\t\t\t}\n\t\t\treturn Math.max(1, max);\n\t\t};\n\t\tconst current = (width: number): number => {\n\t\t\tconst idx = Math.min(this.getCurrentTab(), tabs.length - 1);\n\t\t\treturn Math.max(0, tabs[idx]?.bodyHeights(width).current ?? 0);\n\t\t};\n\t\treturn { global, current };\n\t}\n\n\tprivate pickInitialActivePreview(tabs: ReadonlyArray<TabComponents>): StatefulView<PreviewPaneProps> {\n\t\tconst idx = selectActivePreviewPaneIndex(this.initialState.currentTab, this.questions.length);\n\t\treturn tabs[idx]?.preview ?? tabs[0]!.preview;\n\t}\n\n\tprivate buildDialog(\n\t\ttabs: ReadonlyArray<TabComponents>,\n\t\tsubmitPicker: SubmitPicker | undefined,\n\t\ttabBar: TabBar | undefined,\n\t\theights: HeightComputers,\n\t): DialogView {\n\t\treturn new DialogView(\n\t\t\t{\n\t\t\t\ttheme: this.theme,\n\t\t\t\tquestions: this.questions,\n\t\t\t\ttabBar,\n\t\t\t\tnotesInput: this.notesInput,\n\t\t\t\tchatRow: this.chatRow,\n\t\t\t\tisMulti: this.isMulti,\n\t\t\t\ttabsByIndex: tabs,\n\t\t\t\tsubmitPicker,\n\t\t\t\tgetBodyHeight: heights.global,\n\t\t\t\tgetCurrentBodyHeight: heights.current,\n\t\t\t},\n\t\t\t{ state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) },\n\t\t);\n\t}\n\n\tprivate buildGlobalBindings(\n\t\tdialog: DialogView,\n\t\tsubmitPicker: SubmitPicker | undefined,\n\t\ttabBar: TabBar | undefined,\n\t): ReadonlyArray<BoundGlobalBinding> {\n\t\treturn [\n\t\t\tglobalBinding({ component: dialog, select: selectDialogProps }),\n\t\t\tglobalBinding({ component: this.chatRow, select: selectChatRowProps }),\n\t\t\t...(submitPicker ? [globalBinding({ component: submitPicker, select: selectSubmitPickerProps })] : []),\n\t\t\t...(tabBar ? [globalBinding({ component: tabBar, select: selectTabBarProps })] : []),\n\t\t];\n\t}\n\n\tprivate buildPerTabBindings(): ReadonlyArray<BoundPerTabBinding> {\n\t\treturn [\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.optionList,\n\t\t\t\tpredicate: isActiveTab,\n\t\t\t\tselect: selectOptionListProps,\n\t\t\t}),\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.preview,\n\t\t\t\tpredicate: isActiveTab,\n\t\t\t\tselect: selectPreviewPaneProps,\n\t\t\t}),\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.multiSelect,\n\t\t\t\tselect: selectMultiSelectProps,\n\t\t\t}),\n\t\t];\n\t}\n\n\tprivate buildAdapter(\n\t\ttabs: ReadonlyArray<TabComponents>,\n\t\tglobalBindings: ReadonlyArray<BoundGlobalBinding>,\n\t\tperTabBindings: ReadonlyArray<BoundPerTabBinding>,\n\t): QuestionnairePropsAdapter {\n\t\treturn new QuestionnairePropsAdapter({\n\t\t\ttui: this.tui,\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\ttabsByIndex: tabs,\n\t\t\tinlineInput: this.inlineInput,\n\t\t\tglobalBindings,\n\t\t\tperTabBindings,\n\t\t\textraInvalidatables: [this.notesInput],\n\t\t});\n\t}\n\n\tprivate handle(adapter: QuestionnairePropsAdapter, dialog: DialogView): QuestionnaireBuilt {\n\t\treturn {\n\t\t\tadapter,\n\t\t\tnotesInput: this.notesInput,\n\t\t\tinlineInput: this.inlineInput,\n\t\t\trender: (w) => dialog.render(w),\n\t\t\tinvalidate: () => adapter.invalidate(),\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"build-questionnaire.js","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/build-questionnaire.ts"],"names":[],"mappings":"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","sourcesContent":["import { Input } from \"@earendil-works/pi-tui\";\nimport { getMarkdownTheme, type Theme } from \"../../../../modes/interactive/theme/theme.ts\";\nimport type { QuestionData } from \"../tool/types.ts\";\nimport {\n\ttype BoundGlobalBinding,\n\ttype BoundPerTabBinding,\n\tglobalBinding,\n\tperTabBinding,\n} from \"../view/component-binding.ts\";\nimport { ChatRowView } from \"../view/components/chat-row-view.ts\";\nimport { MultiSelectView } from \"../view/components/multi-select-view.ts\";\nimport { OptionListView } from \"../view/components/option-list-view.ts\";\nimport { PreviewBlockRenderer } from \"../view/components/preview/preview-block-renderer.ts\";\nimport { crossTabLeftWidthWithDonation } from \"../view/components/preview/preview-layout-decider.ts\";\nimport type { PreviewPaneProps } from \"../view/components/preview/preview-pane.ts\";\nimport { PreviewPane } from \"../view/components/preview/preview-pane.ts\";\nimport { SubmitPicker } from \"../view/components/submit-picker.ts\";\nimport { TabBar } from \"../view/components/tab-bar.ts\";\nimport type { WrappingSelectItem, WrappingSelectTheme } from \"../view/components/wrapping-select.ts\";\nimport { DialogView } from \"../view/dialog-builder.ts\";\nimport { QuestionnairePropsAdapter } from \"../view/props-adapter.ts\";\nimport type { StatefulView } from \"../view/stateful-view.ts\";\nimport type { TabBodyHeights, TabComponents } from \"../view/tab-components.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport type { PerTabSelector } from \"./selectors/contract.ts\";\nimport { selectActivePreviewPaneIndex } from \"./selectors/derivations.ts\";\nimport {\n\tselectChatRowProps,\n\tselectDialogProps,\n\tselectMultiSelectProps,\n\tselectOptionListProps,\n\tselectPreviewPaneProps,\n\tselectSubmitPickerProps,\n\tselectTabBarProps,\n} from \"./selectors/projections.ts\";\nimport type { QuestionnaireState } from \"./state.ts\";\n\nexport interface QuestionnaireBuildConfig {\n\ttui: { terminal: { columns: number }; requestRender(): void };\n\ttheme: Theme;\n\tquestions: readonly QuestionData[];\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\tisMulti: boolean;\n\tinitialState: QuestionnaireState;\n\tgetCurrentTab: () => number;\n\tchatAsOption?: boolean;\n}\n\nexport interface QuestionnaireBuilt {\n\tadapter: QuestionnairePropsAdapter;\n\tnotesInput: Input;\n\tinlineInput: Input;\n\trender: (width: number) => string[];\n\tinvalidate: () => void;\n}\n\ninterface HeightComputers {\n\tglobal: (width: number) => number;\n\tcurrent: (width: number) => number;\n}\n\nfunction previewBodyHeights(pane: PreviewPane): (width: number) => TabBodyHeights {\n\treturn (width) => ({\n\t\tcurrent: pane.naturalHeight(width),\n\t\tmax: pane.maxNaturalHeight(width),\n\t});\n}\n\nfunction multiSelectBodyHeights(view: MultiSelectView): (width: number) => TabBodyHeights {\n\treturn (width) => {\n\t\tconst h = view.naturalHeight(width);\n\t\treturn { current: h, max: h };\n\t};\n}\n\nconst isActiveTab: PerTabSelector<boolean> = (s, ctx) =>\n\tctx.i === selectActivePreviewPaneIndex(s.currentTab, ctx.totalQuestions);\n\n/**\n * Pure factory: assembles every TUI component, the props adapter, and a\n * lifecycle handle. Session-state dependencies arrive via `getCurrentTab` and\n * the `inputBuffer` cell. Initial paint is delegated to\n * `adapter.apply(initialState)` (called by the session at construction-end);\n * no selector is invoked here.\n */\nexport function buildQuestionnaire(config: QuestionnaireBuildConfig): QuestionnaireBuilt {\n\treturn new QuestionnaireBuilder(config).build();\n}\n\n/**\n * Programming-by-intention assembly: each private method names one\n * construction step from `build()`. Read top-down; the class is build-only and\n * discarded once the handle is returned.\n */\nclass QuestionnaireBuilder {\n\tprivate readonly tui: QuestionnaireBuildConfig[\"tui\"];\n\tprivate readonly theme: Theme;\n\tprivate readonly questions: readonly QuestionData[];\n\tprivate readonly itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\tprivate readonly isMulti: boolean;\n\tprivate readonly initialState: QuestionnaireState;\n\tprivate readonly getCurrentTab: () => number;\n\n\tprivate readonly selectTheme: WrappingSelectTheme;\n\tprivate readonly markdownTheme = getMarkdownTheme();\n\tprivate readonly chatRow: ChatRowView;\n\tprivate readonly notesInput = new Input();\n\tprivate readonly inlineInput = new Input();\n\tprivate readonly getTerminalWidth = () => this.tui.terminal.columns;\n\n\tconstructor(config: QuestionnaireBuildConfig) {\n\t\tthis.tui = config.tui;\n\t\tthis.theme = config.theme;\n\t\tthis.questions = config.questions;\n\t\tthis.itemsByTab = config.itemsByTab;\n\t\tthis.isMulti = config.isMulti;\n\t\tthis.initialState = config.initialState;\n\t\tthis.getCurrentTab = config.getCurrentTab;\n\n\t\tthis.selectTheme = this.makeSelectTheme();\n\t\tthis.chatRow = new ChatRowView({\n\t\t\titem: { kind: \"chat\", label: ROW_INTENT_META.chat.label },\n\t\t\ttheme: this.selectTheme,\n\t\t\tinlineInputEnabled: config.chatAsOption !== true,\n\t\t});\n\t}\n\n\tbuild(): QuestionnaireBuilt {\n\t\tconst tabs = this.buildTabComponents();\n\t\tthis.injectGlobalLeftWidth(tabs);\n\t\tconst submitPicker = this.buildSubmitPicker();\n\t\tconst tabBar = this.buildTabBar();\n\t\tconst heights = this.buildHeightComputers(tabs);\n\t\tconst dialog = this.buildDialog(tabs, submitPicker, tabBar, heights);\n\t\tconst globalBindings = this.buildGlobalBindings(dialog, submitPicker, tabBar);\n\t\tconst perTabBindings = this.buildPerTabBindings();\n\t\tconst adapter = this.buildAdapter(tabs, globalBindings, perTabBindings);\n\t\treturn this.handle(adapter, dialog);\n\t}\n\n\tprivate makeSelectTheme(): WrappingSelectTheme {\n\t\tconst t = this.theme;\n\t\treturn {\n\t\t\tselectedText: (s) => t.fg(\"accent\", t.bold(s)),\n\t\t\tdescription: (s) => t.fg(\"muted\", s),\n\t\t\tscrollInfo: (s) => t.fg(\"dim\", s),\n\t\t};\n\t}\n\n\tprivate buildTabComponents(): ReadonlyArray<TabComponents> {\n\t\treturn this.questions.map((q, i) => this.buildTabFor(q, i));\n\t}\n\n\tprivate buildTabFor(question: QuestionData, index: number): TabComponents {\n\t\tconst optionList = new OptionListView({\n\t\t\titems: this.itemsByTab[index] ?? [],\n\t\t\ttheme: this.selectTheme,\n\t\t});\n\t\tconst previewBlock = new PreviewBlockRenderer({\n\t\t\tquestion,\n\t\t\ttheme: this.theme,\n\t\t\tmarkdownTheme: this.markdownTheme,\n\t\t});\n\t\tconst preview = new PreviewPane({\n\t\t\tquestion,\n\t\t\tgetTerminalWidth: this.getTerminalWidth,\n\t\t\toptionListView: optionList,\n\t\t\tpreviewBlock,\n\t\t});\n\t\tconst multiSelect = question.multiSelect ? new MultiSelectView(this.theme, question) : undefined;\n\t\tconst bodyHeights = this.buildBodyHeights(question, preview, multiSelect);\n\t\treturn { optionList, preview, multiSelect, bodyHeights };\n\t}\n\n\tprivate buildBodyHeights(\n\t\tquestion: QuestionData,\n\t\tpreview: PreviewPane,\n\t\tmultiSelect: MultiSelectView | undefined,\n\t): (width: number) => TabBodyHeights {\n\t\treturn question.multiSelect ? multiSelectBodyHeights(multiSelect!) : previewBodyHeights(preview);\n\t}\n\n\t/**\n\t * Compute cross-tab max adaptive left width and inject into each PreviewPane.\n\t * Mirrors buildHeightComputers pattern — iterates all tabs, takes max.\n\t * Called after buildTabComponents, before buildDialog (so setGlobalLeftWidth\n\t * is set before any rendering occurs).\n\t */\n\tprivate injectGlobalLeftWidth(tabs: ReadonlyArray<TabComponents>): void {\n\t\tconst questions = this.questions;\n\t\tconst itemsByTab = this.itemsByTab;\n\t\tconst tabsDescriptor = questions.map((q) => ({ multiSelect: q.multiSelect }));\n\t\tconst globalLeftWidth = (paneWidth: number): number =>\n\t\t\tcrossTabLeftWidthWithDonation(tabsDescriptor, itemsByTab, questions, paneWidth);\n\t\tfor (const tab of tabs) {\n\t\t\ttab.preview.setGlobalLeftWidth(globalLeftWidth);\n\t\t}\n\t}\n\n\tprivate buildSubmitPicker(): SubmitPicker | undefined {\n\t\treturn this.isMulti ? new SubmitPicker(this.theme) : undefined;\n\t}\n\n\tprivate buildTabBar(): TabBar | undefined {\n\t\treturn this.isMulti ? new TabBar(this.theme) : undefined;\n\t}\n\n\tprivate buildHeightComputers(tabs: ReadonlyArray<TabComponents>): HeightComputers {\n\t\tconst global = (width: number): number => {\n\t\t\tlet max = 0;\n\t\t\tfor (const tab of tabs) {\n\t\t\t\tconst h = tab.bodyHeights(width).max;\n\t\t\t\tif (h > max) max = h;\n\t\t\t}\n\t\t\treturn Math.max(1, max);\n\t\t};\n\t\tconst current = (width: number): number => {\n\t\t\tconst idx = Math.min(this.getCurrentTab(), tabs.length - 1);\n\t\t\treturn Math.max(0, tabs[idx]?.bodyHeights(width).current ?? 0);\n\t\t};\n\t\treturn { global, current };\n\t}\n\n\tprivate pickInitialActivePreview(tabs: ReadonlyArray<TabComponents>): StatefulView<PreviewPaneProps> {\n\t\tconst idx = selectActivePreviewPaneIndex(this.initialState.currentTab, this.questions.length);\n\t\treturn tabs[idx]?.preview ?? tabs[0]!.preview;\n\t}\n\n\tprivate buildDialog(\n\t\ttabs: ReadonlyArray<TabComponents>,\n\t\tsubmitPicker: SubmitPicker | undefined,\n\t\ttabBar: TabBar | undefined,\n\t\theights: HeightComputers,\n\t): DialogView {\n\t\treturn new DialogView(\n\t\t\t{\n\t\t\t\ttheme: this.theme,\n\t\t\t\tquestions: this.questions,\n\t\t\t\ttabBar,\n\t\t\t\tnotesInput: this.notesInput,\n\t\t\t\tchatRow: this.chatRow,\n\t\t\t\tisMulti: this.isMulti,\n\t\t\t\ttabsByIndex: tabs,\n\t\t\t\tsubmitPicker,\n\t\t\t\tgetBodyHeight: heights.global,\n\t\t\t\tgetCurrentBodyHeight: heights.current,\n\t\t\t},\n\t\t\t{ state: this.initialState, activePreviewPane: this.pickInitialActivePreview(tabs) },\n\t\t);\n\t}\n\n\tprivate buildGlobalBindings(\n\t\tdialog: DialogView,\n\t\tsubmitPicker: SubmitPicker | undefined,\n\t\ttabBar: TabBar | undefined,\n\t): ReadonlyArray<BoundGlobalBinding> {\n\t\treturn [\n\t\t\tglobalBinding({ component: dialog, select: selectDialogProps }),\n\t\t\tglobalBinding({ component: this.chatRow, select: selectChatRowProps }),\n\t\t\t...(submitPicker ? [globalBinding({ component: submitPicker, select: selectSubmitPickerProps })] : []),\n\t\t\t...(tabBar ? [globalBinding({ component: tabBar, select: selectTabBarProps })] : []),\n\t\t];\n\t}\n\n\tprivate buildPerTabBindings(): ReadonlyArray<BoundPerTabBinding> {\n\t\treturn [\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.optionList,\n\t\t\t\tpredicate: isActiveTab,\n\t\t\t\tselect: selectOptionListProps,\n\t\t\t}),\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.preview,\n\t\t\t\tpredicate: isActiveTab,\n\t\t\t\tselect: selectPreviewPaneProps,\n\t\t\t}),\n\t\t\tperTabBinding({\n\t\t\t\tresolve: (tab) => tab.multiSelect,\n\t\t\t\tselect: selectMultiSelectProps,\n\t\t\t}),\n\t\t];\n\t}\n\n\tprivate buildAdapter(\n\t\ttabs: ReadonlyArray<TabComponents>,\n\t\tglobalBindings: ReadonlyArray<BoundGlobalBinding>,\n\t\tperTabBindings: ReadonlyArray<BoundPerTabBinding>,\n\t): QuestionnairePropsAdapter {\n\t\treturn new QuestionnairePropsAdapter({\n\t\t\ttui: this.tui,\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\ttabsByIndex: tabs,\n\t\t\tinlineInput: this.inlineInput,\n\t\t\tglobalBindings,\n\t\t\tperTabBindings,\n\t\t\textraInvalidatables: [this.notesInput],\n\t\t});\n\t}\n\n\tprivate handle(adapter: QuestionnairePropsAdapter, dialog: DialogView): QuestionnaireBuilt {\n\t\treturn {\n\t\t\tadapter,\n\t\t\tnotesInput: this.notesInput,\n\t\t\tinlineInput: this.inlineInput,\n\t\t\trender: (w) => dialog.render(w),\n\t\t\tinvalidate: () => adapter.invalidate(),\n\t\t};\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"key-router.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/key-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAU3E,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE;AACxB;;;GAGG;GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAM7F;AAgHD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAsIpH"}
1
+ {"version":3,"file":"key-router.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/key-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAU3E,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE;AACxB;;;GAGG;GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAM7F;AAiHD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAsIpH"}
@@ -62,11 +62,13 @@ function buildSingleSelectAnswer(state, runtime) {
62
62
  }
63
63
  if (state.inputMode && state.inlineInputOwner === "other") {
64
64
  const label = runtime.inputBuffer;
65
+ if (label.trim().length === 0)
66
+ return null;
65
67
  return {
66
68
  questionIndex: state.currentTab,
67
69
  question: q.question,
68
70
  kind: "custom",
69
- answer: label.length > 0 ? label : null,
71
+ answer: label,
70
72
  };
71
73
  }
72
74
  if (!item)