@algolia/wizard 0.12.0-rc.104.151 → 0.12.0

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 (2) hide show
  1. package/dist/main.js +513 -287
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { render } from "ink";
5
5
 
6
6
  // src/ui/App.tsx
7
- import { Box as Box15, Text as Text15, useApp, useInput as useInput6, useWindowSize as useWindowSize8 } from "ink";
7
+ import { Box as Box16, Text as Text16, useApp, useInput as useInput7, useWindowSize as useWindowSize8 } from "ink";
8
8
 
9
9
  // src/core/store.ts
10
10
  import { create } from "zustand";
@@ -304,7 +304,10 @@ var useWizard = create((set, get) => ({
304
304
  if (_noticeTimer === null) {
305
305
  set({
306
306
  notices: [...notices, notice],
307
- _noticeTimer: setTimeout(() => get()._drainNoticeQueue(), NOTICE_INTERVAL_MS)
307
+ _noticeTimer: setTimeout(
308
+ () => get()._drainNoticeQueue(),
309
+ NOTICE_INTERVAL_MS
310
+ )
308
311
  });
309
312
  } else {
310
313
  set({ _noticeQueue: [..._noticeQueue, notice] });
@@ -316,7 +319,10 @@ var useWizard = create((set, get) => ({
316
319
  set((s) => ({
317
320
  notices: [...s.notices, next],
318
321
  _noticeQueue: rest,
319
- _noticeTimer: setTimeout(() => get()._drainNoticeQueue(), NOTICE_INTERVAL_MS)
322
+ _noticeTimer: setTimeout(
323
+ () => get()._drainNoticeQueue(),
324
+ NOTICE_INTERVAL_MS
325
+ )
320
326
  }));
321
327
  } else {
322
328
  set({ _noticeTimer: null });
@@ -364,13 +370,17 @@ var useWizard = create((set, get) => ({
364
370
  await markInteraction();
365
371
  get()._resolve?.(value);
366
372
  set({ inputReq: null, _resolve: null, phase: "running" });
367
- const id = get().logStart("prompt", `User input: ${describeInputValue(value)}`);
373
+ const id = get().logStart(
374
+ "prompt",
375
+ `User input: ${describeInputValue(value)}`
376
+ );
368
377
  get().logEnd(id, "success");
369
378
  },
370
379
  setDone: () => set({ phase: "done" }),
371
380
  setError: (message) => set({ phase: "error", error: message }),
372
381
  reset: () => {
373
382
  get()._clearNoticeQueue();
383
+ get()._resolve?.(false);
374
384
  set({
375
385
  phase: "idle",
376
386
  homeScreen: "home",
@@ -622,10 +632,13 @@ function Notices() {
622
632
  }
623
633
 
624
634
  // src/ui/PromptInput.tsx
625
- import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
635
+ import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
626
636
  import TextInput from "ink-text-input";
627
637
  import { useState as useState5 } from "react";
628
638
 
639
+ // src/ui/CommandApproval.tsx
640
+ import { Box as Box5, Text as Text5, useInput } from "ink";
641
+
629
642
  // src/ui/NextAction.tsx
630
643
  import { Box as Box4, Text as Text4 } from "ink";
631
644
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -649,14 +662,60 @@ function NextAction({
649
662
  ] });
650
663
  }
651
664
 
665
+ // src/ui/CommandApproval.tsx
666
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
667
+ function CommandApproval({
668
+ command,
669
+ onDecide
670
+ }) {
671
+ useInput((_input, key) => {
672
+ if (key.return) onDecide("approve");
673
+ else if (key.escape) onDecide("reject");
674
+ });
675
+ return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
676
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, bold: true, children: "Run this command?" }),
677
+ /* @__PURE__ */ jsxs4(
678
+ Box5,
679
+ {
680
+ flexDirection: "column",
681
+ paddingLeft: 2,
682
+ borderStyle: "single",
683
+ borderColor: COLORS.success,
684
+ borderTop: false,
685
+ borderBottom: false,
686
+ borderRight: false,
687
+ gap: 1,
688
+ children: [
689
+ /* @__PURE__ */ jsxs4(Box5, { children: [
690
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "$ " }),
691
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.strong, wrap: "wrap", children: command.command })
692
+ ] }),
693
+ /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
694
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "in:" }),
695
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
696
+ ] }),
697
+ command.explanation && /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
698
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "why:" }),
699
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
700
+ ] })
701
+ ]
702
+ }
703
+ ),
704
+ /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
705
+ /* @__PURE__ */ jsx4(NextAction, { action: "approve", keyHint: "enter" }),
706
+ /* @__PURE__ */ jsx4(NextAction, { action: "reject", keyHint: "esc", hierarchy: "secondary" })
707
+ ] })
708
+ ] });
709
+ }
710
+
652
711
  // src/ui/SelectPrompt.tsx
653
- import { Box as Box6, Text as Text6, useInput, useWindowSize as useWindowSize5 } from "ink";
712
+ import { Box as Box7, Text as Text7, useInput as useInput2, useWindowSize as useWindowSize5 } from "ink";
654
713
  import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
655
714
 
656
715
  // src/ui/ScrollView.tsx
657
- import { Box as Box5, Text as Text5, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
716
+ import { Box as Box6, Text as Text6, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
658
717
  import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
659
- import { jsxs as jsxs4 } from "react/jsx-runtime";
718
+ import { jsxs as jsxs5 } from "react/jsx-runtime";
660
719
  var INDICATOR_ROWS = 2;
661
720
  function fittedWidth(node, columns) {
662
721
  let left = 0;
@@ -726,14 +785,14 @@ function useScrollWindow({
726
785
  };
727
786
  }
728
787
  function ScrollView({ scroll, children }) {
729
- return /* @__PURE__ */ jsxs4(Box5, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
730
- scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
788
+ return /* @__PURE__ */ jsxs5(Box6, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
789
+ scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
731
790
  "\u2191 ",
732
791
  scroll.hiddenAbove,
733
792
  " more"
734
793
  ] }),
735
794
  children,
736
- scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
795
+ scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
737
796
  "\u2193 ",
738
797
  scroll.hiddenBelow,
739
798
  " more"
@@ -742,7 +801,7 @@ function ScrollView({ scroll, children }) {
742
801
  }
743
802
 
744
803
  // src/ui/SelectPrompt.tsx
745
- import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
804
+ import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
746
805
  var CANCEL = "cancel";
747
806
  var ARROW_WIDTH = 4;
748
807
  var COLUMN_GAP = 2;
@@ -804,7 +863,7 @@ function SelectPrompt({
804
863
  revealIndex(index);
805
864
  }, [index, revealIndex]);
806
865
  const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
807
- useInput((input, key) => {
866
+ useInput2((input, key) => {
808
867
  if (rows.length === 0) return;
809
868
  if (key.upArrow || input === "k") {
810
869
  setIndex((i) => (i - 1 + rows.length) % rows.length);
@@ -827,56 +886,56 @@ function SelectPrompt({
827
886
  }
828
887
  }
829
888
  });
830
- return /* @__PURE__ */ jsx4(Box6, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, width, children: [
831
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
832
- error && /* @__PURE__ */ jsx4(Text6, { color: COLORS.danger, children: error }),
833
- messages?.map((m, i) => /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
834
- table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
835
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
836
- question && /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: question }),
837
- helpText && /* @__PURE__ */ jsx4(Text6, { color: COLORS.dim, children: helpText })
889
+ return /* @__PURE__ */ jsx5(Box7, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, width, children: [
890
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
891
+ error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: error }),
892
+ messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
893
+ table && /* @__PURE__ */ jsx5(Table, { columns: table.columns, rows: table.rows }),
894
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
895
+ question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: question }),
896
+ helpText && /* @__PURE__ */ jsx5(Text7, { color: COLORS.dim, children: helpText })
838
897
  ] })
839
898
  ] }),
840
- /* @__PURE__ */ jsx4(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
899
+ /* @__PURE__ */ jsx5(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
841
900
  const i = scroll.offset + visibleIndex;
842
901
  const highlighted = i === index;
843
902
  const isCancel = i === cancelIndex;
844
903
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
845
904
  const sec = isCancel ? void 0 : secondary?.[i];
846
905
  const labelColor = highlighted ? COLORS.highlight.fg : void 0;
847
- const label = /* @__PURE__ */ jsxs5(Text6, { color: labelColor, wrap: "truncate", children: [
906
+ const label = /* @__PURE__ */ jsxs6(Text7, { color: labelColor, wrap: "truncate", children: [
848
907
  highlighted ? "\u276F " : " ",
849
908
  bullet,
850
909
  option
851
910
  ] });
852
911
  const isText = sec?.kind === "text";
853
- return /* @__PURE__ */ jsxs5(
854
- Box6,
912
+ return /* @__PURE__ */ jsxs6(
913
+ Box7,
855
914
  {
856
915
  width: isText ? "100%" : barWidth,
857
916
  paddingX: 1,
858
917
  paddingY: 1,
859
918
  backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
860
919
  children: [
861
- /* @__PURE__ */ jsx4(Box6, { width: isText ? labelWidth : barLabelWidth, children: label }),
862
- isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box6, { width: textWidth, children: /* @__PURE__ */ jsx4(
863
- Text6,
920
+ /* @__PURE__ */ jsx5(Box7, { width: isText ? labelWidth : barLabelWidth, children: label }),
921
+ isText && textWidth > 0 && /* @__PURE__ */ jsx5(Box7, { width: textWidth, children: /* @__PURE__ */ jsx5(
922
+ Text7,
864
923
  {
865
924
  wrap: "truncate",
866
925
  color: highlighted ? COLORS.primary : COLORS.muted,
867
926
  children: sec.value
868
927
  }
869
928
  ) }),
870
- sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box6, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text6, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
929
+ sec?.kind === "badge" && /* @__PURE__ */ jsx5(Box7, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx5(Text7, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
871
930
  ]
872
931
  },
873
932
  `row-${i}`
874
933
  );
875
934
  }) }),
876
- /* @__PURE__ */ jsx4(Box6, { flexShrink: 0, children: /* @__PURE__ */ jsx4(Text6, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
935
+ /* @__PURE__ */ jsx5(Box7, { flexShrink: 0, children: /* @__PURE__ */ jsx5(Text7, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs6(Text7, { children: [
877
936
  i > 0 ? " " : "",
878
- /* @__PURE__ */ jsx4(Text6, { color: COLORS.primary, children: key }),
879
- /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
937
+ /* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: key }),
938
+ /* @__PURE__ */ jsxs6(Text7, { color: COLORS.dim, children: [
880
939
  " ",
881
940
  label
882
941
  ] })
@@ -885,23 +944,23 @@ function SelectPrompt({
885
944
  }
886
945
 
887
946
  // src/ui/PromptInput.tsx
888
- import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
947
+ import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
889
948
  var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
890
949
  function EnterToContinuePrompt({
891
950
  question,
892
951
  messages,
893
952
  onDecide
894
953
  }) {
895
- useInput2((_input, key) => {
954
+ useInput3((_input, key) => {
896
955
  if (key.return) onDecide(true);
897
956
  else if (key.escape) onDecide(false);
898
957
  });
899
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, children: [
900
- messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
901
- question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: question }),
902
- /* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
903
- /* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
904
- /* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
958
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, children: [
959
+ messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
960
+ question && /* @__PURE__ */ jsx6(Text8, { color: COLORS.primary, children: question }),
961
+ /* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
962
+ /* @__PURE__ */ jsx6(NextAction, { action: "continue", keyHint: "enter" }),
963
+ /* @__PURE__ */ jsx6(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
905
964
  ] })
906
965
  ] });
907
966
  }
@@ -909,11 +968,11 @@ function PromptInput() {
909
968
  const { phase, inputReq, submitInput } = useWizard();
910
969
  const [draft, setDraft] = useState5("");
911
970
  if (phase === "done" || phase === "error") {
912
- return /* @__PURE__ */ jsx5(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text7, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
971
+ return /* @__PURE__ */ jsx6(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text8, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
913
972
  }
914
973
  if (phase !== "awaitingInput" || !inputReq) return null;
915
974
  if (inputReq.promptType === "multipleChoice") {
916
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
975
+ return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
917
976
  SelectPrompt,
918
977
  {
919
978
  question: inputReq.prompt,
@@ -930,7 +989,7 @@ function PromptInput() {
930
989
  ) });
931
990
  }
932
991
  if (inputReq.promptType === "multiSelect") {
933
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
992
+ return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
934
993
  SelectPrompt,
935
994
  {
936
995
  multi: true,
@@ -945,7 +1004,7 @@ function PromptInput() {
945
1004
  ) });
946
1005
  }
947
1006
  if (inputReq.promptType === "notice") {
948
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
1007
+ return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
949
1008
  SelectPrompt,
950
1009
  {
951
1010
  question: inputReq.prompt,
@@ -956,7 +1015,7 @@ function PromptInput() {
956
1015
  ) });
957
1016
  }
958
1017
  if (inputReq.promptType === "enterToContinue") {
959
- return /* @__PURE__ */ jsx5(
1018
+ return /* @__PURE__ */ jsx6(
960
1019
  EnterToContinuePrompt,
961
1020
  {
962
1021
  question: inputReq.prompt,
@@ -965,9 +1024,12 @@ function PromptInput() {
965
1024
  }
966
1025
  );
967
1026
  }
1027
+ if (inputReq.promptType === "commandApproval" && inputReq.command) {
1028
+ return /* @__PURE__ */ jsx6(CommandApproval, { command: inputReq.command, onDecide: submitInput });
1029
+ }
968
1030
  if (inputReq.promptType === "acceptReject") {
969
1031
  const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
970
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
1032
+ return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
971
1033
  SelectPrompt,
972
1034
  {
973
1035
  question: inputReq.prompt,
@@ -978,15 +1040,15 @@ function PromptInput() {
978
1040
  }
979
1041
  ) });
980
1042
  }
981
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
982
- inputReq.error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: inputReq.error }),
983
- inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
984
- /* @__PURE__ */ jsxs6(Box7, { children: [
985
- /* @__PURE__ */ jsxs6(Text7, { color: COLORS.primary, children: [
1043
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
1044
+ inputReq.error && /* @__PURE__ */ jsx6(Text8, { color: COLORS.danger, children: inputReq.error }),
1045
+ inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
1046
+ /* @__PURE__ */ jsxs7(Box8, { children: [
1047
+ /* @__PURE__ */ jsxs7(Text8, { color: COLORS.primary, children: [
986
1048
  inputReq.prompt,
987
1049
  " "
988
1050
  ] }),
989
- /* @__PURE__ */ jsx5(
1051
+ /* @__PURE__ */ jsx6(
990
1052
  TextInput,
991
1053
  {
992
1054
  value: draft,
@@ -1004,7 +1066,7 @@ function PromptInput() {
1004
1066
  // src/ui/Welcome.tsx
1005
1067
  import { dirname as dirname2, join as join3 } from "node:path";
1006
1068
  import { fileURLToPath } from "node:url";
1007
- import { Box as Box8, Spacer, Text as Text8, useInput as useInput3, useWindowSize as useWindowSize6 } from "ink";
1069
+ import { Box as Box9, Spacer, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
1008
1070
 
1009
1071
  // src/ui/copy/welcome.ts
1010
1072
  var sidebarItems = [
@@ -1031,26 +1093,21 @@ var sidebarItems = [
1031
1093
  ];
1032
1094
 
1033
1095
  // src/ui/Welcome.tsx
1034
- import Image, { TerminalInfoContext, defaultTerminalInfo } from "ink-picture";
1035
- import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
1096
+ import Image, { InkPictureProvider } from "ink-picture";
1097
+ import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
1036
1098
  var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
1037
- var TERMINAL_INFO = {
1038
- ...defaultTerminalInfo,
1039
- supportsUnicode: true,
1040
- supportsColor: true
1041
- };
1042
1099
  function SidebarItem({
1043
1100
  title,
1044
1101
  description
1045
1102
  }) {
1046
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
1047
- /* @__PURE__ */ jsxs7(Box8, { gap: 1, children: [
1048
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.success, children: "\u2192" }),
1049
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.strong, bold: true, children: title })
1103
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
1104
+ /* @__PURE__ */ jsxs8(Box9, { gap: 1, children: [
1105
+ /* @__PURE__ */ jsx7(Text9, { color: COLORS.success, children: "\u2192" }),
1106
+ /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: title })
1050
1107
  ] }),
1051
- /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 2, children: [
1052
- /* @__PURE__ */ jsx6(Spacer, {}),
1053
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: description })
1108
+ /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 2, children: [
1109
+ /* @__PURE__ */ jsx7(Spacer, {}),
1110
+ /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: description })
1054
1111
  ] })
1055
1112
  ] });
1056
1113
  }
@@ -1058,7 +1115,7 @@ function Welcome() {
1058
1115
  const confirmStart = useWizard((s) => s.confirmStart);
1059
1116
  const openLearnMore = useWizard((s) => s.openLearnMore);
1060
1117
  const { rows } = useWindowSize6();
1061
- useInput3((input, key) => {
1118
+ useInput4((input, key) => {
1062
1119
  if (key.return) confirmStart();
1063
1120
  else if (input === "i") openLearnMore();
1064
1121
  });
@@ -1076,16 +1133,16 @@ function Welcome() {
1076
1133
  if (rows < 30) {
1077
1134
  layout = scales["small"];
1078
1135
  }
1079
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
1080
- /* @__PURE__ */ jsx6(
1081
- Box8,
1136
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
1137
+ /* @__PURE__ */ jsx7(
1138
+ Box9,
1082
1139
  {
1083
1140
  paddingY: layout.main.padding.y,
1084
1141
  paddingX: layout.main.padding.x,
1085
1142
  flexDirection: "column",
1086
1143
  justifyContent: "center",
1087
- children: /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 2, children: [
1088
- /* @__PURE__ */ jsx6(TerminalInfoContext.Provider, { value: TERMINAL_INFO, children: /* @__PURE__ */ jsx6(
1144
+ children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 2, children: [
1145
+ /* @__PURE__ */ jsx7(InkPictureProvider, { children: /* @__PURE__ */ jsx7(
1089
1146
  Image,
1090
1147
  {
1091
1148
  src: IMAGE_PATH,
@@ -1096,16 +1153,16 @@ function Welcome() {
1096
1153
  protocol: "halfBlock"
1097
1154
  }
1098
1155
  ) }),
1099
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
1100
- /* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
1101
- /* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
1102
- /* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
1156
+ /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
1157
+ /* @__PURE__ */ jsxs8(Box9, { gap: 1, flexDirection: "column", children: [
1158
+ /* @__PURE__ */ jsx7(NextAction, { action: "start wizard", keyHint: "enter" }),
1159
+ /* @__PURE__ */ jsx7(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
1103
1160
  ] })
1104
1161
  ] })
1105
1162
  }
1106
1163
  ),
1107
- /* @__PURE__ */ jsxs7(
1108
- Box8,
1164
+ /* @__PURE__ */ jsxs8(
1165
+ Box9,
1109
1166
  {
1110
1167
  backgroundColor: COLORS.bg.sidebar,
1111
1168
  width: 40,
@@ -1115,8 +1172,8 @@ function Welcome() {
1115
1172
  flexDirection: "column",
1116
1173
  justifyContent: "center",
1117
1174
  children: [
1118
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
1119
- sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
1175
+ /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
1176
+ sidebarItems.map((i, idx) => /* @__PURE__ */ jsx7(SidebarItem, { title: i.title, description: i.description }, idx))
1120
1177
  ]
1121
1178
  }
1122
1179
  )
@@ -1125,7 +1182,7 @@ function Welcome() {
1125
1182
 
1126
1183
  // src/ui/LearnMore.tsx
1127
1184
  import { Fragment as Fragment2 } from "react";
1128
- import { Box as Box9, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize7 } from "ink";
1185
+ import { Box as Box10, Text as Text10, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
1129
1186
 
1130
1187
  // src/ui/copy/learn-more.ts
1131
1188
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -1133,12 +1190,17 @@ var accessItems = [
1133
1190
  {
1134
1191
  tag: "READ",
1135
1192
  title: "Project files",
1136
- description: "reads package.json, configs & source to detect your stack. Read-only; nothing is uploaded."
1193
+ description: "reads manifests, configs & source to detect your stack. Read-only; nothing is uploaded."
1137
1194
  },
1138
1195
  {
1139
1196
  tag: "WRITE",
1140
1197
  title: "Code changes",
1141
- description: "creates & edits files (search UI, config). Shown as a diff first \u2014 nothing lands without your approval."
1198
+ description: "creates & edits files (search UI, config) in a throwaway git worktree \u2014 your checkout is never touched."
1199
+ },
1200
+ {
1201
+ tag: "EXEC",
1202
+ title: "Setup commands",
1203
+ description: "runs dependency installs, the ingestion script & your own checks. Every command is shown in full and needs your OK; its output is shown as-is, so a command that prints a secret will display it."
1142
1204
  },
1143
1205
  {
1144
1206
  tag: "NET",
@@ -1148,13 +1210,13 @@ var accessItems = [
1148
1210
  {
1149
1211
  tag: "KEY",
1150
1212
  title: "Credentials",
1151
- description: "saves your Admin API key to .env and adds it to .gitignore."
1213
+ description: "writes your Algolia app id and a search-only key (safe to expose) to .env in the worktree."
1152
1214
  }
1153
1215
  ];
1154
1216
  var neverItems = [
1155
1217
  "Send your source code to a model or third party",
1156
1218
  "Commit or push to git",
1157
- "Touch files outside your project directory"
1219
+ "Run a command you haven't approved"
1158
1220
  ];
1159
1221
  var policyLinks = [
1160
1222
  { label: "Terms", url: "https://www.algolia.com/policies/terms" },
@@ -1162,10 +1224,11 @@ var policyLinks = [
1162
1224
  ];
1163
1225
 
1164
1226
  // src/ui/LearnMore.tsx
1165
- import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
1227
+ import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1166
1228
  var TAG_COLORS = {
1167
1229
  READ: COLORS.success,
1168
1230
  WRITE: COLORS.badge,
1231
+ EXEC: COLORS.danger,
1169
1232
  NET: COLORS.accent,
1170
1233
  KEY: COLORS.muted
1171
1234
  };
@@ -1178,12 +1241,12 @@ function NeverLine({
1178
1241
  }) {
1179
1242
  const used = segments.reduce((n, s) => n + s.text.length, 0);
1180
1243
  const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
1181
- return /* @__PURE__ */ jsxs8(Text9, { children: [
1182
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" }),
1244
+ return /* @__PURE__ */ jsxs9(Text10, { children: [
1245
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" }),
1183
1246
  " ".repeat(NEVER_BOX_PAD_X),
1184
- segments.map((s, i) => /* @__PURE__ */ jsx7(Text9, { color: s.color, bold: s.bold, children: s.text }, i)),
1247
+ segments.map((s, i) => /* @__PURE__ */ jsx8(Text10, { color: s.color, bold: s.bold, children: s.text }, i)),
1185
1248
  " ".repeat(rightPad),
1186
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" })
1249
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" })
1187
1250
  ] });
1188
1251
  }
1189
1252
  function LearnMore() {
@@ -1191,12 +1254,12 @@ function LearnMore() {
1191
1254
  const backToHome = useWizard((s) => s.backToHome);
1192
1255
  const { columns } = useWindowSize7();
1193
1256
  const dividerWidth = Math.max(0, columns - PADDING_X * 2);
1194
- useInput4((_input, key) => {
1257
+ useInput5((_input, key) => {
1195
1258
  if (key.escape) backToHome();
1196
1259
  else if (key.return) confirmStart();
1197
1260
  });
1198
- return /* @__PURE__ */ jsxs8(
1199
- Box9,
1261
+ return /* @__PURE__ */ jsxs9(
1262
+ Box10,
1200
1263
  {
1201
1264
  flexDirection: "column",
1202
1265
  paddingX: PADDING_X,
@@ -1204,31 +1267,31 @@ function LearnMore() {
1204
1267
  width: "100%",
1205
1268
  gap: 1,
1206
1269
  children: [
1207
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
1208
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: accessIntro }),
1209
- /* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, children: [
1210
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
1211
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, marginTop: 1, children: [
1212
- /* @__PURE__ */ jsx7(Box9, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text9, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
1213
- /* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { children: [
1214
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: item.title }),
1215
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
1270
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
1271
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: accessIntro }),
1272
+ /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginTop: 1, children: [
1273
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
1274
+ /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, marginTop: 1, children: [
1275
+ /* @__PURE__ */ jsx8(Box10, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text10, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
1276
+ /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text10, { children: [
1277
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: item.title }),
1278
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
1216
1279
  ] }) })
1217
1280
  ] })
1218
1281
  ] }, item.tag)) }),
1219
- /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
1220
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
1221
- /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1222
- /* @__PURE__ */ jsx7(
1282
+ /* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "column", children: [
1283
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
1284
+ /* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
1285
+ /* @__PURE__ */ jsx8(
1223
1286
  NeverLine,
1224
1287
  {
1225
1288
  width: dividerWidth,
1226
1289
  segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
1227
1290
  }
1228
1291
  ),
1229
- neverItems.map((item) => /* @__PURE__ */ jsxs8(Fragment2, { children: [
1230
- /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1231
- /* @__PURE__ */ jsx7(
1292
+ neverItems.map((item) => /* @__PURE__ */ jsxs9(Fragment2, { children: [
1293
+ /* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
1294
+ /* @__PURE__ */ jsx8(
1232
1295
  NeverLine,
1233
1296
  {
1234
1297
  width: dividerWidth,
@@ -1240,24 +1303,24 @@ function LearnMore() {
1240
1303
  }
1241
1304
  )
1242
1305
  ] }, item)),
1243
- /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1244
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1306
+ /* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
1307
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1245
1308
  ] }),
1246
- /* @__PURE__ */ jsx7(Box9, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1247
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1248
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.accent, children: link.url })
1309
+ /* @__PURE__ */ jsx8(Box10, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
1310
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1311
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.accent, children: link.url })
1249
1312
  ] }, link.label)) }),
1250
- /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1251
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1252
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
1253
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "esc" }),
1254
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "] back" })
1313
+ /* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1314
+ /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
1315
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
1316
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "esc" }),
1317
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "] back" })
1255
1318
  ] }),
1256
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1257
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
1258
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "enter" }),
1259
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "]" }),
1260
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.success, bold: true, children: "start wizard" })
1319
+ /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
1320
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
1321
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "enter" }),
1322
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "]" }),
1323
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.success, bold: true, children: "start wizard" })
1261
1324
  ] })
1262
1325
  ] })
1263
1326
  ]
@@ -1266,10 +1329,10 @@ function LearnMore() {
1266
1329
  }
1267
1330
 
1268
1331
  // src/ui/Sidebar.tsx
1269
- import { Box as Box12, Text as Text12 } from "ink";
1332
+ import { Box as Box13, Text as Text13 } from "ink";
1270
1333
 
1271
1334
  // src/ui/Steps.tsx
1272
- import { Box as Box10, Text as Text10 } from "ink";
1335
+ import { Box as Box11, Text as Text11 } from "ink";
1273
1336
  import Spinner from "ink-spinner";
1274
1337
 
1275
1338
  // src/core/persistence.ts
@@ -1298,12 +1361,12 @@ async function clearWorkflowState(workflowId) {
1298
1361
  }
1299
1362
 
1300
1363
  // src/ui/Steps.tsx
1301
- import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1364
+ import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
1302
1365
  function Steps() {
1303
1366
  const { steps } = useWizard();
1304
1367
  const visibleSteps = steps.filter(isStepVisible);
1305
- return /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status[s.status], children: [
1306
- s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
1368
+ return /* @__PURE__ */ jsx9(Box11, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx9(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status[s.status], children: [
1369
+ s.status === "running" ? /* @__PURE__ */ jsx9(Spinner, { type: "dots" }) : MARKER[s.status],
1307
1370
  " ",
1308
1371
  s.title
1309
1372
  ] }) }, s.id)) });
@@ -1312,27 +1375,27 @@ function CurrentStep() {
1312
1375
  const { steps } = useWizard();
1313
1376
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
1314
1377
  if (!currentStep) return null;
1315
- return /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status.running, children: [
1316
- /* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
1378
+ return /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status.running, children: [
1379
+ /* @__PURE__ */ jsx9(Spinner, { type: "dots" }),
1317
1380
  " ",
1318
1381
  ` ${currentStep.title}`
1319
1382
  ] });
1320
1383
  }
1321
1384
 
1322
1385
  // src/ui/Progress.tsx
1323
- import { Box as Box11, Text as Text11 } from "ink";
1324
- import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
1386
+ import { Box as Box12, Text as Text12 } from "ink";
1387
+ import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
1325
1388
  function Progress() {
1326
1389
  const { steps, currentStepIndex } = useWizard();
1327
1390
  const visibleSteps = steps.filter(isStepVisible);
1328
1391
  if (visibleSteps.length === 0) return null;
1329
1392
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
1330
1393
  const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
1331
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
1332
- /* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "STEP" }),
1333
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: activeStepNumber }),
1334
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: "/" }),
1335
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: visibleSteps.length })
1394
+ return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
1395
+ /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "STEP" }),
1396
+ /* @__PURE__ */ jsx10(Text12, { bold: true, children: activeStepNumber }),
1397
+ /* @__PURE__ */ jsx10(Text12, { bold: true, children: "/" }),
1398
+ /* @__PURE__ */ jsx10(Text12, { bold: true, children: visibleSteps.length })
1336
1399
  ] });
1337
1400
  }
1338
1401
 
@@ -1343,10 +1406,10 @@ var sidebarCommands = [
1343
1406
  ];
1344
1407
 
1345
1408
  // src/ui/Sidebar.tsx
1346
- import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
1409
+ import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
1347
1410
  function Sidebar() {
1348
- return /* @__PURE__ */ jsxs11(
1349
- Box12,
1411
+ return /* @__PURE__ */ jsxs12(
1412
+ Box13,
1350
1413
  {
1351
1414
  backgroundColor: "#14171E",
1352
1415
  width: 30,
@@ -1355,16 +1418,16 @@ function Sidebar() {
1355
1418
  flexDirection: "column",
1356
1419
  justifyContent: "space-between",
1357
1420
  children: [
1358
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
1359
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "PROGRESS" }),
1360
- /* @__PURE__ */ jsx10(Steps, {})
1421
+ /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
1422
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "PROGRESS" }),
1423
+ /* @__PURE__ */ jsx11(Steps, {})
1361
1424
  ] }),
1362
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
1363
- /* @__PURE__ */ jsx10(Progress, {}),
1364
- /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: sidebarCommands.map((c) => {
1365
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
1366
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1367
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: c.description })
1425
+ /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
1426
+ /* @__PURE__ */ jsx11(Progress, {}),
1427
+ /* @__PURE__ */ jsx11(Box13, { flexDirection: "column", children: sidebarCommands.map((c) => {
1428
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
1429
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1430
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: c.description })
1368
1431
  ] });
1369
1432
  }) })
1370
1433
  ] })
@@ -1374,12 +1437,12 @@ function Sidebar() {
1374
1437
  }
1375
1438
 
1376
1439
  // src/ui/Ribbon.tsx
1377
- import { Box as Box13, Text as Text13 } from "ink";
1378
- import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
1440
+ import { Box as Box14, Text as Text14 } from "ink";
1441
+ import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
1379
1442
  function Ribbon() {
1380
1443
  const firstCommand = sidebarCommands[0];
1381
- return /* @__PURE__ */ jsxs12(
1382
- Box13,
1444
+ return /* @__PURE__ */ jsxs13(
1445
+ Box14,
1383
1446
  {
1384
1447
  backgroundColor: "#14171E",
1385
1448
  flexDirection: "row",
@@ -1387,11 +1450,11 @@ function Ribbon() {
1387
1450
  paddingX: 2,
1388
1451
  paddingY: 1,
1389
1452
  children: [
1390
- /* @__PURE__ */ jsx11(Progress, {}),
1391
- /* @__PURE__ */ jsx11(CurrentStep, {}),
1392
- /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
1393
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1394
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: firstCommand.description })
1453
+ /* @__PURE__ */ jsx12(Progress, {}),
1454
+ /* @__PURE__ */ jsx12(CurrentStep, {}),
1455
+ /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: 1, children: [
1456
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1457
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: firstCommand.description })
1395
1458
  ] })
1396
1459
  ]
1397
1460
  }
@@ -1402,8 +1465,8 @@ function Ribbon() {
1402
1465
  import { useState as useState6 } from "react";
1403
1466
 
1404
1467
  // src/ui/Logs.tsx
1405
- import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
1406
- import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
1468
+ import { Box as Box15, Text as Text15, useInput as useInput6 } from "ink";
1469
+ import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
1407
1470
  var KIND_COLOR = {
1408
1471
  tool: COLORS.primary,
1409
1472
  prompt: COLORS.badge
@@ -1434,14 +1497,14 @@ function formatTimestamp(ms) {
1434
1497
  function Logs() {
1435
1498
  const logs = useWizard((s) => s.logs);
1436
1499
  const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
1437
- useInput5((_input, key) => {
1500
+ useInput6((_input, key) => {
1438
1501
  if (key.upArrow) scroll.scrollBy(-1);
1439
1502
  else if (key.downArrow) scroll.scrollBy(1);
1440
1503
  });
1441
1504
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
1442
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1443
- logs.length === 0 && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "No logs yet." }),
1444
- /* @__PURE__ */ jsx12(ScrollView, { scroll, children: visible.map((entry) => {
1505
+ return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1506
+ logs.length === 0 && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "No logs yet." }),
1507
+ /* @__PURE__ */ jsx13(ScrollView, { scroll, children: visible.map((entry) => {
1445
1508
  const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
1446
1509
  const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
1447
1510
  const rawPreview = rawInputText(entry.input);
@@ -1451,14 +1514,14 @@ function Logs() {
1451
1514
  const name = truncate2(entry.name, budget);
1452
1515
  budget -= name.length;
1453
1516
  const preview = rawPreview ? truncate2(rawPreview, budget) : "";
1454
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: ROW_GAP, children: [
1455
- /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: timestamp }),
1456
- /* @__PURE__ */ jsx12(Text14, { color: logNameColor(entry), wrap: "truncate", children: name }),
1457
- preview && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, wrap: "truncate", children: preview }),
1458
- durationText && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: durationText })
1517
+ return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", gap: ROW_GAP, children: [
1518
+ /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: timestamp }),
1519
+ /* @__PURE__ */ jsx13(Text15, { color: logNameColor(entry), wrap: "truncate", children: name }),
1520
+ preview && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, wrap: "truncate", children: preview }),
1521
+ durationText && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: durationText })
1459
1522
  ] }, entry.id);
1460
1523
  }) }),
1461
- /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1524
+ /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1462
1525
  ] });
1463
1526
  }
1464
1527
 
@@ -1650,7 +1713,7 @@ function track(event, payload) {
1650
1713
  }
1651
1714
 
1652
1715
  // src/ui/App.tsx
1653
- import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
1716
+ import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
1654
1717
  function App() {
1655
1718
  const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
1656
1719
  const { exit } = useApp();
@@ -1658,7 +1721,7 @@ function App() {
1658
1721
  const [showLogs, setShowLogs] = useState6(false);
1659
1722
  const finished = phase === "done" || phase === "error";
1660
1723
  const currentStep = steps[currentStepIndex];
1661
- useInput6(
1724
+ useInput7(
1662
1725
  (_input, key) => {
1663
1726
  if (key.return) {
1664
1727
  exit();
@@ -1666,7 +1729,7 @@ function App() {
1666
1729
  },
1667
1730
  { isActive: finished }
1668
1731
  );
1669
- useInput6((_input, key) => {
1732
+ useInput7((_input, key) => {
1670
1733
  if (phase === "idle" || phase === "authenticating") return;
1671
1734
  if (key.tab) {
1672
1735
  setShowLogs(!showLogs);
@@ -1677,8 +1740,8 @@ function App() {
1677
1740
  });
1678
1741
  }
1679
1742
  });
1680
- const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1681
- useInput6((_input, key) => {
1743
+ const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
1744
+ useInput7((_input, key) => {
1682
1745
  if (escOwnedElsewhere) return;
1683
1746
  if (key.escape) {
1684
1747
  track("AI Wizard Interaction", {
@@ -1697,8 +1760,8 @@ function App() {
1697
1760
  /* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
1698
1761
  the whole screen, and the scrolling throws off its cursor arithmetic —
1699
1762
  flicker and leftover rows. */
1700
- /* @__PURE__ */ jsxs14(
1701
- Box15,
1763
+ /* @__PURE__ */ jsxs15(
1764
+ Box16,
1702
1765
  {
1703
1766
  backgroundColor: COLORS.bg.main,
1704
1767
  flexDirection: "row",
@@ -1706,16 +1769,16 @@ function App() {
1706
1769
  height: scrollsPastViewport ? void 0 : rows,
1707
1770
  overflow: scrollsPastViewport ? "visible" : "hidden",
1708
1771
  children: [
1709
- mainWindowVisible && /* @__PURE__ */ jsxs14(
1710
- Box15,
1772
+ mainWindowVisible && /* @__PURE__ */ jsxs15(
1773
+ Box16,
1711
1774
  {
1712
1775
  flexDirection,
1713
1776
  width: "100%",
1714
1777
  maxHeight: rows,
1715
1778
  justifyContent: "space-between",
1716
1779
  children: [
1717
- showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : /* @__PURE__ */ jsxs14(
1718
- Box15,
1780
+ showLogs ? /* @__PURE__ */ jsx14(Logs, {}) : /* @__PURE__ */ jsxs15(
1781
+ Box16,
1719
1782
  {
1720
1783
  flexDirection: "column",
1721
1784
  paddingX: 4,
@@ -1723,26 +1786,26 @@ function App() {
1723
1786
  width: showSidebar ? 70 : "100%",
1724
1787
  flexGrow: 1,
1725
1788
  children: [
1726
- phase === "authenticating" && /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginBottom: 1, children: [
1727
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
1728
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
1789
+ phase === "authenticating" && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
1790
+ /* @__PURE__ */ jsx14(Text16, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
1791
+ /* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
1729
1792
  ] }),
1730
- /* @__PURE__ */ jsx13(CliOutput, {}),
1731
- /* @__PURE__ */ jsx13(Notices, {}),
1732
- /* @__PURE__ */ jsx13(PromptInput, {}),
1733
- phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1734
- phase === "error" && error && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { color: COLORS.status.error, children: [
1793
+ /* @__PURE__ */ jsx14(CliOutput, {}),
1794
+ /* @__PURE__ */ jsx14(Notices, {}),
1795
+ /* @__PURE__ */ jsx14(PromptInput, {}),
1796
+ phase === "running" && showSidebar && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx14(CurrentStep, {}) }),
1797
+ phase === "error" && error && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: COLORS.status.error, children: [
1735
1798
  "\u2716 ",
1736
1799
  error
1737
1800
  ] }) })
1738
1801
  ]
1739
1802
  }
1740
1803
  ),
1741
- showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1804
+ showSidebar ? /* @__PURE__ */ jsx14(Sidebar, {}) : /* @__PURE__ */ jsx14(Ribbon, {})
1742
1805
  ]
1743
1806
  }
1744
1807
  ),
1745
- phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1808
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx14(LearnMore, {}) : /* @__PURE__ */ jsx14(Welcome, {}))
1746
1809
  ]
1747
1810
  }
1748
1811
  )
@@ -1822,7 +1885,7 @@ async function ensureConsent() {
1822
1885
  if (config.aiConsent) return;
1823
1886
  const store = useWizard.getState();
1824
1887
  const answer = await store.requestUserInput({
1825
- prompt: "Wizard will make AI-authored changes to this repository.",
1888
+ prompt: "Wizard will make AI-authored changes to this repository, and will propose shell commands to set it up. You approve each command before it runs.",
1826
1889
  promptType: "enterToContinue",
1827
1890
  options: []
1828
1891
  });
@@ -2178,7 +2241,7 @@ async function ensureApplication() {
2178
2241
  }
2179
2242
 
2180
2243
  // src/workflows/default.ts
2181
- import { z as z28 } from "zod";
2244
+ import { z as z29 } from "zod";
2182
2245
 
2183
2246
  // src/actions/listIndices.ts
2184
2247
  import { z as z5 } from "zod";
@@ -2901,13 +2964,187 @@ function verifyImplementationTool() {
2901
2964
  });
2902
2965
  }
2903
2966
 
2967
+ // src/lib/tools/runShell.ts
2968
+ import { tool as tool9 } from "ai";
2969
+ import z16 from "zod";
2970
+
2971
+ // src/lib/tools/utils/runShell.ts
2972
+ import { spawn as spawn3 } from "node:child_process";
2973
+
2974
+ // src/lib/tools/context.ts
2975
+ var DEFAULT_TOOL_LIMITS = {
2976
+ list: 10,
2977
+ search: 10,
2978
+ read: 20,
2979
+ match: 100,
2980
+ shell: 30
2981
+ };
2982
+ var DEFAULT_SHELL_TIMEOUT_MS = 10 * 60 * 1e3;
2983
+ async function refuseByDefault() {
2984
+ return "reject";
2985
+ }
2986
+ function createShellContext(overrides = {}) {
2987
+ return {
2988
+ env: async () => ({}),
2989
+ timeoutMs: DEFAULT_SHELL_TIMEOUT_MS,
2990
+ executions: [],
2991
+ approve: refuseByDefault,
2992
+ ...overrides
2993
+ };
2994
+ }
2995
+ function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd(), shell2 = createShellContext()) {
2996
+ return {
2997
+ root: cwd,
2998
+ cwd,
2999
+ limits: { ...limits },
3000
+ counts: { list: 0, search: 0, read: 0, shell: 0 },
3001
+ shell: shell2
3002
+ };
3003
+ }
3004
+
3005
+ // src/lib/tools/utils/runShell.ts
3006
+ var SIGKILL_DELAY_MS = 5e3;
3007
+ var HEAD_CHARS = 4e3;
3008
+ var TAIL_CHARS = 8e3;
3009
+ function truncateOutput(output) {
3010
+ if (output.length <= HEAD_CHARS + TAIL_CHARS) return output;
3011
+ const omitted = output.length - HEAD_CHARS - TAIL_CHARS;
3012
+ return [
3013
+ output.slice(0, HEAD_CHARS),
3014
+ `
3015
+ \u2026 [${omitted} characters omitted] \u2026
3016
+ `,
3017
+ output.slice(-TAIL_CHARS)
3018
+ ].join("");
3019
+ }
3020
+ function runShell(command, opts) {
3021
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
3022
+ const startedAt = Date.now();
3023
+ return new Promise((resolve4) => {
3024
+ let output = "";
3025
+ let timedOut = false;
3026
+ let settled = false;
3027
+ const child = spawn3(command, {
3028
+ shell: true,
3029
+ cwd: opts.cwd,
3030
+ stdio: ["ignore", "pipe", "pipe"],
3031
+ env: { ...process.env, ...opts.env }
3032
+ });
3033
+ const finish = (exitCode) => {
3034
+ if (settled) return;
3035
+ settled = true;
3036
+ clearTimeout(timer);
3037
+ clearTimeout(killTimer);
3038
+ resolve4({
3039
+ exitCode,
3040
+ output: truncateOutput(output.trim()),
3041
+ timedOut,
3042
+ durationMs: Date.now() - startedAt
3043
+ });
3044
+ };
3045
+ let killTimer;
3046
+ const timer = setTimeout(() => {
3047
+ timedOut = true;
3048
+ output += `
3049
+ [timed out after ${timeoutMs}ms]`;
3050
+ child.kill("SIGTERM");
3051
+ killTimer = setTimeout(() => child.kill("SIGKILL"), SIGKILL_DELAY_MS);
3052
+ }, timeoutMs);
3053
+ child.stdout?.on("data", (d) => output += d);
3054
+ child.stderr?.on("data", (d) => output += d);
3055
+ child.on("error", (err) => {
3056
+ output += `Failed to run ${command}: ${err.message}`;
3057
+ finish(1);
3058
+ });
3059
+ child.on("close", (code) => finish(code ?? 1));
3060
+ });
3061
+ }
3062
+
3063
+ // src/lib/tools/runShell.ts
3064
+ var shellChain = Promise.resolve();
3065
+ function serialize(work) {
3066
+ const result = shellChain.then(work);
3067
+ shellChain = result.catch(() => {
3068
+ });
3069
+ return result;
3070
+ }
3071
+ async function approveAndRun(ctx, command, cwd, explanation) {
3072
+ const decision = await ctx.shell.approve({ command, cwd, explanation });
3073
+ if (decision === "reject") {
3074
+ ctx.shell.executions.push({ command, cwd, approved: false });
3075
+ logger.info({ command }, "runShell: user rejected the command");
3076
+ return "The user rejected this command. Do not retry it. Propose a different command, or report the limitation via reportStatus.";
3077
+ }
3078
+ useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
3079
+ const env = await ctx.shell.env().catch((err) => {
3080
+ logger.warn({ err, command }, "runShell: could not resolve command env");
3081
+ return {};
3082
+ });
3083
+ const run2 = await (ctx.shell.run ?? runShell)(command, {
3084
+ cwd,
3085
+ env,
3086
+ timeoutMs: ctx.shell.timeoutMs
3087
+ });
3088
+ logger.info(
3089
+ {
3090
+ command,
3091
+ exitCode: run2.exitCode,
3092
+ timedOut: run2.timedOut,
3093
+ durationMs: run2.durationMs
3094
+ },
3095
+ "runShell finished"
3096
+ );
3097
+ await markInteraction();
3098
+ ctx.shell.executions.push({
3099
+ command,
3100
+ cwd,
3101
+ approved: true,
3102
+ exitCode: run2.exitCode,
3103
+ output: run2.output,
3104
+ timedOut: run2.timedOut,
3105
+ durationMs: run2.durationMs
3106
+ });
3107
+ return {
3108
+ exitCode: run2.exitCode,
3109
+ timedOut: run2.timedOut,
3110
+ output: run2.output
3111
+ };
3112
+ }
3113
+ function runShellTool(ctx) {
3114
+ return tool9({
3115
+ description: "Run a shell command in the project. Use this for anything the project needs done in its own ecosystem: installing dependencies, running a script you wrote, running the project's lint/typecheck/test commands. The user sees and approves every command before it runs, so write a clear `explanation`. If the user rejects a command, do not retry it \u2014 propose a different approach.",
3116
+ inputSchema: z16.object({
3117
+ command: z16.string().describe(
3118
+ "The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
3119
+ ),
3120
+ cwd: z16.string().optional().describe(
3121
+ "Directory to run in, relative to the project root. Defaults to the project root."
3122
+ ),
3123
+ explanation: z16.string().describe(
3124
+ "One short line telling the user what this command does and why, including any side effect (e.g. writes records to Algolia). This is what they approve against."
3125
+ )
3126
+ }),
3127
+ execute: async ({ command, cwd, explanation }) => {
3128
+ if (++ctx.counts.shell > ctx.limits.shell) {
3129
+ return `Refused: command limit (${ctx.limits.shell}) reached. Stop running commands and report what you have.`;
3130
+ }
3131
+ const resolved2 = resolveInRoot(ctx, cwd ?? ".");
3132
+ if (!resolved2.ok) return resolved2.error;
3133
+ logger.info({ command, cwd: resolved2.target }, "called runShell tool");
3134
+ return serialize(
3135
+ () => approveAndRun(ctx, command, resolved2.target, explanation)
3136
+ );
3137
+ }
3138
+ });
3139
+ }
3140
+
2904
3141
  // src/lib/tools/generateRecord.ts
2905
- import { tool as tool9, generateText, Output, NoObjectGeneratedError } from "ai";
3142
+ import { tool as tool10, generateText, Output, NoObjectGeneratedError } from "ai";
2906
3143
  import { createAnthropic } from "@ai-sdk/anthropic";
2907
3144
  import { nanoid as nanoid2 } from "nanoid";
2908
3145
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
2909
3146
  import { dirname as dirname6 } from "node:path";
2910
- import z16 from "zod";
3147
+ import z17 from "zod";
2911
3148
  var DATA_DIR = ".algolia-wizard/data";
2912
3149
  var RECORD_MODEL = "claude-haiku-4-5";
2913
3150
  var MAX_RECORDS = 100;
@@ -2917,19 +3154,19 @@ var anthropic = createAnthropic({
2917
3154
  apiKey: process.env.PROVIDER_API_KEY ?? ""
2918
3155
  });
2919
3156
  function generateRecordTool(ctx) {
2920
- return tool9({
3157
+ return tool10({
2921
3158
  description: "Generate realistic sample records for an entity and write them to a JSON file in the worktree. Provide the entity name and its attributes; this tool asks a model to invent varied, realistic values, each with a unique objectID, and returns the file path to read them from at runtime. Do not invent the record values or objectIDs yourself, and do not inline the returned records into the script \u2014 call this tool and read the file it writes.",
2922
- inputSchema: z16.object({
2923
- entityName: z16.string().describe("Name of the entity to generate records for."),
2924
- attributes: z16.array(z16.string()).describe("Attribute names each record must contain."),
2925
- count: z16.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2926
- hint: z16.string().optional().describe("Optional context to steer realistic values.")
3159
+ inputSchema: z17.object({
3160
+ entityName: z17.string().describe("Name of the entity to generate records for."),
3161
+ attributes: z17.array(z17.string()).describe("Attribute names each record must contain."),
3162
+ count: z17.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
3163
+ hint: z17.string().optional().describe("Optional context to steer realistic values.")
2927
3164
  }),
2928
3165
  execute: async ({ entityName, attributes, count, hint }) => {
2929
3166
  logger.info({ entityName, count }, "called generateRecord tool");
2930
3167
  try {
2931
- const value = z16.union([z16.string(), z16.number(), z16.boolean(), z16.null()]);
2932
- const recordSchema = z16.object(
3168
+ const value = z17.union([z17.string(), z17.number(), z17.boolean(), z17.null()]);
3169
+ const recordSchema = z17.object(
2933
3170
  Object.fromEntries(attributes.map((attr) => [attr, value]))
2934
3171
  );
2935
3172
  const generateBatch = async (batchCount) => {
@@ -2939,8 +3176,8 @@ function generateRecordTool(ctx) {
2939
3176
  const { output } = await generateText({
2940
3177
  model: anthropic(RECORD_MODEL),
2941
3178
  output: Output.object({
2942
- schema: z16.object({
2943
- records: z16.array(recordSchema).length(batchCount)
3179
+ schema: z17.object({
3180
+ records: z17.array(recordSchema).length(batchCount)
2944
3181
  })
2945
3182
  }),
2946
3183
  prompt: [
@@ -2997,13 +3234,13 @@ function generateRecordTool(ctx) {
2997
3234
  }
2998
3235
 
2999
3236
  // src/lib/tools/notifyUser.ts
3000
- import { tool as tool10 } from "ai";
3001
- import z17 from "zod";
3237
+ import { tool as tool11 } from "ai";
3238
+ import z18 from "zod";
3002
3239
  function notifyUserTool() {
3003
- return tool10({
3240
+ return tool11({
3004
3241
  description: `Give the user a brief, high-level update on what you are currently doing or about to do next. This is for the big picture (e.g. "Reading through your data models", "Writing the search UI") \u2014 not granular detail like individual tool calls, which are already logged separately. Call it when you start a new phase of work or your focus shifts, just not on every step, enough to keep the user engaged. Don't say things like "starting", just describe what you are doing. Don't mention tool calls themselves, just general direction of the work.`,
3005
- inputSchema: z17.object({
3006
- message: z17.string().describe(
3242
+ inputSchema: z18.object({
3243
+ message: z18.string().describe(
3007
3244
  "Short, plain-language description of what you are doing now."
3008
3245
  )
3009
3246
  }),
@@ -3015,22 +3252,6 @@ function notifyUserTool() {
3015
3252
  });
3016
3253
  }
3017
3254
 
3018
- // src/lib/tools/context.ts
3019
- var DEFAULT_TOOL_LIMITS = {
3020
- list: 10,
3021
- search: 10,
3022
- read: 20,
3023
- match: 100
3024
- };
3025
- function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd()) {
3026
- return {
3027
- root: cwd,
3028
- cwd,
3029
- limits,
3030
- counts: { list: 0, search: 0, read: 0 }
3031
- };
3032
- }
3033
-
3034
3255
  // src/lib/tools/index.ts
3035
3256
  function withLogging(name, def) {
3036
3257
  const execute = def.execute;
@@ -3066,6 +3287,7 @@ function createTools(ctx, { output, tools }) {
3066
3287
  "verifyImplementation",
3067
3288
  verifyImplementationTool()
3068
3289
  ),
3290
+ runShell: withLogging("runShell", runShellTool(ctx)),
3069
3291
  generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
3070
3292
  notifyUser: withLogging("notifyUser", notifyUserTool())
3071
3293
  };
@@ -3100,7 +3322,7 @@ async function runAgent(req) {
3100
3322
  baseURL: PROXY_BASE_URL,
3101
3323
  fetch: proxyFetch
3102
3324
  });
3103
- const toolContext = createToolContext();
3325
+ const toolContext = req.toolContext ?? createToolContext();
3104
3326
  const readTools = ["readFile", "searchFiles", "listFiles"];
3105
3327
  const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
3106
3328
  const instructions = [
@@ -3165,7 +3387,11 @@ async function runAgent(req) {
3165
3387
  "runAgent finished"
3166
3388
  );
3167
3389
  logger.info(
3168
- { counts: toolContext.counts, limits: toolContext.limits },
3390
+ {
3391
+ counts: toolContext.counts,
3392
+ limits: toolContext.limits,
3393
+ commandsRun: toolContext.shell.executions.length
3394
+ },
3169
3395
  "tool usage"
3170
3396
  );
3171
3397
  const toolResults = await stream.toolResults;
@@ -3183,10 +3409,10 @@ async function runAgent(req) {
3183
3409
  }
3184
3410
 
3185
3411
  // src/actions/detectLanguage.ts
3186
- import z20 from "zod";
3187
- var detectLanguageSchema = z20.object({
3188
- languages: z20.array(z20.object({ name: z20.string(), version: z20.string() })),
3189
- frameworks: z20.array(z20.object({ name: z20.string(), version: z20.string() }))
3412
+ import z21 from "zod";
3413
+ var detectLanguageSchema = z21.object({
3414
+ languages: z21.array(z21.object({ name: z21.string(), version: z21.string() })),
3415
+ frameworks: z21.array(z21.object({ name: z21.string(), version: z21.string() }))
3190
3416
  });
3191
3417
  var detectLanguage = () => runAgent({
3192
3418
  instructions: [
@@ -3204,31 +3430,31 @@ var detectLanguage = () => runAgent({
3204
3430
  });
3205
3431
 
3206
3432
  // src/actions/analyzeCodebase.ts
3207
- import z21 from "zod";
3433
+ import z22 from "zod";
3208
3434
  var READONLY_TOOLS = [
3209
3435
  "listFiles",
3210
3436
  "changeDirectory",
3211
3437
  "readFile",
3212
3438
  "searchFiles"
3213
3439
  ];
3214
- var ingestionAnalysisSchema = z21.object({
3215
- ingestionAnalysis: z21.array(
3216
- z21.object({
3217
- name: z21.string(),
3218
- paths: z21.array(z21.string()),
3440
+ var ingestionAnalysisSchema = z22.object({
3441
+ ingestionAnalysis: z22.array(
3442
+ z22.object({
3443
+ name: z22.string(),
3444
+ paths: z22.array(z22.string()),
3219
3445
  // indexable fields the agent found for this entity
3220
- attributes: z21.array(z21.string())
3446
+ attributes: z22.array(z22.string())
3221
3447
  })
3222
3448
  )
3223
3449
  });
3224
- var searchImplementationAnalysisSchema = z21.object({
3225
- searchImplementationAnalysis: z21.string()
3450
+ var searchImplementationAnalysisSchema = z22.object({
3451
+ searchImplementationAnalysis: z22.string()
3226
3452
  });
3227
- var verificationSchema = z21.object({
3228
- verification: z21.array(z21.string())
3453
+ var verificationSchema = z22.object({
3454
+ verification: z22.array(z22.string())
3229
3455
  });
3230
3456
  var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
3231
- var analyzeCodebaseSchema = z21.object({
3457
+ var analyzeCodebaseSchema = z22.object({
3232
3458
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
3233
3459
  searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
3234
3460
  verification: verificationSchema.shape.verification.optional(),
@@ -3290,7 +3516,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3290
3516
  // package.json
3291
3517
  var package_default = {
3292
3518
  name: "@algolia/wizard",
3293
- version: "0.12.0-rc.104.151",
3519
+ version: "0.12.0",
3294
3520
  description: "Magically implement Algolia functionality in your codebase",
3295
3521
  type: "module",
3296
3522
  engines: {
@@ -3409,8 +3635,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
3409
3635
  }
3410
3636
 
3411
3637
  // src/actions/confirmLanguage.ts
3412
- import z23 from "zod";
3413
- var confirmLanguageSchema = z23.object({
3638
+ import z24 from "zod";
3639
+ var confirmLanguageSchema = z24.object({
3414
3640
  languages: detectLanguageSchema.shape.languages
3415
3641
  });
3416
3642
  async function confirmLanguage(ctx) {
@@ -3431,8 +3657,8 @@ async function confirmLanguage(ctx) {
3431
3657
  }
3432
3658
 
3433
3659
  // src/actions/confirmFramework.ts
3434
- import z24 from "zod";
3435
- var confirmFrameworkSchema = z24.object({
3660
+ import z25 from "zod";
3661
+ var confirmFrameworkSchema = z25.object({
3436
3662
  frameworks: detectLanguageSchema.shape.frameworks
3437
3663
  });
3438
3664
  var CURATED_FRAMEWORKS = [
@@ -3560,8 +3786,8 @@ async function promptUser(ctx, params) {
3560
3786
  }
3561
3787
 
3562
3788
  // src/actions/confirmEntities.ts
3563
- import z25 from "zod";
3564
- var confirmEntitiesSchema = z25.object({
3789
+ import z26 from "zod";
3790
+ var confirmEntitiesSchema = z26.object({
3565
3791
  // Final detection — the focused re-run may supersede project-scan's.
3566
3792
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
3567
3793
  confirmedEntities: confirmedEntitiesFieldSchema
@@ -3631,15 +3857,15 @@ async function confirmEntities(ctx) {
3631
3857
  }
3632
3858
 
3633
3859
  // src/actions/review.ts
3634
- import { z as z26 } from "zod";
3635
- var reviewSchema = z26.object({
3860
+ import { z as z27 } from "zod";
3861
+ var reviewSchema = z27.object({
3636
3862
  // Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
3637
3863
  // not one entry per workflow step — a step's raw output can be a long,
3638
3864
  // multi-paragraph blob (see implement.ts's summaries.join), and mirroring
3639
3865
  // that 1:1 is what made the old per-step summary an unreadable wall of text.
3640
- summaryPoints: z26.array(z26.string()),
3641
- reviewPrompt: z26.string(),
3642
- nextSteps: z26.array(z26.string())
3866
+ summaryPoints: z27.array(z27.string()),
3867
+ reviewPrompt: z27.string(),
3868
+ nextSteps: z27.array(z27.string())
3643
3869
  });
3644
3870
  function formatCompletedSteps(steps) {
3645
3871
  if (!steps.length) return "(no prior steps completed)";
@@ -3690,10 +3916,10 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
3690
3916
  };
3691
3917
 
3692
3918
  // src/actions/implement.ts
3693
- import z27 from "zod";
3919
+ import z28 from "zod";
3694
3920
 
3695
3921
  // src/lib/worktree.ts
3696
- import { execFile, spawn as spawn3 } from "node:child_process";
3922
+ import { execFile, spawn as spawn4 } from "node:child_process";
3697
3923
  import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3698
3924
  import {
3699
3925
  basename as basename2,
@@ -3774,7 +4000,7 @@ async function installWorktreeDeps(worktreePath) {
3774
4000
  const pm = await detectPackageManager(worktreePath);
3775
4001
  return new Promise((resolve4) => {
3776
4002
  let output = "";
3777
- const child = spawn3(pm, ["install"], {
4003
+ const child = spawn4(pm, ["install"], {
3778
4004
  cwd: worktreePath,
3779
4005
  stdio: ["ignore", "pipe", "pipe"]
3780
4006
  });
@@ -3843,7 +4069,7 @@ async function runIngestScript(worktreePath, runtime, entrypoint, env = {}) {
3843
4069
  }
3844
4070
  return new Promise((resolveRun) => {
3845
4071
  let output = "";
3846
- const child = spawn3(runtime, [entrypoint], {
4072
+ const child = spawn4(runtime, [entrypoint], {
3847
4073
  cwd: worktreePath,
3848
4074
  shell: false,
3849
4075
  stdio: ["ignore", "pipe", "pipe"],
@@ -4049,34 +4275,34 @@ function shellQuote(value) {
4049
4275
  }
4050
4276
 
4051
4277
  // src/actions/implement.ts
4052
- var implementSchema = z27.object({
4053
- filesChanged: z27.array(z27.string()),
4054
- summary: z27.string(),
4055
- worktreePath: z27.string().optional(),
4056
- ingestCommand: z27.string().optional(),
4057
- ingestScriptRan: z27.boolean().optional(),
4058
- ingestRecordCount: z27.number().optional(),
4059
- ingestDurationMs: z27.number().optional(),
4060
- ingestionSource: z27.enum(["local", "fileUpload", "generated"]),
4061
- searchEnvVars: z27.array(
4062
- z27.object({
4063
- name: z27.string(),
4064
- value: z27.string()
4278
+ var implementSchema = z28.object({
4279
+ filesChanged: z28.array(z28.string()),
4280
+ summary: z28.string(),
4281
+ worktreePath: z28.string().optional(),
4282
+ ingestCommand: z28.string().optional(),
4283
+ ingestScriptRan: z28.boolean().optional(),
4284
+ ingestRecordCount: z28.number().optional(),
4285
+ ingestDurationMs: z28.number().optional(),
4286
+ ingestionSource: z28.enum(["local", "fileUpload", "generated"]),
4287
+ searchEnvVars: z28.array(
4288
+ z28.object({
4289
+ name: z28.string(),
4290
+ value: z28.string()
4065
4291
  })
4066
4292
  ).optional()
4067
4293
  });
4068
- var implementationOutputSchema = z27.object({
4069
- summary: z27.string(),
4294
+ var implementationOutputSchema = z28.object({
4295
+ summary: z28.string(),
4070
4296
  // Ingestion only: a structured pair the wizard turns into an argv, never a
4071
4297
  // free-form command string. `runtime` is allowlisted and `entrypoint` is
4072
4298
  // validated worktree-relative, so the agent cannot inject extra commands.
4073
- runtime: z27.enum(INGEST_RUNTIMES).optional(),
4074
- entrypoint: z27.string().optional()
4299
+ runtime: z28.enum(INGEST_RUNTIMES).optional(),
4300
+ entrypoint: z28.string().optional()
4075
4301
  });
4076
- var verificationOutputSchema = z27.object({
4077
- summary: z27.string(),
4078
- sufficient: z27.boolean(),
4079
- additionalInstructions: z27.string().optional()
4302
+ var verificationOutputSchema = z28.object({
4303
+ summary: z28.string(),
4304
+ sufficient: z28.boolean(),
4305
+ additionalInstructions: z28.string().optional()
4080
4306
  });
4081
4307
  var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
4082
4308
  var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
@@ -4754,8 +4980,8 @@ var defaultWorkflow = {
4754
4980
  defineStep({
4755
4981
  id: "select-index",
4756
4982
  title: "Set up index",
4757
- outputSchema: z28.object({
4758
- selection: z28.string()
4983
+ outputSchema: z29.object({
4984
+ selection: z29.string()
4759
4985
  }),
4760
4986
  run: (ctx) => selectIndexStep(ctx)
4761
4987
  }),
@@ -5059,7 +5285,7 @@ async function resetProjectState() {
5059
5285
  }
5060
5286
 
5061
5287
  // src/main.tsx
5062
- import { jsx as jsx14 } from "react/jsx-runtime";
5288
+ import { jsx as jsx15 } from "react/jsx-runtime";
5063
5289
  async function startup() {
5064
5290
  setProjectRoot(process.cwd());
5065
5291
  let args;
@@ -5109,7 +5335,7 @@ ${formatStepList(workflow)}`);
5109
5335
  }
5110
5336
  async function run(workflow) {
5111
5337
  const store = useWizard.getState();
5112
- const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
5338
+ const instance = render(/* @__PURE__ */ jsx15(App, {}), { incrementalRendering: true });
5113
5339
  await store.waitForStart();
5114
5340
  let user = await getUser();
5115
5341
  if (!user) {