@qxbyte/muse 0.1.2 → 0.1.3

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/dist/cli.js CHANGED
@@ -5,14 +5,24 @@ import { Command } from "commander";
5
5
  import { render } from "ink";
6
6
 
7
7
  // src/app.tsx
8
- import { useCallback, useEffect as useEffect5, useMemo as useMemo2, useReducer, useRef, useState as useState7 } from "react";
9
- import { Box as Box12, Text as Text14, useApp, useInput as useInput4, useStdout } from "ink";
8
+ import { useCallback, useEffect as useEffect5, useMemo as useMemo2, useReducer, useRef, useState as useState8 } from "react";
9
+ import { Box as Box13, Text as Text15, useApp, useInput as useInput5, useStdout as useStdout2 } from "ink";
10
10
 
11
11
  // src/components/BgTextInput.tsx
12
12
  import { useState, useEffect } from "react";
13
13
  import { Text, useInput } from "ink";
14
14
  import { jsx, jsxs } from "react/jsx-runtime";
15
15
  var BLINK_MS = 530;
16
+ var PASTE_CHAR_THRESHOLD = 200;
17
+ function looksLikePaste(input) {
18
+ return input.includes("\n") || input.includes("\r") || input.length > PASTE_CHAR_THRESHOLD;
19
+ }
20
+ function stripBracketedPaste(s) {
21
+ return s.replace(/\x1b\[20[01]~/g, "");
22
+ }
23
+ function normalizeLineEndings(s) {
24
+ return s.replace(/\r\n?/g, "\n");
25
+ }
16
26
  function BgTextInput({
17
27
  value,
18
28
  onChange,
@@ -20,7 +30,9 @@ function BgTextInput({
20
30
  width,
21
31
  backgroundColor,
22
32
  color,
23
- isActive = true
33
+ isActive = true,
34
+ onPaste,
35
+ placeholder
24
36
  }) {
25
37
  const [cursor, setCursor] = useState(value.length);
26
38
  const [blinkOn, setBlinkOn] = useState(true);
@@ -66,17 +78,35 @@ function BgTextInput({
66
78
  return;
67
79
  }
68
80
  if (input && !key.return) {
69
- const next = value.slice(0, cursor) + input + value.slice(cursor);
81
+ const cleaned = normalizeLineEndings(stripBracketedPaste(input));
82
+ if (!cleaned) return;
83
+ const insertion = onPaste && looksLikePaste(cleaned) ? onPaste(cleaned) : cleaned;
84
+ const next = value.slice(0, cursor) + insertion + value.slice(cursor);
70
85
  onChange(next);
71
- setCursor((c) => c + input.length);
86
+ setCursor((c) => c + insertion.length);
72
87
  }
73
88
  },
74
89
  { isActive }
75
90
  );
76
- const view = computeViewport(value, cursor, width);
91
+ const showCursor = isActive && blinkOn;
92
+ if (value.length === 0 && placeholder) {
93
+ const maxW = Math.max(0, width - 1);
94
+ let truncated = placeholder;
95
+ while (stringWidth(truncated) > maxW && truncated.length > 0) {
96
+ truncated = truncated.slice(0, -1);
97
+ }
98
+ const usedW = 1 + stringWidth(truncated);
99
+ const padLen2 = Math.max(0, width - usedW);
100
+ return /* @__PURE__ */ jsxs(Text, { backgroundColor, color, children: [
101
+ showCursor ? /* @__PURE__ */ jsx(Text, { backgroundColor: "blue", color, dimColor: true, children: " " }) : /* @__PURE__ */ jsx(Text, { backgroundColor, color, children: " " }),
102
+ /* @__PURE__ */ jsx(Text, { backgroundColor, dimColor: true, children: truncated }),
103
+ " ".repeat(padLen2)
104
+ ] });
105
+ }
106
+ const displayValue = value.replace(/[\n\r]/g, "\u21B5");
107
+ const view = computeViewport(displayValue, cursor, width);
77
108
  const at = view.atChar;
78
109
  const padLen = Math.max(0, width - view.consumedWidth);
79
- const showCursor = isActive && blinkOn;
80
110
  return /* @__PURE__ */ jsxs(Text, { backgroundColor, color, children: [
81
111
  view.before,
82
112
  showCursor ? /* @__PURE__ */ jsx(Text, { backgroundColor: "blue", color, dimColor: true, children: at }) : /* @__PURE__ */ jsx(Text, { backgroundColor, color, children: at }),
@@ -242,37 +272,533 @@ function pickBanner(width, props) {
242
272
 
243
273
  // src/components/MessageView.tsx
244
274
  import { useMemo } from "react";
245
- import { Box as Box2, Text as Text3 } from "ink";
275
+ import { Box as Box2, Text as Text3, useStdout } from "ink";
276
+
277
+ // node_modules/chalk/source/vendor/ansi-styles/index.js
278
+ var ANSI_BACKGROUND_OFFSET = 10;
279
+ var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
280
+ var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
281
+ var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
282
+ var styles = {
283
+ modifier: {
284
+ reset: [0, 0],
285
+ // 21 isn't widely supported and 22 does the same thing
286
+ bold: [1, 22],
287
+ dim: [2, 22],
288
+ italic: [3, 23],
289
+ underline: [4, 24],
290
+ overline: [53, 55],
291
+ inverse: [7, 27],
292
+ hidden: [8, 28],
293
+ strikethrough: [9, 29]
294
+ },
295
+ color: {
296
+ black: [30, 39],
297
+ red: [31, 39],
298
+ green: [32, 39],
299
+ yellow: [33, 39],
300
+ blue: [34, 39],
301
+ magenta: [35, 39],
302
+ cyan: [36, 39],
303
+ white: [37, 39],
304
+ // Bright color
305
+ blackBright: [90, 39],
306
+ gray: [90, 39],
307
+ // Alias of `blackBright`
308
+ grey: [90, 39],
309
+ // Alias of `blackBright`
310
+ redBright: [91, 39],
311
+ greenBright: [92, 39],
312
+ yellowBright: [93, 39],
313
+ blueBright: [94, 39],
314
+ magentaBright: [95, 39],
315
+ cyanBright: [96, 39],
316
+ whiteBright: [97, 39]
317
+ },
318
+ bgColor: {
319
+ bgBlack: [40, 49],
320
+ bgRed: [41, 49],
321
+ bgGreen: [42, 49],
322
+ bgYellow: [43, 49],
323
+ bgBlue: [44, 49],
324
+ bgMagenta: [45, 49],
325
+ bgCyan: [46, 49],
326
+ bgWhite: [47, 49],
327
+ // Bright color
328
+ bgBlackBright: [100, 49],
329
+ bgGray: [100, 49],
330
+ // Alias of `bgBlackBright`
331
+ bgGrey: [100, 49],
332
+ // Alias of `bgBlackBright`
333
+ bgRedBright: [101, 49],
334
+ bgGreenBright: [102, 49],
335
+ bgYellowBright: [103, 49],
336
+ bgBlueBright: [104, 49],
337
+ bgMagentaBright: [105, 49],
338
+ bgCyanBright: [106, 49],
339
+ bgWhiteBright: [107, 49]
340
+ }
341
+ };
342
+ var modifierNames = Object.keys(styles.modifier);
343
+ var foregroundColorNames = Object.keys(styles.color);
344
+ var backgroundColorNames = Object.keys(styles.bgColor);
345
+ var colorNames = [...foregroundColorNames, ...backgroundColorNames];
346
+ function assembleStyles() {
347
+ const codes = /* @__PURE__ */ new Map();
348
+ for (const [groupName, group] of Object.entries(styles)) {
349
+ for (const [styleName, style] of Object.entries(group)) {
350
+ styles[styleName] = {
351
+ open: `\x1B[${style[0]}m`,
352
+ close: `\x1B[${style[1]}m`
353
+ };
354
+ group[styleName] = styles[styleName];
355
+ codes.set(style[0], style[1]);
356
+ }
357
+ Object.defineProperty(styles, groupName, {
358
+ value: group,
359
+ enumerable: false
360
+ });
361
+ }
362
+ Object.defineProperty(styles, "codes", {
363
+ value: codes,
364
+ enumerable: false
365
+ });
366
+ styles.color.close = "\x1B[39m";
367
+ styles.bgColor.close = "\x1B[49m";
368
+ styles.color.ansi = wrapAnsi16();
369
+ styles.color.ansi256 = wrapAnsi256();
370
+ styles.color.ansi16m = wrapAnsi16m();
371
+ styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
372
+ styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
373
+ styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
374
+ Object.defineProperties(styles, {
375
+ rgbToAnsi256: {
376
+ value(red, green, blue) {
377
+ if (red === green && green === blue) {
378
+ if (red < 8) {
379
+ return 16;
380
+ }
381
+ if (red > 248) {
382
+ return 231;
383
+ }
384
+ return Math.round((red - 8) / 247 * 24) + 232;
385
+ }
386
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
387
+ },
388
+ enumerable: false
389
+ },
390
+ hexToRgb: {
391
+ value(hex) {
392
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
393
+ if (!matches) {
394
+ return [0, 0, 0];
395
+ }
396
+ let [colorString] = matches;
397
+ if (colorString.length === 3) {
398
+ colorString = [...colorString].map((character) => character + character).join("");
399
+ }
400
+ const integer = Number.parseInt(colorString, 16);
401
+ return [
402
+ /* eslint-disable no-bitwise */
403
+ integer >> 16 & 255,
404
+ integer >> 8 & 255,
405
+ integer & 255
406
+ /* eslint-enable no-bitwise */
407
+ ];
408
+ },
409
+ enumerable: false
410
+ },
411
+ hexToAnsi256: {
412
+ value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
413
+ enumerable: false
414
+ },
415
+ ansi256ToAnsi: {
416
+ value(code) {
417
+ if (code < 8) {
418
+ return 30 + code;
419
+ }
420
+ if (code < 16) {
421
+ return 90 + (code - 8);
422
+ }
423
+ let red;
424
+ let green;
425
+ let blue;
426
+ if (code >= 232) {
427
+ red = ((code - 232) * 10 + 8) / 255;
428
+ green = red;
429
+ blue = red;
430
+ } else {
431
+ code -= 16;
432
+ const remainder = code % 36;
433
+ red = Math.floor(code / 36) / 5;
434
+ green = Math.floor(remainder / 6) / 5;
435
+ blue = remainder % 6 / 5;
436
+ }
437
+ const value = Math.max(red, green, blue) * 2;
438
+ if (value === 0) {
439
+ return 30;
440
+ }
441
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
442
+ if (value === 2) {
443
+ result += 60;
444
+ }
445
+ return result;
446
+ },
447
+ enumerable: false
448
+ },
449
+ rgbToAnsi: {
450
+ value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
451
+ enumerable: false
452
+ },
453
+ hexToAnsi: {
454
+ value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
455
+ enumerable: false
456
+ }
457
+ });
458
+ return styles;
459
+ }
460
+ var ansiStyles = assembleStyles();
461
+ var ansi_styles_default = ansiStyles;
462
+
463
+ // node_modules/chalk/source/vendor/supports-color/index.js
464
+ import process2 from "process";
465
+ import os from "os";
466
+ import tty from "tty";
467
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
468
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
469
+ const position = argv.indexOf(prefix + flag);
470
+ const terminatorPosition = argv.indexOf("--");
471
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
472
+ }
473
+ var { env } = process2;
474
+ var flagForceColor;
475
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
476
+ flagForceColor = 0;
477
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
478
+ flagForceColor = 1;
479
+ }
480
+ function envForceColor() {
481
+ if ("FORCE_COLOR" in env) {
482
+ if (env.FORCE_COLOR === "true") {
483
+ return 1;
484
+ }
485
+ if (env.FORCE_COLOR === "false") {
486
+ return 0;
487
+ }
488
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
489
+ }
490
+ }
491
+ function translateLevel(level) {
492
+ if (level === 0) {
493
+ return false;
494
+ }
495
+ return {
496
+ level,
497
+ hasBasic: true,
498
+ has256: level >= 2,
499
+ has16m: level >= 3
500
+ };
501
+ }
502
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
503
+ const noFlagForceColor = envForceColor();
504
+ if (noFlagForceColor !== void 0) {
505
+ flagForceColor = noFlagForceColor;
506
+ }
507
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
508
+ if (forceColor === 0) {
509
+ return 0;
510
+ }
511
+ if (sniffFlags) {
512
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
513
+ return 3;
514
+ }
515
+ if (hasFlag("color=256")) {
516
+ return 2;
517
+ }
518
+ }
519
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
520
+ return 1;
521
+ }
522
+ if (haveStream && !streamIsTTY && forceColor === void 0) {
523
+ return 0;
524
+ }
525
+ const min = forceColor || 0;
526
+ if (env.TERM === "dumb") {
527
+ return min;
528
+ }
529
+ if (process2.platform === "win32") {
530
+ const osRelease = os.release().split(".");
531
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
532
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
533
+ }
534
+ return 1;
535
+ }
536
+ if ("CI" in env) {
537
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
538
+ return 3;
539
+ }
540
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
541
+ return 1;
542
+ }
543
+ return min;
544
+ }
545
+ if ("TEAMCITY_VERSION" in env) {
546
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
547
+ }
548
+ if (env.COLORTERM === "truecolor") {
549
+ return 3;
550
+ }
551
+ if (env.TERM === "xterm-kitty") {
552
+ return 3;
553
+ }
554
+ if (env.TERM === "xterm-ghostty") {
555
+ return 3;
556
+ }
557
+ if (env.TERM === "wezterm") {
558
+ return 3;
559
+ }
560
+ if ("TERM_PROGRAM" in env) {
561
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
562
+ switch (env.TERM_PROGRAM) {
563
+ case "iTerm.app": {
564
+ return version >= 3 ? 3 : 2;
565
+ }
566
+ case "Apple_Terminal": {
567
+ return 2;
568
+ }
569
+ }
570
+ }
571
+ if (/-256(color)?$/i.test(env.TERM)) {
572
+ return 2;
573
+ }
574
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
575
+ return 1;
576
+ }
577
+ if ("COLORTERM" in env) {
578
+ return 1;
579
+ }
580
+ return min;
581
+ }
582
+ function createSupportsColor(stream, options = {}) {
583
+ const level = _supportsColor(stream, {
584
+ streamIsTTY: stream && stream.isTTY,
585
+ ...options
586
+ });
587
+ return translateLevel(level);
588
+ }
589
+ var supportsColor = {
590
+ stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
591
+ stderr: createSupportsColor({ isTTY: tty.isatty(2) })
592
+ };
593
+ var supports_color_default = supportsColor;
594
+
595
+ // node_modules/chalk/source/utilities.js
596
+ function stringReplaceAll(string, substring, replacer) {
597
+ let index = string.indexOf(substring);
598
+ if (index === -1) {
599
+ return string;
600
+ }
601
+ const substringLength = substring.length;
602
+ let endIndex = 0;
603
+ let returnValue = "";
604
+ do {
605
+ returnValue += string.slice(endIndex, index) + substring + replacer;
606
+ endIndex = index + substringLength;
607
+ index = string.indexOf(substring, endIndex);
608
+ } while (index !== -1);
609
+ returnValue += string.slice(endIndex);
610
+ return returnValue;
611
+ }
612
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
613
+ let endIndex = 0;
614
+ let returnValue = "";
615
+ do {
616
+ const gotCR = string[index - 1] === "\r";
617
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
618
+ endIndex = index + 1;
619
+ index = string.indexOf("\n", endIndex);
620
+ } while (index !== -1);
621
+ returnValue += string.slice(endIndex);
622
+ return returnValue;
623
+ }
624
+
625
+ // node_modules/chalk/source/index.js
626
+ var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
627
+ var GENERATOR = /* @__PURE__ */ Symbol("GENERATOR");
628
+ var STYLER = /* @__PURE__ */ Symbol("STYLER");
629
+ var IS_EMPTY = /* @__PURE__ */ Symbol("IS_EMPTY");
630
+ var levelMapping = [
631
+ "ansi",
632
+ "ansi",
633
+ "ansi256",
634
+ "ansi16m"
635
+ ];
636
+ var styles2 = /* @__PURE__ */ Object.create(null);
637
+ var applyOptions = (object, options = {}) => {
638
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
639
+ throw new Error("The `level` option should be an integer from 0 to 3");
640
+ }
641
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
642
+ object.level = options.level === void 0 ? colorLevel : options.level;
643
+ };
644
+ var chalkFactory = (options) => {
645
+ const chalk2 = (...strings) => strings.join(" ");
646
+ applyOptions(chalk2, options);
647
+ Object.setPrototypeOf(chalk2, createChalk.prototype);
648
+ return chalk2;
649
+ };
650
+ function createChalk(options) {
651
+ return chalkFactory(options);
652
+ }
653
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
654
+ for (const [styleName, style] of Object.entries(ansi_styles_default)) {
655
+ styles2[styleName] = {
656
+ get() {
657
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
658
+ Object.defineProperty(this, styleName, { value: builder });
659
+ return builder;
660
+ }
661
+ };
662
+ }
663
+ styles2.visible = {
664
+ get() {
665
+ const builder = createBuilder(this, this[STYLER], true);
666
+ Object.defineProperty(this, "visible", { value: builder });
667
+ return builder;
668
+ }
669
+ };
670
+ var getModelAnsi = (model, level, type, ...arguments_) => {
671
+ if (model === "rgb") {
672
+ if (level === "ansi16m") {
673
+ return ansi_styles_default[type].ansi16m(...arguments_);
674
+ }
675
+ if (level === "ansi256") {
676
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
677
+ }
678
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
679
+ }
680
+ if (model === "hex") {
681
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
682
+ }
683
+ return ansi_styles_default[type][model](...arguments_);
684
+ };
685
+ var usedModels = ["rgb", "hex", "ansi256"];
686
+ for (const model of usedModels) {
687
+ styles2[model] = {
688
+ get() {
689
+ const { level } = this;
690
+ return function(...arguments_) {
691
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
692
+ return createBuilder(this, styler, this[IS_EMPTY]);
693
+ };
694
+ }
695
+ };
696
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
697
+ styles2[bgModel] = {
698
+ get() {
699
+ const { level } = this;
700
+ return function(...arguments_) {
701
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
702
+ return createBuilder(this, styler, this[IS_EMPTY]);
703
+ };
704
+ }
705
+ };
706
+ }
707
+ var proto = Object.defineProperties(() => {
708
+ }, {
709
+ ...styles2,
710
+ level: {
711
+ enumerable: true,
712
+ get() {
713
+ return this[GENERATOR].level;
714
+ },
715
+ set(level) {
716
+ this[GENERATOR].level = level;
717
+ }
718
+ }
719
+ });
720
+ var createStyler = (open, close, parent) => {
721
+ let openAll;
722
+ let closeAll;
723
+ if (parent === void 0) {
724
+ openAll = open;
725
+ closeAll = close;
726
+ } else {
727
+ openAll = parent.openAll + open;
728
+ closeAll = close + parent.closeAll;
729
+ }
730
+ return {
731
+ open,
732
+ close,
733
+ openAll,
734
+ closeAll,
735
+ parent
736
+ };
737
+ };
738
+ var createBuilder = (self, _styler, _isEmpty) => {
739
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
740
+ Object.setPrototypeOf(builder, proto);
741
+ builder[GENERATOR] = self;
742
+ builder[STYLER] = _styler;
743
+ builder[IS_EMPTY] = _isEmpty;
744
+ return builder;
745
+ };
746
+ var applyStyle = (self, string) => {
747
+ if (self.level <= 0 || !string) {
748
+ return self[IS_EMPTY] ? "" : string;
749
+ }
750
+ let styler = self[STYLER];
751
+ if (styler === void 0) {
752
+ return string;
753
+ }
754
+ const { openAll, closeAll } = styler;
755
+ if (string.includes("\x1B")) {
756
+ while (styler !== void 0) {
757
+ string = stringReplaceAll(string, styler.close, styler.open);
758
+ styler = styler.parent;
759
+ }
760
+ }
761
+ const lfIndex = string.indexOf("\n");
762
+ if (lfIndex !== -1) {
763
+ string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
764
+ }
765
+ return openAll + string + closeAll;
766
+ };
767
+ Object.defineProperties(createChalk.prototype, styles2);
768
+ var chalk = createChalk();
769
+ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
770
+ var source_default = chalk;
771
+
772
+ // src/components/MessageView.tsx
246
773
  import { marked } from "marked";
247
774
  import { markedTerminal } from "marked-terminal";
248
775
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
776
+ if (source_default.level === 0) source_default.level = 3;
249
777
  marked.use(markedTerminal());
250
778
  function renderMarkdown(text) {
251
779
  try {
252
- const out = marked.parse(text);
253
- return out.replace(/\n+$/, "");
780
+ let out = marked.parse(text);
781
+ out = out.replace(/\n+$/, "");
782
+ out = out.replace(/\*\*([^\n*]+?)\*\*/g, (_, body) => `\x1B[1m${body}\x1B[22m`);
783
+ out = out.replace(/(?<![*\\\x1b])\*([^\n*]+?)\*(?!\*)/g, (_, body) => `\x1B[3m${body}\x1B[23m`);
784
+ out = out.replace(/\x1b\[0m/g, "");
785
+ return out;
254
786
  } catch {
255
787
  return text;
256
788
  }
257
789
  }
258
- function MessageView({ message }) {
790
+ function MessageView({
791
+ message,
792
+ resultsByCallId
793
+ }) {
259
794
  switch (message.role) {
260
795
  case "user":
261
796
  return /* @__PURE__ */ jsx3(UserMessage, { content: typeof message.content === "string" ? message.content : flattenText(message.content) });
262
797
  case "assistant":
263
- return /* @__PURE__ */ jsx3(AssistantMessage, { content: message.content });
798
+ return /* @__PURE__ */ jsx3(AssistantMessage, { content: message.content, resultsByCallId });
264
799
  case "tool":
265
800
  if (message.toolName === "TodoWrite") return null;
266
- return /* @__PURE__ */ jsx3(
267
- ToolResultLine,
268
- {
269
- isError: message.isError ?? false,
270
- content: message.content,
271
- diff: message.diff,
272
- summary: message.summary,
273
- kind: message.kind
274
- }
275
- );
801
+ return /* @__PURE__ */ jsx3(ToolResultTree, { result: message, standalone: true });
276
802
  case "system":
277
803
  return null;
278
804
  }
@@ -281,13 +807,38 @@ function flattenText(parts) {
281
807
  return parts.filter((p) => p.type === "text").map((p) => p.text).join("\n");
282
808
  }
283
809
  var DOT = "\u23FA";
810
+ var USER_BG = "#262626";
284
811
  function UserMessage({ content }) {
285
- return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "row", marginTop: 1, children: [
286
- /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "> " }),
287
- /* @__PURE__ */ jsx3(Text3, { children: content })
812
+ const { stdout } = useStdout();
813
+ const termWidth = stdout?.columns ?? 80;
814
+ const bandWidth = Math.max(1, termWidth - 1);
815
+ const PREFIX = " \u203A ";
816
+ const PREFIX_W = 3;
817
+ const rendered = useMemo(() => renderMarkdown(content), [content]);
818
+ const lines = rendered.split("\n");
819
+ const bg = source_default.bgHex(USER_BG);
820
+ const prefixStyle = source_default.gray.bold;
821
+ const padRow = bg(" ".repeat(bandWidth));
822
+ return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", marginTop: 1, children: [
823
+ /* @__PURE__ */ jsx3(Text3, { children: padRow }),
824
+ lines.map((line, i) => {
825
+ const visible = stringWidth(stripAnsi(line));
826
+ const padLen = Math.max(0, bandWidth - PREFIX_W - visible);
827
+ const prefix = i === 0 ? PREFIX : " ";
828
+ const fullLine = bg(prefixStyle(prefix) + line + " ".repeat(padLen));
829
+ return /* @__PURE__ */ jsx3(Text3, { children: fullLine }, i);
830
+ }),
831
+ /* @__PURE__ */ jsx3(Text3, { children: padRow })
288
832
  ] });
289
833
  }
290
- function AssistantMessage({ content }) {
834
+ var ANSI_RE = /\x1b\[[0-9;]*m/g;
835
+ function stripAnsi(s) {
836
+ return s.replace(ANSI_RE, "");
837
+ }
838
+ function AssistantMessage({
839
+ content,
840
+ resultsByCallId
841
+ }) {
291
842
  return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", marginTop: 1, children: content.map((part, i) => {
292
843
  if (part.type === "text") {
293
844
  return /* @__PURE__ */ jsx3(AssistantTextPart, { text: part.text }, i);
@@ -296,7 +847,8 @@ function AssistantMessage({ content }) {
296
847
  if (part.name === "TodoWrite") {
297
848
  return /* @__PURE__ */ jsx3(TodoList, { todos: extractTodos(part.args) }, i);
298
849
  }
299
- return /* @__PURE__ */ jsx3(ToolCallLine, { name: part.name, args: part.args }, i);
850
+ const result = resultsByCallId?.get(part.id);
851
+ return /* @__PURE__ */ jsx3(ToolCallBlock, { name: part.name, args: part.args, result }, i);
300
852
  }
301
853
  return null;
302
854
  }) });
@@ -360,36 +912,50 @@ function TodoRow({ todo }) {
360
912
  ] });
361
913
  }
362
914
  }
363
- function ToolResultLine({
364
- isError,
365
- content,
366
- diff,
367
- summary,
368
- kind
915
+ function ToolCallBlock({
916
+ name,
917
+ args,
918
+ result
369
919
  }) {
370
- const fallback = (() => {
371
- const preview = content.length > 200 ? content.slice(0, 200) + "\u2026" : content;
372
- return preview.split("\n")[0];
373
- })();
374
- const headLine = summary ?? fallback;
375
- const totalLines = content.split("\n").length;
376
- const extra = totalLines > 1 ? ` (+${totalLines - 1} lines)` : "";
377
- const effective = kind ?? (isError ? "error" : "success");
920
+ return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", children: [
921
+ /* @__PURE__ */ jsx3(ToolCallLine, { name, args }),
922
+ result && /* @__PURE__ */ jsx3(ToolResultTree, { result })
923
+ ] });
924
+ }
925
+ var MAX_RESULT_LINES = 5;
926
+ function ToolResultTree({ result, standalone = false }) {
927
+ const isError = result.isError ?? false;
928
+ const effective = result.kind ?? (isError ? "error" : "success");
378
929
  const dotColor = effective === "error" ? "red" : effective === "warn" ? "yellowBright" : "green";
379
- return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", marginLeft: 2, children: [
380
- /* @__PURE__ */ jsxs3(Box2, { flexDirection: "row", children: [
381
- /* @__PURE__ */ jsxs3(Text3, { color: dotColor, children: [
382
- DOT,
383
- " "
384
- ] }),
385
- /* @__PURE__ */ jsx3(Box2, { flexGrow: 1, minWidth: 0, children: /* @__PURE__ */ jsxs3(Text3, { dimColor: true, wrap: "truncate-end", children: [
386
- headLine,
387
- extra
388
- ] }) })
389
- ] }),
390
- diff && /* @__PURE__ */ jsx3(DiffBlock, { diff })
930
+ const cleaned = stripWrapperTags(result.content);
931
+ const rawLines = cleaned.split("\n");
932
+ while (rawLines.length > 0 && rawLines[rawLines.length - 1].trim() === "") rawLines.pop();
933
+ while (rawLines.length > 0 && rawLines[0].trim() === "") rawLines.shift();
934
+ let displayLines;
935
+ let omitted = 0;
936
+ if (result.summary) {
937
+ const extra = rawLines.length > 1 ? ` (+${rawLines.length - 1} lines)` : "";
938
+ displayLines = [result.summary + extra];
939
+ } else if (rawLines.length === 0) {
940
+ displayLines = ["(no output)"];
941
+ } else if (rawLines.length <= MAX_RESULT_LINES) {
942
+ displayLines = rawLines;
943
+ } else {
944
+ displayLines = rawLines.slice(0, MAX_RESULT_LINES);
945
+ omitted = rawLines.length - MAX_RESULT_LINES;
946
+ }
947
+ return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", marginLeft: 2, marginTop: standalone ? 1 : 0, children: [
948
+ displayLines.map((line, i) => /* @__PURE__ */ jsxs3(Box2, { flexDirection: "row", children: [
949
+ /* @__PURE__ */ jsx3(Text3, { color: i === 0 ? dotColor : void 0, children: i === 0 ? "\u2514 " : " " }),
950
+ /* @__PURE__ */ jsx3(Box2, { flexGrow: 1, minWidth: 0, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, wrap: "truncate-end", children: line || " " }) })
951
+ ] }, i)),
952
+ omitted > 0 && /* @__PURE__ */ jsx3(Box2, { marginLeft: 2, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: `(+${omitted} more lines)` }) }),
953
+ result.diff && /* @__PURE__ */ jsx3(DiffBlock, { diff: result.diff })
391
954
  ] });
392
955
  }
956
+ function stripWrapperTags(content) {
957
+ return content.split("\n").filter((l) => !/^<\/?(stdout|stderr|timeout|exit_code)>\s*$/.test(l.trim())).join("\n");
958
+ }
393
959
  function DiffBlock({ diff }) {
394
960
  const lines = diff.split("\n");
395
961
  const start = lines.findIndex((l) => l.startsWith("@@"));
@@ -676,9 +1242,340 @@ function formatTime(iso) {
676
1242
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
677
1243
  }
678
1244
 
679
- // src/components/SlashAutocomplete.tsx
680
- import { Box as Box7, Text as Text8 } from "ink";
1245
+ // src/components/QuestionPicker.tsx
1246
+ import { useState as useState4 } from "react";
1247
+ import { Box as Box7, Text as Text8, useInput as useInput4 } from "ink";
681
1248
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1249
+ var POINTER_COLOR2 = "#A855F7";
1250
+ var FOCUS_BG = "#5B5598";
1251
+ var FOCUS_FG = "white";
1252
+ var SUBMIT_COLOR = "green";
1253
+ var NOTES_LABEL_COLOR = "yellow";
1254
+ var PREVIEW_BORDER = "gray";
1255
+ var TAB_GAP = 3;
1256
+ var PREVIEW_MIN_WIDTH = 28;
1257
+ function QuestionPicker({ request }) {
1258
+ const { questions } = request;
1259
+ const N = questions.length;
1260
+ const hasTabs = N > 1;
1261
+ const submitTabIndex = N;
1262
+ const [tabIndex, setTabIndex] = useState4(0);
1263
+ const [states, setStates] = useState4(
1264
+ () => questions.map(() => ({
1265
+ optionIndex: 0,
1266
+ selected: /* @__PURE__ */ new Set(),
1267
+ notes: "",
1268
+ notesEditing: false,
1269
+ notesDraft: ""
1270
+ }))
1271
+ );
1272
+ const currentQ = tabIndex < N ? questions[tabIndex] : null;
1273
+ const isMulti = currentQ?.multiSelect === true;
1274
+ const onSubmitChip = hasTabs && tabIndex === submitTabIndex;
1275
+ const anyPreview = !!currentQ?.options.some((o) => o.preview);
1276
+ const focusedOpt = currentQ?.options[states[tabIndex]?.optionIndex ?? 0];
1277
+ const focusedPreview = anyPreview ? focusedOpt?.preview ?? "" : "";
1278
+ const buildResponses = (cancelled) => {
1279
+ if (cancelled) return questions.map(() => ({ cancelled: true, selections: [] }));
1280
+ return questions.map((q, qi) => ({
1281
+ cancelled: false,
1282
+ selections: Array.from(states[qi].selected).sort((a, b) => a - b).map((i) => q.options[i].label),
1283
+ notes: states[qi].notes
1284
+ }));
1285
+ };
1286
+ const submit = () => request.resolve(buildResponses(false));
1287
+ const cancel = () => request.resolve(buildResponses(true));
1288
+ const updateState = (qi, mut) => {
1289
+ setStates((prev) => {
1290
+ const next = [...prev];
1291
+ next[qi] = mut(prev[qi]);
1292
+ return next;
1293
+ });
1294
+ };
1295
+ const toggleOption = (qi, oi) => {
1296
+ updateState(qi, (s) => {
1297
+ const sel = new Set(s.selected);
1298
+ if (sel.has(oi)) sel.delete(oi);
1299
+ else sel.add(oi);
1300
+ return { ...s, selected: sel };
1301
+ });
1302
+ };
1303
+ const selectSingleOption = (qi, oi) => {
1304
+ updateState(qi, (s) => ({ ...s, selected: /* @__PURE__ */ new Set([oi]) }));
1305
+ if (!hasTabs) {
1306
+ request.resolve([
1307
+ {
1308
+ cancelled: false,
1309
+ selections: [questions[0].options[oi].label],
1310
+ notes: states[0].notes
1311
+ }
1312
+ ]);
1313
+ return;
1314
+ }
1315
+ if (qi < N - 1) setTabIndex(qi + 1);
1316
+ else setTabIndex(submitTabIndex);
1317
+ };
1318
+ const singleQMultiSubmitRowIndex = !hasTabs && isMulti ? currentQ.options.length : -1;
1319
+ const optionRowCount = !hasTabs && isMulti ? currentQ.options.length + 1 : currentQ?.options.length ?? 0;
1320
+ const currentNotesEditing = tabIndex < N ? states[tabIndex].notesEditing : false;
1321
+ useInput4((input, key) => {
1322
+ if (currentNotesEditing) {
1323
+ if (key.escape) {
1324
+ updateState(tabIndex, (s2) => ({ ...s2, notesEditing: false, notesDraft: "" }));
1325
+ return;
1326
+ }
1327
+ if (key.return) {
1328
+ updateState(tabIndex, (s2) => ({
1329
+ ...s2,
1330
+ notesEditing: false,
1331
+ notes: s2.notesDraft,
1332
+ notesDraft: ""
1333
+ }));
1334
+ return;
1335
+ }
1336
+ if (key.backspace || key.delete) {
1337
+ updateState(tabIndex, (s2) => ({
1338
+ ...s2,
1339
+ notesDraft: s2.notesDraft.slice(0, -1)
1340
+ }));
1341
+ return;
1342
+ }
1343
+ if (key.ctrl || key.tab || key.upArrow || key.downArrow || key.leftArrow || key.rightArrow || key.meta) {
1344
+ return;
1345
+ }
1346
+ if (input) {
1347
+ updateState(tabIndex, (s2) => ({ ...s2, notesDraft: s2.notesDraft + input }));
1348
+ }
1349
+ return;
1350
+ }
1351
+ if (key.escape) {
1352
+ cancel();
1353
+ return;
1354
+ }
1355
+ if (hasTabs && (key.tab || key.leftArrow || key.rightArrow)) {
1356
+ if (key.leftArrow || key.shift && key.tab) {
1357
+ setTabIndex((t) => Math.max(0, t - 1));
1358
+ } else {
1359
+ setTabIndex((t) => Math.min(submitTabIndex, t + 1));
1360
+ }
1361
+ return;
1362
+ }
1363
+ if (onSubmitChip) {
1364
+ if (key.return) submit();
1365
+ return;
1366
+ }
1367
+ const qi = tabIndex;
1368
+ const s = states[qi];
1369
+ if (key.upArrow) {
1370
+ updateState(qi, (st) => ({ ...st, optionIndex: Math.max(0, st.optionIndex - 1) }));
1371
+ return;
1372
+ }
1373
+ if (key.downArrow) {
1374
+ updateState(qi, (st) => ({
1375
+ ...st,
1376
+ optionIndex: Math.min(optionRowCount - 1, st.optionIndex + 1)
1377
+ }));
1378
+ return;
1379
+ }
1380
+ if (input === "n" && !key.ctrl && !key.meta) {
1381
+ updateState(qi, (st) => ({
1382
+ ...st,
1383
+ notesEditing: true,
1384
+ notesDraft: st.notes
1385
+ }));
1386
+ return;
1387
+ }
1388
+ if (key.return) {
1389
+ if (!hasTabs && isMulti && s.optionIndex === singleQMultiSubmitRowIndex) {
1390
+ submit();
1391
+ return;
1392
+ }
1393
+ if (isMulti) toggleOption(qi, s.optionIndex);
1394
+ else selectSingleOption(qi, s.optionIndex);
1395
+ return;
1396
+ }
1397
+ if (isMulti && input === " " && s.optionIndex !== singleQMultiSubmitRowIndex) {
1398
+ toggleOption(qi, s.optionIndex);
1399
+ }
1400
+ });
1401
+ return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", marginTop: 1, children: [
1402
+ /* @__PURE__ */ jsx8(Divider, {}),
1403
+ hasTabs && /* @__PURE__ */ jsx8(Box7, { marginTop: 0, children: /* @__PURE__ */ jsx8(TabBar, { questions, states, tabIndex }) }),
1404
+ tabIndex < N && /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", marginTop: 1, children: [
1405
+ /* @__PURE__ */ jsx8(Text8, { children: questions[tabIndex].question }),
1406
+ /* @__PURE__ */ jsxs8(Box7, { flexDirection: "row", marginTop: 1, children: [
1407
+ /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", flexGrow: anyPreview ? 0 : 1, flexShrink: 0, marginRight: anyPreview ? 2 : 0, children: /* @__PURE__ */ jsx8(
1408
+ OptionsList,
1409
+ {
1410
+ options: questions[tabIndex].options,
1411
+ focusedIndex: states[tabIndex].optionIndex,
1412
+ selected: states[tabIndex].selected,
1413
+ isMulti,
1414
+ submitRowIndex: singleQMultiSubmitRowIndex
1415
+ }
1416
+ ) }),
1417
+ anyPreview && /* @__PURE__ */ jsx8(Box7, { flexGrow: 1, minWidth: PREVIEW_MIN_WIDTH, children: /* @__PURE__ */ jsx8(PreviewPanel, { content: focusedPreview }) })
1418
+ ] }),
1419
+ /* @__PURE__ */ jsx8(
1420
+ NotesLine,
1421
+ {
1422
+ notes: states[tabIndex].notes,
1423
+ editing: states[tabIndex].notesEditing,
1424
+ draft: states[tabIndex].notesDraft
1425
+ }
1426
+ )
1427
+ ] }),
1428
+ onSubmitChip && /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx8(SubmitPreview, { questions, states }) }),
1429
+ /* @__PURE__ */ jsx8(Divider, {}),
1430
+ /* @__PURE__ */ jsx8(Box7, { children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: hintLine(hasTabs, isMulti, currentNotesEditing) }) })
1431
+ ] });
1432
+ }
1433
+ function Divider() {
1434
+ return /* @__PURE__ */ jsx8(Box7, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "gray" });
1435
+ }
1436
+ function TabBar({
1437
+ questions,
1438
+ states,
1439
+ tabIndex
1440
+ }) {
1441
+ const submitIndex = questions.length;
1442
+ const canLeft = tabIndex > 0;
1443
+ const canRight = tabIndex < submitIndex;
1444
+ return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "row", flexWrap: "wrap", children: [
1445
+ /* @__PURE__ */ jsx8(Box7, { marginRight: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: !canLeft, children: "\u2190" }) }),
1446
+ questions.map((q, i) => {
1447
+ const focused = i === tabIndex;
1448
+ const isMulti = q.multiSelect === true;
1449
+ const count = states[i].selected.size;
1450
+ const answered = count > 0;
1451
+ const mark = answered ? isMulti ? `[${count}]` : "\u2714" : "\u25A1";
1452
+ return /* @__PURE__ */ jsx8(Box7, { marginRight: TAB_GAP, children: /* @__PURE__ */ jsx8(
1453
+ Text8,
1454
+ {
1455
+ backgroundColor: focused ? FOCUS_BG : void 0,
1456
+ color: focused ? FOCUS_FG : void 0,
1457
+ bold: focused,
1458
+ dimColor: !focused,
1459
+ children: ` ${mark} ${q.header} `
1460
+ }
1461
+ ) }, i);
1462
+ }),
1463
+ /* @__PURE__ */ jsx8(Box7, { marginRight: 1, children: /* @__PURE__ */ jsx8(
1464
+ Text8,
1465
+ {
1466
+ backgroundColor: tabIndex === submitIndex ? FOCUS_BG : void 0,
1467
+ color: tabIndex === submitIndex ? FOCUS_FG : SUBMIT_COLOR,
1468
+ bold: tabIndex === submitIndex,
1469
+ dimColor: tabIndex !== submitIndex,
1470
+ children: " \u2714 Submit "
1471
+ }
1472
+ ) }),
1473
+ /* @__PURE__ */ jsx8(Text8, { dimColor: !canRight, children: "\u2192" })
1474
+ ] });
1475
+ }
1476
+ function OptionsList({
1477
+ options,
1478
+ focusedIndex,
1479
+ selected,
1480
+ isMulti,
1481
+ submitRowIndex
1482
+ }) {
1483
+ return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
1484
+ options.map((opt, i) => {
1485
+ const focused = i === focusedIndex;
1486
+ const checked = selected.has(i);
1487
+ return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
1488
+ /* @__PURE__ */ jsxs8(Box7, { flexDirection: "row", children: [
1489
+ /* @__PURE__ */ jsx8(Text8, { color: POINTER_COLOR2, bold: true, children: focused ? "\u203A " : " " }),
1490
+ isMulti && /* @__PURE__ */ jsx8(Text8, { color: checked ? "green" : void 0, children: checked ? "[x] " : "[ ] " }),
1491
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: `${i + 1}. ` }),
1492
+ /* @__PURE__ */ jsx8(Text8, { color: focused ? POINTER_COLOR2 : void 0, bold: focused, children: opt.label })
1493
+ ] }),
1494
+ opt.description && /* @__PURE__ */ jsx8(Box7, { marginLeft: isMulti ? 6 : 5, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, wrap: "truncate-end", children: opt.description }) })
1495
+ ] }, i);
1496
+ }),
1497
+ submitRowIndex >= 0 && /* @__PURE__ */ jsxs8(Box7, { flexDirection: "row", marginTop: 1, children: [
1498
+ /* @__PURE__ */ jsx8(Text8, { color: POINTER_COLOR2, bold: true, children: focusedIndex === submitRowIndex ? "\u203A " : " " }),
1499
+ /* @__PURE__ */ jsx8(
1500
+ Text8,
1501
+ {
1502
+ color: focusedIndex === submitRowIndex ? SUBMIT_COLOR : void 0,
1503
+ bold: focusedIndex === submitRowIndex,
1504
+ dimColor: focusedIndex !== submitRowIndex,
1505
+ children: `\u2500\u2500 Submit (${selected.size} selected)`
1506
+ }
1507
+ )
1508
+ ] })
1509
+ ] });
1510
+ }
1511
+ function PreviewPanel({ content }) {
1512
+ const lines = content ? content.split("\n") : ["(no preview)"];
1513
+ return /* @__PURE__ */ jsx8(
1514
+ Box7,
1515
+ {
1516
+ borderStyle: "round",
1517
+ borderColor: PREVIEW_BORDER,
1518
+ flexDirection: "column",
1519
+ paddingX: 1,
1520
+ flexGrow: 1,
1521
+ children: lines.map((line, i) => /* @__PURE__ */ jsx8(Text8, { wrap: "truncate-end", dimColor: !content, children: line || " " }, i))
1522
+ }
1523
+ );
1524
+ }
1525
+ function NotesLine({
1526
+ notes,
1527
+ editing,
1528
+ draft
1529
+ }) {
1530
+ if (editing) {
1531
+ return /* @__PURE__ */ jsxs8(Box7, { marginTop: 1, flexDirection: "row", children: [
1532
+ /* @__PURE__ */ jsx8(Text8, { color: NOTES_LABEL_COLOR, bold: true, children: "Notes: " }),
1533
+ /* @__PURE__ */ jsx8(Text8, { children: draft }),
1534
+ /* @__PURE__ */ jsx8(Text8, { color: POINTER_COLOR2, children: "\u258E" }),
1535
+ /* @__PURE__ */ jsx8(Box7, { flexGrow: 1, marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "(Enter to save \xB7 Esc to discard)" }) })
1536
+ ] });
1537
+ }
1538
+ return /* @__PURE__ */ jsxs8(Box7, { marginTop: 1, flexDirection: "row", justifyContent: "center", children: [
1539
+ /* @__PURE__ */ jsx8(Text8, { color: NOTES_LABEL_COLOR, bold: true, children: "Notes: " }),
1540
+ notes ? /* @__PURE__ */ jsx8(Text8, { children: notes }) : /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "press n to add notes" })
1541
+ ] });
1542
+ }
1543
+ function SubmitPreview({
1544
+ questions,
1545
+ states
1546
+ }) {
1547
+ return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
1548
+ /* @__PURE__ */ jsx8(Text8, { bold: true, children: "Review" }),
1549
+ questions.map((q, qi) => {
1550
+ const sel = Array.from(states[qi].selected).sort((a, b) => a - b).map((i) => q.options[i].label);
1551
+ const notes = states[qi].notes.trim();
1552
+ return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", marginLeft: 2, children: [
1553
+ /* @__PURE__ */ jsxs8(Box7, { flexDirection: "row", children: [
1554
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: `${q.header}: ` }),
1555
+ /* @__PURE__ */ jsx8(Text8, { dimColor: sel.length === 0, children: sel.length > 0 ? sel.join(", ") : "(no answer)" })
1556
+ ] }),
1557
+ notes && /* @__PURE__ */ jsxs8(Box7, { marginLeft: 2, flexDirection: "row", children: [
1558
+ /* @__PURE__ */ jsx8(Text8, { color: NOTES_LABEL_COLOR, children: "notes: " }),
1559
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: notes })
1560
+ ] })
1561
+ ] }, qi);
1562
+ })
1563
+ ] });
1564
+ }
1565
+ function hintLine(hasTabs, isMulti, editingNotes) {
1566
+ if (editingNotes) {
1567
+ return "Enter to save \xB7 Esc to discard \xB7 backspace to delete";
1568
+ }
1569
+ const parts = ["Enter to select", "\u2191/\u2193 to navigate", "n to add notes"];
1570
+ if (hasTabs) parts.push("Tab to switch questions");
1571
+ if (isMulti) parts.push("Space to toggle");
1572
+ parts.push("Esc to cancel");
1573
+ return parts.join(" \xB7 ");
1574
+ }
1575
+
1576
+ // src/components/SlashAutocomplete.tsx
1577
+ import { Box as Box8, Text as Text9 } from "ink";
1578
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
682
1579
  var DEFAULT_MAX = 10;
683
1580
  var SLASH_COLOR = "#A855F7";
684
1581
  function SlashAutocomplete({ matches, index, maxVisible = DEFAULT_MAX }) {
@@ -687,33 +1584,33 @@ function SlashAutocomplete({ matches, index, maxVisible = DEFAULT_MAX }) {
687
1584
  const end = Math.min(matches.length, start + maxVisible);
688
1585
  const visible = matches.slice(start, end);
689
1586
  const nameWidth = Math.max(...matches.map((c) => c.name.length));
690
- return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", marginTop: 1, children: [
1587
+ return /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", marginTop: 1, children: [
691
1588
  visible.map((cmd, i) => {
692
1589
  const realIndex = start + i;
693
- return /* @__PURE__ */ jsx8(Row, { cmd, focused: realIndex === index, nameWidth }, cmd.name);
1590
+ return /* @__PURE__ */ jsx9(Row, { cmd, focused: realIndex === index, nameWidth }, cmd.name);
694
1591
  }),
695
- matches.length > visible.length && /* @__PURE__ */ jsx8(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1592
+ matches.length > visible.length && /* @__PURE__ */ jsx9(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
696
1593
  "\u2191\u2193 select \xB7 Tab/Enter accept \xB7 Esc cancel (",
697
1594
  matches.length - visible.length,
698
1595
  " more)"
699
1596
  ] }) }),
700
- matches.length <= visible.length && /* @__PURE__ */ jsx8(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "\u2191\u2193 select \xB7 Tab/Enter accept \xB7 Esc cancel" }) })
1597
+ matches.length <= visible.length && /* @__PURE__ */ jsx9(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2191\u2193 select \xB7 Tab/Enter accept \xB7 Esc cancel" }) })
701
1598
  ] });
702
1599
  }
703
1600
  function Row({ cmd, focused, nameWidth }) {
704
1601
  const padded = cmd.name.padEnd(nameWidth);
705
- return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "row", children: [
706
- /* @__PURE__ */ jsxs8(Text8, { color: focused ? SLASH_COLOR : void 0, bold: focused, children: [
1602
+ return /* @__PURE__ */ jsxs9(Box8, { flexDirection: "row", children: [
1603
+ /* @__PURE__ */ jsxs9(Text9, { color: focused ? SLASH_COLOR : void 0, bold: focused, children: [
707
1604
  "/",
708
1605
  padded
709
1606
  ] }),
710
- /* @__PURE__ */ jsx8(Text8, { children: " " }),
711
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: cmd.description })
1607
+ /* @__PURE__ */ jsx9(Text9, { children: " " }),
1608
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: cmd.description })
712
1609
  ] });
713
1610
  }
714
1611
 
715
1612
  // src/components/PermissionModeBar.tsx
716
- import { Box as Box8, Text as Text9 } from "ink";
1613
+ import { Box as Box9, Text as Text10 } from "ink";
717
1614
 
718
1615
  // src/permission/index.ts
719
1616
  var MODE_CYCLE = [
@@ -821,7 +1718,7 @@ var PermissionGate = class {
821
1718
  };
822
1719
 
823
1720
  // src/components/PermissionModeBar.tsx
824
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1721
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
825
1722
  function PermissionModeBar({ mode, compact }) {
826
1723
  const color = MODE_COLOR[mode];
827
1724
  const label = MODE_LABEL[mode];
@@ -833,23 +1730,23 @@ function PermissionModeBar({ mode, compact }) {
833
1730
  plan: "[plan]",
834
1731
  bypassPermissions: "[bypass]"
835
1732
  };
836
- return /* @__PURE__ */ jsxs9(Box8, { flexDirection: "row", children: [
837
- /* @__PURE__ */ jsx9(Text9, { color, bold: isBypass, children: short[mode] }),
838
- /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " shift+tab" })
1733
+ return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "row", children: [
1734
+ /* @__PURE__ */ jsx10(Text10, { color, bold: isBypass, children: short[mode] }),
1735
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " shift+tab" })
839
1736
  ] });
840
1737
  }
841
- return /* @__PURE__ */ jsxs9(Box8, { flexDirection: "row", children: [
842
- /* @__PURE__ */ jsxs9(Text9, { color, bold: isBypass, children: [
1738
+ return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "row", children: [
1739
+ /* @__PURE__ */ jsxs10(Text10, { color, bold: isBypass, children: [
843
1740
  "\u25B8\u25B8 ",
844
1741
  label
845
1742
  ] }),
846
- /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " (shift+tab to cycle)" })
1743
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " (shift+tab to cycle)" })
847
1744
  ] });
848
1745
  }
849
1746
 
850
1747
  // src/components/FooterStatus.tsx
851
- import { Box as Box9, Text as Text10 } from "ink";
852
- import { Fragment, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1748
+ import { Box as Box10, Text as Text11 } from "ink";
1749
+ import { Fragment, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
853
1750
  var BAR_TOTAL_WIDE = 10;
854
1751
  var BAR_TOTAL_COMPACT = 6;
855
1752
  function FooterStatus({
@@ -865,57 +1762,57 @@ function FooterStatus({
865
1762
  const hasCtx = contextWindow > 0;
866
1763
  const pct = hasCtx ? Math.min(100, Math.round(lastInputTokens / contextWindow * 100)) : 0;
867
1764
  const ctxColor = pct >= 90 ? "red" : pct >= 70 ? "yellow" : "green";
868
- const SEP = /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " \u2502 " });
869
- const SEP_DOT = /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " \xB7 " });
1765
+ const SEP = /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: " \u2502 " });
1766
+ const SEP_DOT = /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: " \xB7 " });
870
1767
  const renderBar = (barW) => {
871
1768
  const filled = Math.round(pct / 100 * barW);
872
1769
  const empty = barW - filled;
873
- return /* @__PURE__ */ jsxs10(Fragment, { children: [
874
- filled > 0 && /* @__PURE__ */ jsx10(Text10, { color: ctxColor, children: "\u2588".repeat(filled) }),
875
- empty > 0 && /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2591".repeat(empty) })
1770
+ return /* @__PURE__ */ jsxs11(Fragment, { children: [
1771
+ filled > 0 && /* @__PURE__ */ jsx11(Text11, { color: ctxColor, children: "\u2588".repeat(filled) }),
1772
+ empty > 0 && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "\u2591".repeat(empty) })
876
1773
  ] });
877
1774
  };
878
1775
  if (termWidth < 60) {
879
- return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "row", children: [
880
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: sid }),
1776
+ return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "row", children: [
1777
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", bold: true, children: sid }),
881
1778
  SEP_DOT,
882
- /* @__PURE__ */ jsx10(Text10, { color: "magenta", children: model }),
883
- hasCtx && /* @__PURE__ */ jsxs10(Fragment, { children: [
1779
+ /* @__PURE__ */ jsx11(Text11, { color: "magenta", children: model }),
1780
+ hasCtx && /* @__PURE__ */ jsxs11(Fragment, { children: [
884
1781
  SEP_DOT,
885
1782
  renderBar(BAR_TOTAL_COMPACT),
886
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: ` ${pct}%` })
1783
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: ` ${pct}%` })
887
1784
  ] })
888
1785
  ] });
889
1786
  }
890
1787
  if (termWidth < 100) {
891
- return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "row", children: [
892
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: `@${sid}` }),
1788
+ return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "row", children: [
1789
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", bold: true, children: `@${sid}` }),
893
1790
  SEP,
894
- /* @__PURE__ */ jsx10(Text10, { color: "magenta", children: model }),
895
- hasCtx && /* @__PURE__ */ jsxs10(Fragment, { children: [
1791
+ /* @__PURE__ */ jsx11(Text11, { color: "magenta", children: model }),
1792
+ hasCtx && /* @__PURE__ */ jsxs11(Fragment, { children: [
896
1793
  SEP,
897
- /* @__PURE__ */ jsx10(Text10, { children: "ctx: " }),
1794
+ /* @__PURE__ */ jsx11(Text11, { children: "ctx: " }),
898
1795
  renderBar(BAR_TOTAL_COMPACT),
899
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: ` ${pct}%` })
1796
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: ` ${pct}%` })
900
1797
  ] })
901
1798
  ] });
902
1799
  }
903
- return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "row", children: [
904
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: `@${sid}` }),
1800
+ return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "row", children: [
1801
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", bold: true, children: `@${sid}` }),
905
1802
  SEP,
906
- /* @__PURE__ */ jsx10(Text10, { color: "magenta", children: model }),
907
- hasCtx && /* @__PURE__ */ jsxs10(Fragment, { children: [
1803
+ /* @__PURE__ */ jsx11(Text11, { color: "magenta", children: model }),
1804
+ hasCtx && /* @__PURE__ */ jsxs11(Fragment, { children: [
908
1805
  SEP,
909
- /* @__PURE__ */ jsx10(Text10, { children: "ctx: " }),
1806
+ /* @__PURE__ */ jsx11(Text11, { children: "ctx: " }),
910
1807
  renderBar(BAR_TOTAL_WIDE),
911
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: ` ${pct}%` }),
912
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: ` ${formatTokens(lastInputTokens)}/${formatTokens(contextWindow)}` })
1808
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: ` ${pct}%` }),
1809
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: ` ${formatTokens(lastInputTokens)}/${formatTokens(contextWindow)}` })
913
1810
  ] }),
914
1811
  SEP,
915
- /* @__PURE__ */ jsx10(Text10, { children: "tok: " }),
916
- /* @__PURE__ */ jsx10(Text10, { color: "green", children: `\u2191${formatTokens(sessionInputTokens)}` }),
917
- /* @__PURE__ */ jsx10(Text10, { children: " " }),
918
- /* @__PURE__ */ jsx10(Text10, { color: "blueBright", children: `\u2193${formatTokens(sessionOutputTokens)}` })
1812
+ /* @__PURE__ */ jsx11(Text11, { children: "tok: " }),
1813
+ /* @__PURE__ */ jsx11(Text11, { color: "green", children: `\u2191${formatTokens(sessionInputTokens)}` }),
1814
+ /* @__PURE__ */ jsx11(Text11, { children: " " }),
1815
+ /* @__PURE__ */ jsx11(Text11, { color: "blueBright", children: `\u2193${formatTokens(sessionOutputTokens)}` })
919
1816
  ] });
920
1817
  }
921
1818
  function formatTokens(n) {
@@ -926,14 +1823,14 @@ function formatTokens(n) {
926
1823
  }
927
1824
 
928
1825
  // src/components/ProgressBanner.tsx
929
- import { useEffect as useEffect2, useState as useState4 } from "react";
930
- import { Box as Box10, Text as Text11 } from "ink";
931
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1826
+ import { useEffect as useEffect2, useState as useState5 } from "react";
1827
+ import { Box as Box11, Text as Text12 } from "ink";
1828
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
932
1829
  var BAR_WIDTH = 42;
933
1830
  var TICK_MS = 400;
934
1831
  var TIP_ROTATE_SEC = 5;
935
1832
  function ProgressBanner({ state }) {
936
- const [now, setNow] = useState4(Date.now());
1833
+ const [now, setNow] = useState5(Date.now());
937
1834
  useEffect2(() => {
938
1835
  const t = setInterval(() => setNow(Date.now()), TICK_MS);
939
1836
  return () => clearInterval(t);
@@ -944,60 +1841,60 @@ function ProgressBanner({ state }) {
944
1841
  const empty = BAR_WIDTH - filled;
945
1842
  const bar = "\u25B0".repeat(filled) + "\u25B1".repeat(empty);
946
1843
  const tip = state.tips.length ? state.tips[Math.floor(elapsedSec / TIP_ROTATE_SEC) % state.tips.length] : "";
947
- return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", marginTop: 1, children: [
948
- /* @__PURE__ */ jsxs11(Box10, { children: [
949
- /* @__PURE__ */ jsx11(Text11, { color: "cyan", bold: true, children: "\u2726 " }),
950
- /* @__PURE__ */ jsxs11(Text11, { color: "cyan", children: [
1844
+ return /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", marginTop: 1, children: [
1845
+ /* @__PURE__ */ jsxs12(Box11, { children: [
1846
+ /* @__PURE__ */ jsx12(Text12, { color: "cyan", bold: true, children: "\u2726 " }),
1847
+ /* @__PURE__ */ jsxs12(Text12, { color: "cyan", children: [
951
1848
  state.title,
952
1849
  "..."
953
1850
  ] }),
954
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: ` (${elapsedSec}s)` })
1851
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: ` (${elapsedSec}s)` })
955
1852
  ] }),
956
- /* @__PURE__ */ jsxs11(Box10, { marginLeft: 2, children: [
957
- /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: bar }),
958
- /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: ` ${percent}%` })
1853
+ /* @__PURE__ */ jsxs12(Box11, { marginLeft: 2, children: [
1854
+ /* @__PURE__ */ jsx12(Text12, { color: "cyan", children: bar }),
1855
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: ` ${percent}%` })
959
1856
  ] }),
960
- tip && /* @__PURE__ */ jsx11(Box10, { marginLeft: 2, children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: `\u2514 Tip: ${tip}` }) })
1857
+ tip && /* @__PURE__ */ jsx12(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: `\u2514 Tip: ${tip}` }) })
961
1858
  ] });
962
1859
  }
963
1860
 
964
1861
  // src/components/StatusLine.tsx
965
- import { useEffect as useEffect4, useState as useState6 } from "react";
966
- import { Box as Box11, Text as Text13 } from "ink";
1862
+ import { useEffect as useEffect4, useState as useState7 } from "react";
1863
+ import { Box as Box12, Text as Text14 } from "ink";
967
1864
 
968
1865
  // src/components/Shimmer.tsx
969
- import { useEffect as useEffect3, useState as useState5 } from "react";
970
- import { Text as Text12 } from "ink";
971
- import { jsx as jsx12 } from "react/jsx-runtime";
1866
+ import { useEffect as useEffect3, useState as useState6 } from "react";
1867
+ import { Text as Text13 } from "ink";
1868
+ import { jsx as jsx13 } from "react/jsx-runtime";
972
1869
  var FRAME_MS = 100;
973
1870
  var TRAIL = 4;
974
1871
  function Shimmer({ text, bold = true }) {
975
1872
  const chars = Array.from(text);
976
1873
  const cycle = chars.length + TRAIL;
977
- const [phase, setPhase] = useState5(0);
1874
+ const [phase, setPhase] = useState6(0);
978
1875
  useEffect3(() => {
979
1876
  const id = setInterval(() => {
980
1877
  setPhase((p) => (p + 1) % cycle);
981
1878
  }, FRAME_MS);
982
1879
  return () => clearInterval(id);
983
1880
  }, [cycle]);
984
- return /* @__PURE__ */ jsx12(Text12, { children: chars.map((ch, i) => {
1881
+ return /* @__PURE__ */ jsx13(Text13, { children: chars.map((ch, i) => {
985
1882
  const d = Math.abs(i - phase);
986
1883
  if (d === 0) {
987
- return /* @__PURE__ */ jsx12(Text12, { color: "white", bold, children: ch }, i);
1884
+ return /* @__PURE__ */ jsx13(Text13, { color: "white", bold, children: ch }, i);
988
1885
  }
989
1886
  if (d === 1) {
990
- return /* @__PURE__ */ jsx12(Text12, { color: "white", children: ch }, i);
1887
+ return /* @__PURE__ */ jsx13(Text13, { color: "white", children: ch }, i);
991
1888
  }
992
- return /* @__PURE__ */ jsx12(Text12, { color: "gray", dimColor: true, children: ch }, i);
1889
+ return /* @__PURE__ */ jsx13(Text13, { color: "gray", dimColor: true, children: ch }, i);
993
1890
  }) });
994
1891
  }
995
1892
 
996
1893
  // src/components/StatusLine.tsx
997
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1894
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
998
1895
  var TICK_MS2 = 400;
999
1896
  function StatusLine({ startTime, firstTextTime, inputTokens, runningTool, lang }) {
1000
- const [now, setNow] = useState6(Date.now());
1897
+ const [now, setNow] = useState7(Date.now());
1001
1898
  useEffect4(() => {
1002
1899
  const t = setInterval(() => setNow(Date.now()), TICK_MS2);
1003
1900
  return () => clearInterval(t);
@@ -1014,15 +1911,15 @@ function StatusLine({ startTime, firstTextTime, inputTokens, runningTool, lang }
1014
1911
  lang === "zh-CN" ? `\u601D\u8003 ${formatDuration(thinkSec)}` : `thought for ${formatDuration(thinkSec)}`
1015
1912
  );
1016
1913
  }
1017
- return /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", marginTop: 1, children: [
1018
- /* @__PURE__ */ jsxs12(Box11, { flexDirection: "row", children: [
1019
- /* @__PURE__ */ jsx13(Text13, { color: "gray", children: "\u25CF " }),
1020
- /* @__PURE__ */ jsx13(Shimmer, { text: mainLabel }),
1021
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: ` (${parts.join(" \xB7 ")})` })
1914
+ return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginTop: 1, children: [
1915
+ /* @__PURE__ */ jsxs13(Box12, { flexDirection: "row", children: [
1916
+ /* @__PURE__ */ jsx14(Text14, { color: "gray", children: "\u25CF " }),
1917
+ /* @__PURE__ */ jsx14(Shimmer, { text: mainLabel }),
1918
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: ` (${parts.join(" \xB7 ")})` })
1022
1919
  ] }),
1023
- runningTool && /* @__PURE__ */ jsxs12(Box11, { flexDirection: "row", marginLeft: 2, marginTop: 0, children: [
1024
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "\u21B3 " }),
1025
- /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: runningTool })
1920
+ runningTool && /* @__PURE__ */ jsxs13(Box12, { flexDirection: "row", marginLeft: 2, marginTop: 0, children: [
1921
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "\u21B3 " }),
1922
+ /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: runningTool })
1026
1923
  ] })
1027
1924
  ] });
1028
1925
  }
@@ -1597,6 +2494,7 @@ ${todoSection}` : this.ctx.systemPrompt;
1597
2494
  this.ctx.events?.onTurnEnd?.();
1598
2495
  return;
1599
2496
  }
2497
+ this.ctx.events?.onAssistantTurn?.();
1600
2498
  for (const call of toolCallsToRun) {
1601
2499
  await this.runToolCall(call);
1602
2500
  }
@@ -1677,7 +2575,8 @@ ${todoSection}` : this.ctx.systemPrompt;
1677
2575
  abortSignal: this.ctx.abortSignal,
1678
2576
  askPermission: async () => true,
1679
2577
  // 已在外层处理
1680
- todos: this.todos
2578
+ todos: this.todos,
2579
+ askQuestions: this.ctx.events?.onAskQuestions ? (qs) => this.ctx.events.onAskQuestions(qs) : void 0
1681
2580
  };
1682
2581
  const result = await this.ctx.tools.execute(call.name, call.args, toolCtx);
1683
2582
  this.recordToolResult(call.id, call.name, result.content, result.isError ?? false, result.summary, result.diff, result.kind);
@@ -2834,7 +3733,7 @@ var BUILTIN_SLASH_COMMANDS = [
2834
3733
  ];
2835
3734
 
2836
3735
  // src/app.tsx
2837
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
3736
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
2838
3737
  function reducer(state, action) {
2839
3738
  switch (action.type) {
2840
3739
  case "user_submit":
@@ -2891,14 +3790,14 @@ function App({
2891
3790
  initialMessages
2892
3791
  }) {
2893
3792
  const { exit } = useApp();
2894
- const { stdout } = useStdout();
3793
+ const { stdout } = useStdout2();
2895
3794
  const termWidth = stdout?.columns ?? 80;
2896
- const [llm, setLLM] = useState7(initialLLM);
2897
- const [permissions, setPermissions] = useState7(initialPermissions);
2898
- const [settings, setSettings] = useState7(initialSettings);
2899
- const [settingsSources, setSettingsSources] = useState7(initialSources);
2900
- const [modelsRegistry, setModelsRegistry] = useState7(initialModelsRegistry);
2901
- const [mode, setMode] = useState7(initialPermissions.getMode());
3795
+ const [llm, setLLM] = useState8(initialLLM);
3796
+ const [permissions, setPermissions] = useState8(initialPermissions);
3797
+ const [settings, setSettings] = useState8(initialSettings);
3798
+ const [settingsSources, setSettingsSources] = useState8(initialSources);
3799
+ const [modelsRegistry, setModelsRegistry] = useState8(initialModelsRegistry);
3800
+ const [mode, setMode] = useState8(initialPermissions.getMode());
2902
3801
  const [state, dispatch] = useReducer(reducer, {
2903
3802
  history: initialMessages ?? [],
2904
3803
  streamingText: "",
@@ -2912,20 +3811,32 @@ function App({
2912
3811
  turnInputTokens: 0
2913
3812
  });
2914
3813
  const messagesRef = useRef(initialMessages ?? []);
2915
- const [input, setInput] = useState7("");
2916
- const [inputRemountKey, setInputRemountKey] = useState7(0);
3814
+ const [input, setInput] = useState8("");
3815
+ const [inputRemountKey, setInputRemountKey] = useState8(0);
2917
3816
  const commitInput = (value) => {
2918
3817
  setInput(value);
2919
3818
  setInputRemountKey((k) => k + 1);
2920
3819
  };
2921
- const [pending, setPending] = useState7(null);
2922
- const [picker, setPicker] = useState7(null);
2923
- const [sessionPicker, setSessionPicker] = useState7(null);
2924
- const [autocompleteIndex, setAutocompleteIndex] = useState7(0);
2925
- const [progress, setProgress] = useState7(null);
3820
+ const pasteRegistryRef = useRef({
3821
+ map: /* @__PURE__ */ new Map(),
3822
+ nextId: 1
3823
+ });
3824
+ const handlePaste = useCallback((chunk) => {
3825
+ const reg = pasteRegistryRef.current;
3826
+ const id = reg.nextId++;
3827
+ reg.map.set(id, chunk);
3828
+ const lines = chunk.split("\n").length;
3829
+ return `[Pasted text #${id} +${lines} lines]`;
3830
+ }, []);
3831
+ const [pending, setPending] = useState8(null);
3832
+ const [picker, setPicker] = useState8(null);
3833
+ const [sessionPicker, setSessionPicker] = useState8(null);
3834
+ const [questionPicker, setQuestionPicker] = useState8(null);
3835
+ const [autocompleteIndex, setAutocompleteIndex] = useState8(0);
3836
+ const [progress, setProgress] = useState8(null);
2926
3837
  const agentRef = useRef(null);
2927
3838
  const queuedInputsRef = useRef([]);
2928
- const [queuedInputs, setQueuedInputs] = useState7([]);
3839
+ const [queuedInputs, setQueuedInputs] = useState8([]);
2929
3840
  const enqueueInput = (text) => {
2930
3841
  queuedInputsRef.current.push(text);
2931
3842
  setQueuedInputs([...queuedInputsRef.current]);
@@ -2979,7 +3890,7 @@ function App({
2979
3890
  useEffect5(() => {
2980
3891
  return () => resetTerminalTitle();
2981
3892
  }, []);
2982
- const [memoryIndex, setMemoryIndex] = useState7("");
3893
+ const [memoryIndex, setMemoryIndex] = useState8("");
2983
3894
  useEffect5(() => {
2984
3895
  let cancelled = false;
2985
3896
  loadMemoryIndex(cwd).then((idx) => {
@@ -3008,7 +3919,22 @@ function App({
3008
3919
  events: {
3009
3920
  onText: (delta) => dispatch({ type: "stream_delta", delta }),
3010
3921
  onToolCallStart: (_id, name) => dispatch({ type: "tool_start", name }),
3011
- onToolResult: () => dispatch({ type: "set_status", status: "streaming" }),
3922
+ // assistant 流刚结束、这一批 calls 已落到 messages tool 还没开始执行:
3923
+ // 立刻同步 history,让所有 ⏺ Tool(...) 调用头一次性显示出来(之前要等第一个
3924
+ // result 才能见到任何东西,看起来像"卡死了")
3925
+ onAssistantTurn: () => {
3926
+ const msgs = [...agent.getMessages()];
3927
+ messagesRef.current = msgs;
3928
+ dispatch({ type: "history_set", messages: msgs });
3929
+ dispatch({ type: "stream_reset" });
3930
+ },
3931
+ // 每个 tool result 到位就同步:result 立刻挂到对应 ⏺ 调用的 └ 树枝下方
3932
+ onToolResult: () => {
3933
+ const msgs = [...agent.getMessages()];
3934
+ messagesRef.current = msgs;
3935
+ dispatch({ type: "history_set", messages: msgs });
3936
+ dispatch({ type: "set_status", status: "streaming" });
3937
+ },
3012
3938
  onUsage: (usage) => dispatch({ type: "add_usage", usage }),
3013
3939
  onTurnEnd: () => {
3014
3940
  const msgs = [...agent.getMessages()];
@@ -3020,7 +3946,8 @@ function App({
3020
3946
  if (next) {
3021
3947
  setTimeout(() => {
3022
3948
  dispatch({ type: "user_submit" });
3023
- agent.runTurn(next).catch((err) => {
3949
+ const expanded = expandPastes(next, pasteRegistryRef.current.map);
3950
+ agent.runTurn(expanded).catch((err) => {
3024
3951
  const m = err instanceof Error ? err.message : String(err);
3025
3952
  dispatch({ type: "stream_delta", delta: `
3026
3953
  [error] ${m}
@@ -3038,13 +3965,16 @@ function App({
3038
3965
  },
3039
3966
  onPermissionRequest: (toolName, args, summary) => new Promise((resolve6) => {
3040
3967
  setPending({ toolName, args, summary, resolve: resolve6 });
3968
+ }),
3969
+ onAskQuestions: (questions) => new Promise((resolve6) => {
3970
+ setQuestionPicker({ questions, resolve: resolve6 });
3041
3971
  })
3042
3972
  }
3043
3973
  });
3044
3974
  agent.setMessages(messagesRef.current);
3045
3975
  agentRef.current = agent;
3046
3976
  }, [llm, tools, permissions, session, cwd, lang, memoryIndex]);
3047
- useInput4(
3977
+ useInput5(
3048
3978
  (inputKey, key) => {
3049
3979
  if (key.ctrl && inputKey === "c") {
3050
3980
  exit();
@@ -3087,9 +4017,11 @@ function App({
3087
4017
  },
3088
4018
  // 模型在跑时也要响应键盘(让用户能 Ctrl+C / Shift+Tab / autocomplete 导航);
3089
4019
  // 仅模态弹起时让出键盘所有权
3090
- { isActive: !pending && !picker && !sessionPicker }
4020
+ { isActive: !pending && !picker && !sessionPicker && !questionPicker }
3091
4021
  );
3092
- const acceptingInput = pending === null && picker === null && sessionPicker === null;
4022
+ const acceptingInput = pending === null && picker === null && sessionPicker === null && questionPicker === null;
4023
+ const inputVisible = pending === null && picker === null && sessionPicker === null;
4024
+ const inputPlaceholder = questionPicker ? "Chat about this" : void 0;
3093
4025
  const actions = useMemo2(
3094
4026
  () => ({
3095
4027
  setMessages: (msgs) => {
@@ -3221,8 +4153,15 @@ function App({
3221
4153
  return;
3222
4154
  }
3223
4155
  dispatch({ type: "user_submit" });
4156
+ const expanded = expandPastes(trimmed, pasteRegistryRef.current.map);
4157
+ {
4158
+ const userMsg = { role: "user", content: expanded };
4159
+ const next = [...messagesRef.current, userMsg];
4160
+ messagesRef.current = next;
4161
+ dispatch({ type: "history_set", messages: next });
4162
+ }
3224
4163
  try {
3225
- await agentRef.current?.runTurn(trimmed);
4164
+ await agentRef.current?.runTurn(expanded);
3226
4165
  } catch (err) {
3227
4166
  const msg = err instanceof Error ? err.message : String(err);
3228
4167
  dispatch({ type: "stream_delta", delta: `
@@ -3249,19 +4188,35 @@ function App({
3249
4188
  }
3250
4189
  }
3251
4190
  const banner = !showBanner ? null : pickBanner(termWidth, { version: "0.1.0", model: llm.model, cwd: shortCwd(cwd) });
3252
- return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
4191
+ const { resultsByCallId, inlinedIds } = useMemo2(() => {
4192
+ const byId = /* @__PURE__ */ new Map();
4193
+ const used = /* @__PURE__ */ new Set();
4194
+ for (const m of state.history) {
4195
+ if (m.role === "tool" && m.toolUseId) byId.set(m.toolUseId, m);
4196
+ if (m.role === "assistant" && Array.isArray(m.content)) {
4197
+ for (const p of m.content) {
4198
+ if (p.type === "tool_use") used.add(p.id);
4199
+ }
4200
+ }
4201
+ }
4202
+ return { resultsByCallId: byId, inlinedIds: used };
4203
+ }, [state.history]);
4204
+ return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
3253
4205
  banner,
3254
- /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginTop: 1, children: [
3255
- state.history.map((msg, i) => /* @__PURE__ */ jsx14(MessageView, { message: msg }, i)),
3256
- state.streamingText && /* @__PURE__ */ jsxs13(Box12, { flexDirection: "row", marginTop: 1, children: [
3257
- /* @__PURE__ */ jsxs13(Text14, { color: "cyan", children: [
4206
+ /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", marginTop: 1, children: [
4207
+ state.history.map((msg, i) => {
4208
+ if (msg.role === "tool" && inlinedIds.has(msg.toolUseId)) return null;
4209
+ return /* @__PURE__ */ jsx15(MessageView, { message: msg, resultsByCallId }, i);
4210
+ }),
4211
+ state.streamingText && /* @__PURE__ */ jsxs14(Box13, { flexDirection: "row", marginTop: 1, children: [
4212
+ /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
3258
4213
  DOT,
3259
4214
  " "
3260
4215
  ] }),
3261
- /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx14(Text14, { children: state.streamingText }) })
4216
+ /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx15(Text15, { children: state.streamingText }) })
3262
4217
  ] })
3263
4218
  ] }),
3264
- pending && /* @__PURE__ */ jsx14(
4219
+ pending && /* @__PURE__ */ jsx15(
3265
4220
  PermissionPrompt,
3266
4221
  {
3267
4222
  request: {
@@ -3273,7 +4228,7 @@ function App({
3273
4228
  }
3274
4229
  }
3275
4230
  ),
3276
- picker && /* @__PURE__ */ jsx14(
4231
+ picker && /* @__PURE__ */ jsx15(
3277
4232
  ModelSelector,
3278
4233
  {
3279
4234
  request: {
@@ -3285,7 +4240,7 @@ function App({
3285
4240
  }
3286
4241
  }
3287
4242
  ),
3288
- sessionPicker && /* @__PURE__ */ jsx14(
4243
+ sessionPicker && /* @__PURE__ */ jsx15(
3289
4244
  SessionSelector,
3290
4245
  {
3291
4246
  request: {
@@ -3297,7 +4252,19 @@ function App({
3297
4252
  }
3298
4253
  }
3299
4254
  ),
3300
- state.status !== "idle" && /* @__PURE__ */ jsx14(
4255
+ questionPicker && /* @__PURE__ */ jsx15(
4256
+ QuestionPicker,
4257
+ {
4258
+ request: {
4259
+ questions: questionPicker.questions,
4260
+ resolve: (responses) => {
4261
+ questionPicker.resolve(responses);
4262
+ setQuestionPicker(null);
4263
+ }
4264
+ }
4265
+ }
4266
+ ),
4267
+ state.status !== "idle" && /* @__PURE__ */ jsx15(
3301
4268
  StatusLine,
3302
4269
  {
3303
4270
  startTime: state.turnStartTime,
@@ -3307,17 +4274,17 @@ function App({
3307
4274
  lang
3308
4275
  }
3309
4276
  ),
3310
- progress && /* @__PURE__ */ jsx14(ProgressBanner, { state: progress }),
3311
- acceptingInput && /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
3312
- queuedInputs.length > 0 && /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
3313
- queuedInputs.map((q, i) => /* @__PURE__ */ jsx14(Text14, { color: "yellow", dimColor: true, children: `\u21B3 queued: ${q.length > 60 ? q.slice(0, 60) + "\u2026" : q}` }, i)),
3314
- /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: ` (will send after current turn \xB7 ${queuedInputs.length} pending)` })
4277
+ progress && /* @__PURE__ */ jsx15(ProgressBanner, { state: progress }),
4278
+ inputVisible && /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
4279
+ queuedInputs.length > 0 && /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
4280
+ queuedInputs.map((q, i) => /* @__PURE__ */ jsx15(Text15, { color: "yellow", dimColor: true, children: `\u21B3 queued: ${q.length > 60 ? q.slice(0, 60) + "\u2026" : q}` }, i)),
4281
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: ` (will send after current turn \xB7 ${queuedInputs.length} pending)` })
3315
4282
  ] }),
3316
- /* @__PURE__ */ jsxs13(Box12, { marginTop: 1, flexDirection: "column", children: [
3317
- /* @__PURE__ */ jsx14(Text14, { backgroundColor: "#1c1c1c", children: " ".repeat(Math.max(1, termWidth - 1)) }),
3318
- /* @__PURE__ */ jsxs13(Box12, { flexDirection: "row", children: [
3319
- /* @__PURE__ */ jsx14(Text14, { backgroundColor: "#1c1c1c", color: "gray", bold: true, children: " \u276F " }),
3320
- /* @__PURE__ */ jsx14(
4283
+ /* @__PURE__ */ jsxs14(Box13, { marginTop: 1, flexDirection: "column", children: [
4284
+ /* @__PURE__ */ jsx15(Text15, { backgroundColor: "#1c1c1c", children: " ".repeat(Math.max(1, termWidth - 1)) }),
4285
+ /* @__PURE__ */ jsxs14(Box13, { flexDirection: "row", children: [
4286
+ /* @__PURE__ */ jsx15(Text15, { backgroundColor: "#1c1c1c", color: "gray", bold: true, children: " \u203A " }),
4287
+ /* @__PURE__ */ jsx15(
3321
4288
  BgTextInput,
3322
4289
  {
3323
4290
  value: input,
@@ -3325,17 +4292,19 @@ function App({
3325
4292
  onSubmit: handleSubmit,
3326
4293
  width: Math.max(10, termWidth - 4),
3327
4294
  backgroundColor: "#1c1c1c",
3328
- isActive: acceptingInput
4295
+ isActive: acceptingInput,
4296
+ onPaste: handlePaste,
4297
+ placeholder: inputPlaceholder
3329
4298
  },
3330
4299
  inputRemountKey
3331
4300
  )
3332
4301
  ] }),
3333
- /* @__PURE__ */ jsx14(Text14, { backgroundColor: "#1c1c1c", children: " ".repeat(Math.max(1, termWidth - 1)) })
4302
+ /* @__PURE__ */ jsx15(Text15, { backgroundColor: "#1c1c1c", children: " ".repeat(Math.max(1, termWidth - 1)) })
3334
4303
  ] }),
3335
- autocomplete && autocomplete.matches.length > 0 && /* @__PURE__ */ jsx14(SlashAutocomplete, { matches: autocomplete.matches, index: autocompleteIndex })
4304
+ autocomplete && autocomplete.matches.length > 0 && /* @__PURE__ */ jsx15(SlashAutocomplete, { matches: autocomplete.matches, index: autocompleteIndex })
3336
4305
  ] }),
3337
- /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
3338
- /* @__PURE__ */ jsx14(
4306
+ /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
4307
+ /* @__PURE__ */ jsx15(
3339
4308
  FooterStatus,
3340
4309
  {
3341
4310
  sessionId: session.meta.id,
@@ -3347,7 +4316,7 @@ function App({
3347
4316
  termWidth
3348
4317
  }
3349
4318
  ),
3350
- /* @__PURE__ */ jsx14(PermissionModeBar, { mode, compact: termWidth < 60 })
4319
+ /* @__PURE__ */ jsx15(PermissionModeBar, { mode, compact: termWidth < 60 })
3351
4320
  ] })
3352
4321
  ] });
3353
4322
  }
@@ -3361,6 +4330,13 @@ function extractUserInputs(messages) {
3361
4330
  }
3362
4331
  return out;
3363
4332
  }
4333
+ var PASTE_PLACEHOLDER_RE = /\[Pasted text #(\d+) \+\d+ lines\]/g;
4334
+ function expandPastes(value, map) {
4335
+ return value.replace(PASTE_PLACEHOLDER_RE, (full, id) => {
4336
+ const text = map.get(Number(id));
4337
+ return text ?? full;
4338
+ });
4339
+ }
3364
4340
  function shortCwd(cwd) {
3365
4341
  const home = homedir8();
3366
4342
  if (cwd === home) return "~";
@@ -4159,6 +5135,61 @@ var MemoryReadTool = defineTool({
4159
5135
  }
4160
5136
  });
4161
5137
 
5138
+ // src/tools/builtin/ask-user-question.ts
5139
+ import { z as z12 } from "zod";
5140
+ var AskQuestionOptionSchema = z12.object({
5141
+ label: z12.string().min(1).describe("Option text shown to the user. Concise (1-5 words)."),
5142
+ description: z12.string().optional().describe("Optional one-line explanation of what this option means."),
5143
+ preview: z12.string().optional().describe(
5144
+ "Optional rich preview rendered in a right-side panel when this option is focused. Use for code/diagram/config snippets that help compare options visually. Multi-line text supported."
5145
+ )
5146
+ });
5147
+ var AskQuestionSchema = z12.object({
5148
+ question: z12.string().min(1).describe("Full question text (end with ?)."),
5149
+ header: z12.string().min(1).max(16).describe("Very short label (chip), max 12 chars. E.g. 'Auth method'."),
5150
+ options: z12.array(AskQuestionOptionSchema).min(2).max(4).describe("2-4 options. Mutually exclusive unless multiSelect=true."),
5151
+ multiSelect: z12.boolean().optional().describe("Allow multiple selections. Default false.")
5152
+ });
5153
+ var AskUserQuestionArgs = z12.object({
5154
+ questions: z12.array(AskQuestionSchema).min(1).max(4).describe("1-4 questions to ask the user sequentially.")
5155
+ });
5156
+ var AskUserQuestionTool = defineTool({
5157
+ name: "AskUserQuestion",
5158
+ description: "Ask the user one or more multiple-choice questions when their input is needed to proceed. Each question has 2-4 options. Use multiSelect=true for non-mutually-exclusive choices. Prefer this over plain-text questions when the answer space is bounded. If the user presses Esc, the entire batch is treated as cancelled.",
5159
+ parameters: AskUserQuestionArgs,
5160
+ permission: "read",
5161
+ summarize: (args) => `AskUserQuestion(${args.questions.length} question${args.questions.length === 1 ? "" : "s"})`,
5162
+ async execute(args, ctx) {
5163
+ if (!ctx.askQuestions) {
5164
+ return {
5165
+ content: "AskUserQuestion is unavailable: this agent run has no question handler. (Internal bug; tell the user.)",
5166
+ isError: true
5167
+ };
5168
+ }
5169
+ const responses = await ctx.askQuestions(args.questions);
5170
+ if (responses.length > 0 && responses[0].cancelled) {
5171
+ return {
5172
+ content: "User cancelled (Esc). No answers were collected.",
5173
+ isError: false
5174
+ };
5175
+ }
5176
+ const blocks = args.questions.map((q, qi) => {
5177
+ const r = responses[qi];
5178
+ const sel = r?.selections ?? [];
5179
+ const ans = sel.length === 0 ? "(no answer)" : sel.join(", ");
5180
+ const notes = r?.notes?.trim();
5181
+ return notes ? `Q: ${q.question}
5182
+ A: ${ans}
5183
+ Notes: ${notes}` : `Q: ${q.question}
5184
+ A: ${ans}`;
5185
+ });
5186
+ return {
5187
+ content: blocks.join("\n\n"),
5188
+ summary: `Asked ${args.questions.length} question${args.questions.length === 1 ? "" : "s"}`
5189
+ };
5190
+ }
5191
+ });
5192
+
4162
5193
  // src/tools/builtin/index.ts
4163
5194
  var BUILTIN_TOOLS = [
4164
5195
  ReadTool,
@@ -4170,11 +5201,12 @@ var BUILTIN_TOOLS = [
4170
5201
  TodoWriteTool,
4171
5202
  WebFetchTool,
4172
5203
  MemoryReadTool,
4173
- MemoryWriteTool
5204
+ MemoryWriteTool,
5205
+ AskUserQuestionTool
4174
5206
  ];
4175
5207
 
4176
5208
  // src/cli.tsx
4177
- import { jsx as jsx15 } from "react/jsx-runtime";
5209
+ import { jsx as jsx16 } from "react/jsx-runtime";
4178
5210
  var VERSION = "0.1.0";
4179
5211
  async function main() {
4180
5212
  const program = new Command();
@@ -4267,7 +5299,7 @@ async function main() {
4267
5299
  return;
4268
5300
  }
4269
5301
  const { waitUntilExit } = render(
4270
- /* @__PURE__ */ jsx15(
5302
+ /* @__PURE__ */ jsx16(
4271
5303
  App,
4272
5304
  {
4273
5305
  llm,