@garuda-agent/garuda 0.17.0 → 0.18.1

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 (64) hide show
  1. package/CHANGELOG.md +243 -0
  2. package/README.md +3 -2
  3. package/SECURITY.md +1 -1
  4. package/dist/{acpCommand-7EY3NHPG.js → acpCommand-JAYNMIMP.js} +19 -16
  5. package/dist/advise-HDKJGNBL.js +195 -0
  6. package/dist/brief-RWKESOPT.js +91 -0
  7. package/dist/buildCheck-HB46NWLX.js +19 -0
  8. package/dist/buildInfo-K73FFI5S.js +8 -0
  9. package/dist/chunk-2OYTNNUJ.js +216 -0
  10. package/dist/{chunk-Z66BWTMD.js → chunk-34WBDQBL.js} +135 -78
  11. package/dist/chunk-4QRTTRVK.js +88 -0
  12. package/dist/{chunk-2DYMJMRQ.js → chunk-5UDYQ5MK.js} +405 -70
  13. package/dist/{chunk-J4346IDH.js → chunk-7KBOIPUT.js} +2 -1
  14. package/dist/chunk-CWVXOSAA.js +894 -0
  15. package/dist/{chunk-FARS4R67.js → chunk-DQSCCE77.js} +18 -1
  16. package/dist/chunk-DUYLVGPY.js +821 -0
  17. package/dist/chunk-GKU62MHQ.js +5258 -0
  18. package/dist/chunk-H7LIBGZT.js +9 -0
  19. package/dist/chunk-IKPGHM54.js +334 -0
  20. package/dist/chunk-IYGJWX65.js +9 -0
  21. package/dist/{chunk-U65DQZDB.js → chunk-JMH5W5DG.js} +2 -45
  22. package/dist/chunk-L55VUSX3.js +54 -0
  23. package/dist/{chunk-B4SJY7KT.js → chunk-LMKGSYJC.js} +77 -8
  24. package/dist/{chunk-23FXBNBB.js → chunk-MNE7HHX2.js} +2 -1
  25. package/dist/chunk-NH72AELP.js +48 -0
  26. package/dist/{chunk-735BKABA.js → chunk-NLS7GMF7.js} +12 -106
  27. package/dist/{chunk-ZAE25JM2.js → chunk-QANRZW7W.js} +8 -1
  28. package/dist/{chunk-JRVWH27M.js → chunk-QLBCM2YQ.js} +7 -3
  29. package/dist/{chunk-6XHON3Q6.js → chunk-QXTHEFKP.js} +3 -3
  30. package/dist/{chunk-TJIWD7R7.js → chunk-R7RCW4BQ.js} +10 -8
  31. package/dist/chunk-TLA2PDGM.js +156 -0
  32. package/dist/{chunk-5B5HDTUR.js → chunk-TWQ7V2L6.js} +2 -2
  33. package/dist/chunk-ULAJPMY7.js +107 -0
  34. package/dist/chunk-VISKX36R.js +306 -0
  35. package/dist/{chunk-55CFEEIN.js → chunk-WK5NE6RY.js} +2 -2
  36. package/dist/chunk-Y62J7PTD.js +90 -0
  37. package/dist/chunk-YAQ6QLKB.js +40 -0
  38. package/dist/{chunk-7TH3NHCQ.js → chunk-ZKT4P2PC.js} +2 -2
  39. package/dist/cli/index.js +289 -65
  40. package/dist/config-WV2H64TF.js +23 -0
  41. package/dist/{create-W6BV75QX.js → create-OACSGJ37.js} +5 -5
  42. package/dist/evalCommand-BLJJ4G76.js +696 -0
  43. package/dist/evalTune-W53KAD4V.js +302 -0
  44. package/dist/{export-SJ3DUP6W.js → export-E677HIQS.js} +27 -4
  45. package/dist/exportHtml-BMV73YRO.js +21 -0
  46. package/dist/{inkChat-FNUBK43U.js → inkChat-EVLYDC3K.js} +263 -47
  47. package/dist/{install-TKWN5AYH.js → install-4ST5BNRG.js} +3 -3
  48. package/dist/{jobCommand-FE6XN3YD.js → jobCommand-ILOTYOX3.js} +9 -8
  49. package/dist/{launchd-3A3ONEW5.js → launchd-QG6R5RAK.js} +4 -4
  50. package/dist/{lspCommand-ZZ6R4PA2.js → lspCommand-X4FTTONR.js} +3 -3
  51. package/dist/{manager-73MPYFTP.js → manager-L25IUXHW.js} +11 -11
  52. package/dist/{nightCommand-OODJNWDT.js → nightCommand-XPWT6CQZ.js} +9 -8
  53. package/dist/receiptView-YFDHV253.js +19 -0
  54. package/dist/{remove-42WQNVPX.js → remove-STX5UO7X.js} +3 -3
  55. package/dist/{run-ML6GSPN4.js → run-W4TK52WT.js} +6 -6
  56. package/dist/{server-VEYIJHEU.js → server-ASM73QVY.js} +3 -3
  57. package/dist/{setupCommand-GBQIVYJC.js → setupCommand-OSOMFHDQ.js} +10 -9
  58. package/dist/tour-CEVMAC5S.js +18 -0
  59. package/dist/tune-VX2WQTHS.js +52 -0
  60. package/dist/uiState-YFJQ43D7.js +16 -0
  61. package/package.json +1 -1
  62. package/dist/chunk-4JSUUDFQ.js +0 -118
  63. package/dist/chunk-BSK5T2ZG.js +0 -9
  64. package/dist/evalCommand-YVQAWUZE.js +0 -2833
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ KEYS_HELP,
4
+ contextBar,
5
+ contextLevel
6
+ } from "./chunk-L55VUSX3.js";
2
7
  import {
3
8
  FOCUS_REPORTING_OFF,
4
9
  FOCUS_REPORTING_ON,
@@ -6,7 +11,19 @@ import {
6
11
  fuzzyFiles,
7
12
  runChat,
8
13
  statusOf
9
- } from "./chunk-Z66BWTMD.js";
14
+ } from "./chunk-34WBDQBL.js";
15
+ import "./chunk-5UDYQ5MK.js";
16
+ import "./chunk-G2AX7MGS.js";
17
+ import "./chunk-ULAJPMY7.js";
18
+ import "./chunk-JMH5W5DG.js";
19
+ import "./chunk-NH72AELP.js";
20
+ import {
21
+ colorPreview,
22
+ header,
23
+ hunkCount,
24
+ terminalPaint,
25
+ unifiedDiff
26
+ } from "./chunk-VISKX36R.js";
10
27
  import {
11
28
  retryText,
12
29
  serverToolOutput,
@@ -14,27 +31,27 @@ import {
14
31
  summariseCall,
15
32
  summariseResult,
16
33
  todoLines
17
- } from "./chunk-FARS4R67.js";
18
- import {
19
- colorPreview,
20
- header
21
- } from "./chunk-4JSUUDFQ.js";
22
- import "./chunk-2DYMJMRQ.js";
23
- import "./chunk-G2AX7MGS.js";
34
+ } from "./chunk-DQSCCE77.js";
35
+ import "./chunk-4QRTTRVK.js";
24
36
  import "./chunk-A2QAIRQL.js";
25
- import "./chunk-735BKABA.js";
26
- import "./chunk-U65DQZDB.js";
27
- import "./chunk-J4346IDH.js";
37
+ import {
38
+ receiptText,
39
+ screenReaderMode
40
+ } from "./chunk-2OYTNNUJ.js";
41
+ import "./chunk-NLS7GMF7.js";
42
+ import "./chunk-7KBOIPUT.js";
43
+ import "./chunk-Y62J7PTD.js";
28
44
  import "./chunk-B25VL4RU.js";
29
- import "./chunk-23FXBNBB.js";
45
+ import "./chunk-MNE7HHX2.js";
46
+ import "./chunk-42DSA4EA.js";
30
47
  import "./chunk-EU7PA45O.js";
31
- import "./chunk-BSK5T2ZG.js";
48
+ import "./chunk-IYGJWX65.js";
32
49
  import "./chunk-5DEWP3YX.js";
33
- import "./chunk-ZAE25JM2.js";
34
- import "./chunk-ATW7BNIG.js";
35
50
  import "./chunk-PZS3V6P3.js";
51
+ import "./chunk-QANRZW7W.js";
52
+ import "./chunk-ATW7BNIG.js";
36
53
  import "./chunk-AOGFYUKG.js";
37
- import "./chunk-B4SJY7KT.js";
54
+ import "./chunk-LMKGSYJC.js";
38
55
  import "./chunk-BHZ6G65V.js";
39
56
  import "./chunk-P6XWWIEK.js";
40
57
  import "./chunk-VTMD5IHD.js";
@@ -357,6 +374,45 @@ function paletteWindow(selected, count) {
357
374
  return Math.min(Math.max(0, selected - PALETTE_ROWS + 1), count - PALETTE_ROWS);
358
375
  }
359
376
 
377
+ // src/cli/chat/toolGroups.ts
378
+ var GROUPS = {
379
+ read_file: (n) => `Read ${n} files`,
380
+ glob: (n) => `Listed files ${n} times`,
381
+ grep: (n) => `Searched ${n} times`,
382
+ find_symbol: (n) => `Looked up ${n} symbols`,
383
+ find_references: (n) => `Found references ${n} times`,
384
+ find_callers: (n) => `Found callers ${n} times`,
385
+ repo_map: (n) => `Mapped the repository ${n} times`
386
+ };
387
+ function groups(name) {
388
+ return Object.hasOwn(GROUPS, name);
389
+ }
390
+ function groupTargets(calls, width) {
391
+ const all = calls.map((c) => summariseCall(c)).filter((t) => t !== "");
392
+ const shown = [];
393
+ for (const [i, target] of all.entries()) {
394
+ const rest = all.length - i - 1;
395
+ const next = [...shown, target].join(", ");
396
+ const tail = rest > 0 ? `, +${rest}` : "";
397
+ if (shown.length > 0 && next.length + tail.length > width) {
398
+ return `${shown.join(", ")}, +${all.length - i}`;
399
+ }
400
+ shown.push(target);
401
+ }
402
+ return shown.join(", ");
403
+ }
404
+ function groupLine(name, calls, paint, columns) {
405
+ const head = GROUPS[name]?.(calls.length) ?? `${name} \xD7${calls.length}`;
406
+ const room = Math.max(20, columns - head.length - 6);
407
+ return `${paint("cyan", "\u25CF")} ${paint("bold", head)} ${paint("dim", groupTargets(calls, room))}`;
408
+ }
409
+ function planText(lines, paint) {
410
+ const painted = lines.map(
411
+ (line) => line.startsWith("\u2714") ? paint("green", line) : line.startsWith("\u25B6") ? paint("yellow", line) : paint("dim", line)
412
+ );
413
+ return [paint("bold", "Plan"), ...painted.map((l) => ` ${l}`)].join("\n");
414
+ }
415
+
360
416
  // src/cli/chat/store.ts
361
417
  var APPROVAL_KEY_DELAY_MS = 400;
362
418
  var APPROVAL_CHOICES = [
@@ -387,10 +443,18 @@ var ChatStore = class {
387
443
  /** Ctrl-G and /editor (0.6): the chat sets it; it edits the text in the user's editor. */
388
444
  externalEdit;
389
445
  paint;
446
+ columns;
447
+ /** The turn's answer has started (its first text item got the mark). */
448
+ answered = true;
449
+ /** UX U4: the open group: one tool, its calls, and the text of a lone call. */
450
+ openGroup;
451
+ /** UX U4: the last plan of the turn, for the scrollback at the end. */
452
+ planLines;
390
453
  now;
391
454
  constructor(status, options = {}) {
392
455
  this.paint = options.paint ?? ansi;
393
456
  this.now = options.now ?? Date.now;
457
+ this.columns = options.columns ?? (() => process.stdout.columns || 80);
394
458
  this.approvalKeyDelayMs = options.approvalKeyDelayMs ?? APPROVAL_KEY_DELAY_MS;
395
459
  this.state = {
396
460
  items: [],
@@ -401,7 +465,8 @@ var ChatStore = class {
401
465
  editor: emptyEditor(options.history),
402
466
  busy: false,
403
467
  status,
404
- exiting: false
468
+ exiting: false,
469
+ keyHints: options.keyHints ?? true
405
470
  };
406
471
  }
407
472
  // The external store for React (useSyncExternalStore).
@@ -415,7 +480,14 @@ var ChatStore = class {
415
480
  for (const listener of this.listeners) listener();
416
481
  }
417
482
  add(item) {
418
- this.update({ items: [...this.state.items, { ...item, id: this.nextId++ }] });
483
+ const lead = item.kind === "text" && !this.answered;
484
+ if (item.kind === "text") this.answered = true;
485
+ this.update({
486
+ items: [
487
+ ...this.state.items,
488
+ { ...item, ...lead ? { lead } : {}, id: this.nextId++ }
489
+ ]
490
+ });
419
491
  }
420
492
  // Input.
421
493
  /** The next line to run: from the queue, or the next Enter. undefined means exit. */
@@ -571,11 +643,14 @@ var ChatStore = class {
571
643
  /** Start a turn. `show: false` when the line is already on screen (a custom command). */
572
644
  begin(prompt, show = true) {
573
645
  if (show) this.add({ kind: "user", text: prompt });
646
+ this.answered = false;
574
647
  this.stopping = false;
575
648
  this.update({ busy: true });
576
649
  }
577
650
  end(status) {
578
651
  this.flushText();
652
+ this.closeGroup();
653
+ this.closePlan();
579
654
  this.stopping = false;
580
655
  this.update({ busy: false, running: [], status: { ...this.state.status, ...status } });
581
656
  }
@@ -612,6 +687,7 @@ ${text}` });
612
687
  return;
613
688
  case "text_delta": {
614
689
  this.flushThinking();
690
+ this.closeGroup();
615
691
  const { blocks, rest } = takeBlocks(this.state.streaming + event.text);
616
692
  for (const block of blocks)
617
693
  this.add({ kind: "text", text: renderMarkdown(block, this.paint) });
@@ -644,14 +720,30 @@ ${text}` });
644
720
  const summary = summariseResult(event.call, event.outcome);
645
721
  const line = `${this.paint("cyan", "\u25CF")} ${this.paint("bold", event.call.name)} ${summariseCall(event.call)}`;
646
722
  const result = ` ${this.paint("dim", "\u23BF")} ${event.outcome.isError ? this.paint("red", summary) : this.paint("dim", summary)}`;
647
- const todos = todoLines(event.call, event.outcome).map((l) => ` ${l}`);
648
723
  const shown = this.details || event.outcome.isError ? [result] : [];
649
- this.add({ kind: "tool", text: [line, ...shown, ...todos].join("\n") });
724
+ const text = [line, ...shown].join("\n");
650
725
  this.lastOutput = {
651
726
  title: `${event.call.name} ${summariseCall(event.call)}`,
652
727
  text: event.outcome.content
653
728
  };
654
- this.update({ running: this.state.running.filter((r) => r.id !== event.call.id) });
729
+ const running = this.state.running.filter((r) => r.id !== event.call.id);
730
+ const todos = todoLines(event.call, event.outcome);
731
+ if (event.call.name === "todo_write" && todos.length > 0) {
732
+ this.planLines = todos;
733
+ this.update({ running, plan: planText(todos, this.paint) });
734
+ return;
735
+ }
736
+ if (groups(event.call.name) && !event.outcome.isError) {
737
+ if (this.openGroup?.name !== event.call.name) this.closeGroup();
738
+ const open = this.openGroup ?? { name: event.call.name, calls: [], single: text };
739
+ open.calls.push(event.call);
740
+ this.openGroup = open;
741
+ this.update({ running, group: this.groupText(open) });
742
+ return;
743
+ }
744
+ this.closeGroup();
745
+ this.add({ kind: "tool", text });
746
+ this.update({ running });
655
747
  return;
656
748
  }
657
749
  case "compaction": {
@@ -667,6 +759,7 @@ ${text}` });
667
759
  return;
668
760
  case "server_tool": {
669
761
  this.flushText();
762
+ this.closeGroup();
670
763
  const { call, result } = serverToolText(event);
671
764
  const failed = event.result?.error !== void 0;
672
765
  const head = `${this.paint("cyan", "\u25CF")} ${this.paint("bold", call)}`;
@@ -685,6 +778,24 @@ ${text}` });
685
778
  info(text) {
686
779
  this.note("info", text);
687
780
  }
781
+ /** UX U10: `?` on an empty input line prints every key. */
782
+ showKeys() {
783
+ this.add({ kind: "output", text: this.paint("dim", KEYS_HELP) });
784
+ }
785
+ /** UX U1: the turn's receipt, as a box in the scrollback. */
786
+ receipt(receipt) {
787
+ this.flushText();
788
+ this.closeGroup();
789
+ this.add({
790
+ kind: "output",
791
+ text: `
792
+ ${receiptText(receipt, {
793
+ columns: this.columns(),
794
+ paint: (style, text) => this.paint(style, text),
795
+ screenReader: screenReaderMode()
796
+ })}`
797
+ });
798
+ }
688
799
  warn(text) {
689
800
  this.note("warn", text);
690
801
  }
@@ -693,6 +804,7 @@ ${text}` });
693
804
  }
694
805
  note(level, text) {
695
806
  this.flushText();
807
+ this.closeGroup();
696
808
  const trimmed = text.replace(/^\n+/, "");
697
809
  const style = level === "info" ? "dim" : level === "warn" ? "yellow" : "red";
698
810
  this.add({ kind: "note", level, text: this.paint(style, trimmed) });
@@ -707,6 +819,30 @@ ${text}` });
707
819
  this.add({ kind: "output", text: thinkingPreview(text, (s) => this.paint("dim", s)) });
708
820
  this.lastOutput = { title: "thinking", text };
709
821
  }
822
+ /** UX U4: the open group's text: a lone call as it always was, else one line for all. */
823
+ groupText(open) {
824
+ return open.calls.length === 1 ? open.single : groupLine(open.name, open.calls, this.paint, this.columns());
825
+ }
826
+ /** UX U4: the open group goes into the scrollback as one item. */
827
+ closeGroup() {
828
+ const open = this.openGroup;
829
+ if (open === void 0) return;
830
+ this.openGroup = void 0;
831
+ this.add({ kind: "tool", text: this.groupText(open) });
832
+ const { group: _closed, ...rest } = this.state;
833
+ this.state = rest;
834
+ for (const listener of this.listeners) listener();
835
+ }
836
+ /** UX U4: at the end of a turn, the last plan goes into the scrollback once. */
837
+ closePlan() {
838
+ const lines = this.planLines;
839
+ if (lines === void 0) return;
840
+ this.planLines = void 0;
841
+ this.add({ kind: "tool", text: planText(lines, this.paint) });
842
+ const { plan: _closed, ...rest } = this.state;
843
+ this.state = rest;
844
+ for (const listener of this.listeners) listener();
845
+ }
710
846
  flushText() {
711
847
  this.flushThinking();
712
848
  const rest = this.state.streaming;
@@ -741,8 +877,13 @@ ${text}` });
741
877
  approvalKeyDelayMs;
742
878
  askNow(request, signal) {
743
879
  this.flushText();
744
- this.add({ kind: "output", text: `${header(request)}
745
- ${colorPreview(request)}` });
880
+ const hunks = request.selectHunks === void 0 ? 0 : hunkCount(request.preview);
881
+ const hint = hunks >= 2 ? this.paint("dim", ` \xB7 ${hunks} hunks \xB7 h reviews them one by one`) : "";
882
+ this.add({
883
+ kind: "output",
884
+ text: `${header(request)}${hint}
885
+ ${colorPreview(request, this.columns())}`
886
+ });
746
887
  return new Promise((resolve, reject) => {
747
888
  const onAbort = () => {
748
889
  this.answer = void 0;
@@ -881,6 +1022,7 @@ function App({ store }) {
881
1022
  /* @__PURE__ */ jsx(Static, { items: state.items, children: (item) => /* @__PURE__ */ jsx(ItemView, { item }, item.id) }),
882
1023
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
883
1024
  state.streaming !== "" && /* @__PURE__ */ jsx(Text, { children: state.streaming }),
1025
+ state.group !== void 0 && /* @__PURE__ */ jsx(Text, { children: state.group }),
884
1026
  state.running.map((tool) => /* @__PURE__ */ jsxs(Text, { children: [
885
1027
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: frame }),
886
1028
  " ",
@@ -900,6 +1042,7 @@ function App({ store }) {
900
1042
  hasHunks: state.approval.request.selectHunks !== void 0
901
1043
  }
902
1044
  ),
1045
+ state.plan !== void 0 && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { children: state.plan }) }),
903
1046
  state.queue.map((line, i) => (
904
1047
  // biome-ignore lint/suspicious/noArrayIndexKey: queued lines can repeat.
905
1048
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -914,15 +1057,45 @@ function App({ store }) {
914
1057
  ] })
915
1058
  ] });
916
1059
  }
1060
+ var USER_BAR = "\u258C";
1061
+ var USER_BORDER = {
1062
+ topLeft: "",
1063
+ top: "",
1064
+ topRight: "",
1065
+ left: USER_BAR,
1066
+ right: "",
1067
+ bottomLeft: "",
1068
+ bottom: "",
1069
+ bottomRight: ""
1070
+ };
1071
+ var GARUDA_MARK = "\u2726 ";
917
1072
  function ItemView({ item }) {
918
1073
  switch (item.kind) {
919
1074
  case "user":
920
- return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { bold: true, children: [
921
- "\u203A ",
1075
+ if (screenReaderMode()) {
1076
+ return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { bold: true, children: [
1077
+ "You: ",
1078
+ item.text
1079
+ ] }) });
1080
+ }
1081
+ return /* @__PURE__ */ jsx(
1082
+ Box,
1083
+ {
1084
+ marginTop: 1,
1085
+ borderStyle: USER_BORDER,
1086
+ borderColor: "cyan",
1087
+ borderTop: false,
1088
+ borderRight: false,
1089
+ borderBottom: false,
1090
+ paddingLeft: 1,
1091
+ children: /* @__PURE__ */ jsx(Text, { bold: true, children: item.text })
1092
+ }
1093
+ );
1094
+ case "text":
1095
+ return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { children: [
1096
+ item.lead === true && /* @__PURE__ */ jsx(Text, { color: "yellow", children: screenReaderMode() ? "Garuda: " : GARUDA_MARK }),
922
1097
  item.text
923
1098
  ] }) });
924
- case "text":
925
- return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { children: item.text }) });
926
1099
  default:
927
1100
  return /* @__PURE__ */ jsx(Text, { children: item.text });
928
1101
  }
@@ -951,13 +1124,7 @@ function HunkStagingView({ staging }) {
951
1124
  ] })
952
1125
  ] }),
953
1126
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: current.header }),
954
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingLeft: 1, children: current.lines.map((line, i) => {
955
- const color = line.startsWith("+") ? "green" : line.startsWith("-") ? "red" : void 0;
956
- return (
957
- // biome-ignore lint/suspicious/noArrayIndexKey: diff lines can be identical
958
- /* @__PURE__ */ jsx(Text, { ...color ? { color } : {}, children: line }, i)
959
- );
960
- }) }),
1127
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingLeft: 1, children: /* @__PURE__ */ jsx(Text, { children: unifiedDiff(current.lines.join("\n"), terminalPaint) }) }),
961
1128
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "[y] stage hunk \xB7 [n] skip \xB7 [a] stage all \xB7 [d] discard all \xB7 [\u2190/\u2192] prev/next \xB7 [Esc] back" })
962
1129
  ] });
963
1130
  }
@@ -1013,36 +1180,76 @@ function PaletteView({ palette }) {
1013
1180
  ] })
1014
1181
  ] });
1015
1182
  }
1183
+ var INPUT_HINT = "Type a task \xB7 / for commands \xB7 @ for files";
1184
+ var INPUT_HINT_BUSY = "Type to queue the next task \xB7 Esc stops this one";
1185
+ var INPUT_BOX_MIN_COLUMNS = 40;
1016
1186
  function InputLine({ state }) {
1017
1187
  const { text, cursor } = state.editor;
1018
1188
  const at = text[cursor];
1019
- return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { children: [
1189
+ const line = text === "" ? /* @__PURE__ */ jsxs(Text, { children: [
1190
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u203A " }),
1191
+ /* @__PURE__ */ jsx(Text, { inverse: true, children: " " }),
1192
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: state.busy ? INPUT_HINT_BUSY : INPUT_HINT })
1193
+ ] }) : /* @__PURE__ */ jsxs(Text, { children: [
1020
1194
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u203A " }),
1021
1195
  text.slice(0, cursor),
1022
1196
  /* @__PURE__ */ jsx(Text, { inverse: true, children: at === void 0 || at === "\n" ? " " : at }),
1023
1197
  at === "\n" ? "\n" : "",
1024
1198
  text.slice(cursor + 1)
1025
- ] }) });
1199
+ ] });
1200
+ if (screenReaderMode() || (process.stdout.columns || 80) < INPUT_BOX_MIN_COLUMNS) {
1201
+ return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: line });
1202
+ }
1203
+ return /* @__PURE__ */ jsx(
1204
+ Box,
1205
+ {
1206
+ marginTop: 1,
1207
+ borderStyle: "round",
1208
+ borderColor: "cyan",
1209
+ borderDimColor: state.busy,
1210
+ paddingX: 1,
1211
+ children: line
1212
+ }
1213
+ );
1026
1214
  }
1027
1215
  function Footer({ state }) {
1028
1216
  const { model, sandbox, contextPercent, costUsd, mode } = state.status;
1029
- const parts = [model, sandbox];
1030
- if (contextPercent !== void 0) parts.push(`context ${contextPercent}%`);
1031
- if (costUsd !== void 0) parts.push(`$${costUsd.toFixed(costUsd < 0.01 ? 4 : 2)}`);
1032
- const keys = state.busy ? "Esc stop \xB7 Ctrl-O output" : "\\ then Enter: new line \xB7 Ctrl-G editor \xB7 Ctrl-P commands";
1217
+ const keys = footerKeys(state.busy, state.keyHints);
1218
+ const level = contextPercent === void 0 ? "ok" : contextLevel(contextPercent);
1033
1219
  return /* @__PURE__ */ jsxs(Text, { children: [
1220
+ " ",
1034
1221
  mode === "plan" ? /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
1035
1222
  "PLAN",
1036
1223
  " "
1037
1224
  ] }) : null,
1225
+ /* @__PURE__ */ jsx(
1226
+ Text,
1227
+ {
1228
+ dimColor: true,
1229
+ children: `${model} \xB7 ${sandbox === "no sandbox" ? sandbox : `\u26E8 ${sandbox.replace(/^sandbox /, "")}`}`
1230
+ }
1231
+ ),
1232
+ contextPercent !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1233
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
1234
+ /* @__PURE__ */ jsx(
1235
+ Text,
1236
+ {
1237
+ ...level === "ok" ? { dimColor: true } : { color: level === "full" ? "red" : "yellow" },
1238
+ children: `${contextBar(contextPercent)} ${contextPercent}%`
1239
+ }
1240
+ )
1241
+ ] }),
1038
1242
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1039
- parts.join(" \xB7 "),
1040
- " ",
1243
+ costUsd === void 0 ? "" : ` \xB7 $${costUsd.toFixed(costUsd < 0.01 ? 4 : 2)}`,
1041
1244
  " ",
1042
1245
  keys
1043
1246
  ] })
1044
1247
  ] });
1045
1248
  }
1249
+ function footerKeys(busy, hints) {
1250
+ if (busy) return "Esc stop \xB7 Ctrl-O output";
1251
+ return hints ? "\\ then Enter: new line \xB7 Ctrl-G editor \xB7 Ctrl-P commands \xB7 ? keys" : "? keys";
1252
+ }
1046
1253
  var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1047
1254
  function useSpinner(active) {
1048
1255
  const [i, setI] = useState(0);
@@ -1135,7 +1342,9 @@ function onKey(store, state, input, key) {
1135
1342
  if (key.shift) store.toggleMode();
1136
1343
  else store.completeLine();
1137
1344
  } else if (/[\r\n]/.test(input)) typeAhead(store, input);
1138
- else if (input !== "" && !key.meta) store.editLine({ type: "insert", text: input });
1345
+ else if (input === "?" && store.getState().editor.text === "" && !store.getState().busy) {
1346
+ store.showKeys();
1347
+ } else if (input !== "" && !key.meta) store.editLine({ type: "insert", text: input });
1139
1348
  }
1140
1349
  function typeAhead(store, chunk) {
1141
1350
  const parts = chunk.split(/\r\n|\r|\n/);
@@ -1152,8 +1361,14 @@ function typeAhead(store, chunk) {
1152
1361
  }
1153
1362
 
1154
1363
  // src/cli/chat/inkChat.ts
1155
- async function runInkChat(runtime, approver, setEventTarget, banner, sessionPath, exitNow, firstInput, notifier, focus) {
1156
- const store = new ChatStore(statusOf(runtime));
1364
+ function clearScreenKeepingScrollback(rows) {
1365
+ return `${"\n".repeat(Math.max(1, rows))}\x1B[H`;
1366
+ }
1367
+ async function runInkChat(runtime, approver, setEventTarget, banner, sessionPath, exitNow, firstInput, notifier, focus, keyHints = true, clearOnStart = true, tour) {
1368
+ if (clearOnStart && process.stdout.isTTY === true) {
1369
+ process.stdout.write(clearScreenKeepingScrollback(process.stdout.rows || 24));
1370
+ }
1371
+ const store = new ChatStore(statusOf(runtime), { keyHints });
1157
1372
  store.print(banner);
1158
1373
  if (firstInput !== void 0) store.enqueue(firstInput);
1159
1374
  approver.current = store;
@@ -1185,13 +1400,14 @@ async function runInkChat(runtime, approver, setEventTarget, banner, sessionPath
1185
1400
  return exitNow();
1186
1401
  };
1187
1402
  try {
1188
- await runChat(runtime, store, sessionPath, exitClean, notifier);
1403
+ await runChat(runtime, store, sessionPath, exitClean, notifier, tour);
1189
1404
  } finally {
1190
1405
  reportFocus(false);
1191
1406
  ink.unmount();
1192
1407
  }
1193
1408
  }
1194
1409
  export {
1410
+ clearScreenKeepingScrollback,
1195
1411
  runInkChat
1196
1412
  };
1197
- //# sourceMappingURL=inkChat-FNUBK43U.js.map
1413
+ //# sourceMappingURL=inkChat-EVLYDC3K.js.map
@@ -3,9 +3,9 @@ import {
3
3
  INSTALL_TIMEOUT_MS,
4
4
  installCommand,
5
5
  installServer
6
- } from "./chunk-7TH3NHCQ.js";
6
+ } from "./chunk-ZKT4P2PC.js";
7
7
  import "./chunk-EU7PA45O.js";
8
- import "./chunk-B4SJY7KT.js";
8
+ import "./chunk-LMKGSYJC.js";
9
9
  import "./chunk-BHZ6G65V.js";
10
10
  import "./chunk-P6XWWIEK.js";
11
11
  import "./chunk-UIRJOQQS.js";
@@ -16,4 +16,4 @@ export {
16
16
  installCommand,
17
17
  installServer
18
18
  };
19
- //# sourceMappingURL=install-TKWN5AYH.js.map
19
+ //# sourceMappingURL=install-4ST5BNRG.js.map
@@ -5,18 +5,19 @@ import {
5
5
  msUntil,
6
6
  prepareJob,
7
7
  testsBefore
8
- } from "./chunk-TJIWD7R7.js";
8
+ } from "./chunk-R7RCW4BQ.js";
9
9
  import "./chunk-N4I6YS3E.js";
10
- import "./chunk-735BKABA.js";
11
- import "./chunk-J4346IDH.js";
10
+ import "./chunk-ULAJPMY7.js";
11
+ import "./chunk-NLS7GMF7.js";
12
+ import "./chunk-7KBOIPUT.js";
12
13
  import "./chunk-5DEWP3YX.js";
13
14
  import "./chunk-PZS3V6P3.js";
14
- import "./chunk-55CFEEIN.js";
15
- import "./chunk-5B5HDTUR.js";
15
+ import "./chunk-WK5NE6RY.js";
16
+ import "./chunk-TWQ7V2L6.js";
16
17
  import "./chunk-3MRQJKUO.js";
17
18
  import "./chunk-AOGFYUKG.js";
18
- import "./chunk-6XHON3Q6.js";
19
- import "./chunk-B4SJY7KT.js";
19
+ import "./chunk-QXTHEFKP.js";
20
+ import "./chunk-LMKGSYJC.js";
20
21
  import "./chunk-BHZ6G65V.js";
21
22
  import "./chunk-P6XWWIEK.js";
22
23
  import "./chunk-VTMD5IHD.js";
@@ -30,4 +31,4 @@ export {
30
31
  prepareJob,
31
32
  testsBefore
32
33
  };
33
- //# sourceMappingURL=jobCommand-FE6XN3YD.js.map
34
+ //# sourceMappingURL=jobCommand-ILOTYOX3.js.map
@@ -14,9 +14,9 @@ import {
14
14
  removeSpec,
15
15
  specPath,
16
16
  specPlist
17
- } from "./chunk-5B5HDTUR.js";
18
- import "./chunk-6XHON3Q6.js";
19
- import "./chunk-B4SJY7KT.js";
17
+ } from "./chunk-TWQ7V2L6.js";
18
+ import "./chunk-QXTHEFKP.js";
19
+ import "./chunk-LMKGSYJC.js";
20
20
  import "./chunk-BHZ6G65V.js";
21
21
  import "./chunk-P6XWWIEK.js";
22
22
  import "./chunk-VTMD5IHD.js";
@@ -39,4 +39,4 @@ export {
39
39
  specPath,
40
40
  specPlist
41
41
  };
42
- //# sourceMappingURL=launchd-3A3ONEW5.js.map
42
+ //# sourceMappingURL=launchd-QG6R5RAK.js.map
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-36GQIVGT.js";
6
6
  import {
7
7
  installServer
8
- } from "./chunk-7TH3NHCQ.js";
8
+ } from "./chunk-ZKT4P2PC.js";
9
9
  import {
10
10
  LSP_CONFIG_FILE,
11
11
  loadLspConfig
@@ -18,7 +18,7 @@ import {
18
18
  HostExecutor,
19
19
  createExecutor,
20
20
  loadSettings
21
- } from "./chunk-B4SJY7KT.js";
21
+ } from "./chunk-LMKGSYJC.js";
22
22
  import "./chunk-BHZ6G65V.js";
23
23
  import "./chunk-P6XWWIEK.js";
24
24
  import "./chunk-UIRJOQQS.js";
@@ -98,4 +98,4 @@ export {
98
98
  lspInstallCommand,
99
99
  lspStatusCommand
100
100
  };
101
- //# sourceMappingURL=lspCommand-ZZ6R4PA2.js.map
101
+ //# sourceMappingURL=lspCommand-X4FTTONR.js.map
@@ -11,30 +11,30 @@ import {
11
11
  } from "./chunk-4P26OKTP.js";
12
12
  import {
13
13
  VERSION
14
- } from "./chunk-BSK5T2ZG.js";
14
+ } from "./chunk-IYGJWX65.js";
15
15
  import {
16
16
  USER_SCOPE
17
17
  } from "./chunk-5DEWP3YX.js";
18
+ import {
19
+ capText,
20
+ cleanJson,
21
+ cleanLine,
22
+ cleanText,
23
+ neutralizeTags
24
+ } from "./chunk-PZS3V6P3.js";
18
25
  import {
19
26
  commandLine,
20
27
  defHash,
21
28
  expandEnv,
22
29
  isHttp
23
- } from "./chunk-ZAE25JM2.js";
30
+ } from "./chunk-QANRZW7W.js";
24
31
  import {
25
32
  isLoopbackHost
26
33
  } from "./chunk-ATW7BNIG.js";
27
- import {
28
- capText,
29
- cleanJson,
30
- cleanLine,
31
- cleanText,
32
- neutralizeTags
33
- } from "./chunk-PZS3V6P3.js";
34
34
  import {
35
35
  DEFAULT_ENV_ALLOWLIST,
36
36
  sandboxPaths
37
- } from "./chunk-B4SJY7KT.js";
37
+ } from "./chunk-LMKGSYJC.js";
38
38
  import "./chunk-BHZ6G65V.js";
39
39
  import "./chunk-P6XWWIEK.js";
40
40
  import "./chunk-UIRJOQQS.js";
@@ -846,4 +846,4 @@ export {
846
846
  changeLines,
847
847
  warnings
848
848
  };
849
- //# sourceMappingURL=manager-73MPYFTP.js.map
849
+ //# sourceMappingURL=manager-L25IUXHW.js.map