@algolia/wizard 0.11.0 → 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 +512 -281
  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 = [
@@ -1032,20 +1094,20 @@ var sidebarItems = [
1032
1094
 
1033
1095
  // src/ui/Welcome.tsx
1034
1096
  import Image, { InkPictureProvider } from "ink-picture";
1035
- import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
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
1099
  function SidebarItem({
1038
1100
  title,
1039
1101
  description
1040
1102
  }) {
1041
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
1042
- /* @__PURE__ */ jsxs7(Box8, { gap: 1, children: [
1043
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.success, children: "\u2192" }),
1044
- /* @__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 })
1045
1107
  ] }),
1046
- /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 2, children: [
1047
- /* @__PURE__ */ jsx6(Spacer, {}),
1048
- /* @__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 })
1049
1111
  ] })
1050
1112
  ] });
1051
1113
  }
@@ -1053,7 +1115,7 @@ function Welcome() {
1053
1115
  const confirmStart = useWizard((s) => s.confirmStart);
1054
1116
  const openLearnMore = useWizard((s) => s.openLearnMore);
1055
1117
  const { rows } = useWindowSize6();
1056
- useInput3((input, key) => {
1118
+ useInput4((input, key) => {
1057
1119
  if (key.return) confirmStart();
1058
1120
  else if (input === "i") openLearnMore();
1059
1121
  });
@@ -1071,16 +1133,16 @@ function Welcome() {
1071
1133
  if (rows < 30) {
1072
1134
  layout = scales["small"];
1073
1135
  }
1074
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
1075
- /* @__PURE__ */ jsx6(
1076
- Box8,
1136
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
1137
+ /* @__PURE__ */ jsx7(
1138
+ Box9,
1077
1139
  {
1078
1140
  paddingY: layout.main.padding.y,
1079
1141
  paddingX: layout.main.padding.x,
1080
1142
  flexDirection: "column",
1081
1143
  justifyContent: "center",
1082
- children: /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 2, children: [
1083
- /* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
1144
+ children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 2, children: [
1145
+ /* @__PURE__ */ jsx7(InkPictureProvider, { children: /* @__PURE__ */ jsx7(
1084
1146
  Image,
1085
1147
  {
1086
1148
  src: IMAGE_PATH,
@@ -1091,16 +1153,16 @@ function Welcome() {
1091
1153
  protocol: "halfBlock"
1092
1154
  }
1093
1155
  ) }),
1094
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
1095
- /* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
1096
- /* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
1097
- /* @__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" })
1098
1160
  ] })
1099
1161
  ] })
1100
1162
  }
1101
1163
  ),
1102
- /* @__PURE__ */ jsxs7(
1103
- Box8,
1164
+ /* @__PURE__ */ jsxs8(
1165
+ Box9,
1104
1166
  {
1105
1167
  backgroundColor: COLORS.bg.sidebar,
1106
1168
  width: 40,
@@ -1110,8 +1172,8 @@ function Welcome() {
1110
1172
  flexDirection: "column",
1111
1173
  justifyContent: "center",
1112
1174
  children: [
1113
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
1114
- 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))
1115
1177
  ]
1116
1178
  }
1117
1179
  )
@@ -1120,7 +1182,7 @@ function Welcome() {
1120
1182
 
1121
1183
  // src/ui/LearnMore.tsx
1122
1184
  import { Fragment as Fragment2 } from "react";
1123
- 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";
1124
1186
 
1125
1187
  // src/ui/copy/learn-more.ts
1126
1188
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -1128,12 +1190,17 @@ var accessItems = [
1128
1190
  {
1129
1191
  tag: "READ",
1130
1192
  title: "Project files",
1131
- 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."
1132
1194
  },
1133
1195
  {
1134
1196
  tag: "WRITE",
1135
1197
  title: "Code changes",
1136
- 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."
1137
1204
  },
1138
1205
  {
1139
1206
  tag: "NET",
@@ -1143,13 +1210,13 @@ var accessItems = [
1143
1210
  {
1144
1211
  tag: "KEY",
1145
1212
  title: "Credentials",
1146
- 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."
1147
1214
  }
1148
1215
  ];
1149
1216
  var neverItems = [
1150
1217
  "Send your source code to a model or third party",
1151
1218
  "Commit or push to git",
1152
- "Touch files outside your project directory"
1219
+ "Run a command you haven't approved"
1153
1220
  ];
1154
1221
  var policyLinks = [
1155
1222
  { label: "Terms", url: "https://www.algolia.com/policies/terms" },
@@ -1157,10 +1224,11 @@ var policyLinks = [
1157
1224
  ];
1158
1225
 
1159
1226
  // src/ui/LearnMore.tsx
1160
- import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
1227
+ import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1161
1228
  var TAG_COLORS = {
1162
1229
  READ: COLORS.success,
1163
1230
  WRITE: COLORS.badge,
1231
+ EXEC: COLORS.danger,
1164
1232
  NET: COLORS.accent,
1165
1233
  KEY: COLORS.muted
1166
1234
  };
@@ -1173,12 +1241,12 @@ function NeverLine({
1173
1241
  }) {
1174
1242
  const used = segments.reduce((n, s) => n + s.text.length, 0);
1175
1243
  const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
1176
- return /* @__PURE__ */ jsxs8(Text9, { children: [
1177
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" }),
1244
+ return /* @__PURE__ */ jsxs9(Text10, { children: [
1245
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" }),
1178
1246
  " ".repeat(NEVER_BOX_PAD_X),
1179
- 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)),
1180
1248
  " ".repeat(rightPad),
1181
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" })
1249
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" })
1182
1250
  ] });
1183
1251
  }
1184
1252
  function LearnMore() {
@@ -1186,12 +1254,12 @@ function LearnMore() {
1186
1254
  const backToHome = useWizard((s) => s.backToHome);
1187
1255
  const { columns } = useWindowSize7();
1188
1256
  const dividerWidth = Math.max(0, columns - PADDING_X * 2);
1189
- useInput4((_input, key) => {
1257
+ useInput5((_input, key) => {
1190
1258
  if (key.escape) backToHome();
1191
1259
  else if (key.return) confirmStart();
1192
1260
  });
1193
- return /* @__PURE__ */ jsxs8(
1194
- Box9,
1261
+ return /* @__PURE__ */ jsxs9(
1262
+ Box10,
1195
1263
  {
1196
1264
  flexDirection: "column",
1197
1265
  paddingX: PADDING_X,
@@ -1199,31 +1267,31 @@ function LearnMore() {
1199
1267
  width: "100%",
1200
1268
  gap: 1,
1201
1269
  children: [
1202
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
1203
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: accessIntro }),
1204
- /* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, children: [
1205
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
1206
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, marginTop: 1, children: [
1207
- /* @__PURE__ */ jsx7(Box9, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text9, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
1208
- /* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { children: [
1209
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: item.title }),
1210
- /* @__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}` })
1211
1279
  ] }) })
1212
1280
  ] })
1213
1281
  ] }, item.tag)) }),
1214
- /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
1215
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
1216
- /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1217
- /* @__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(
1218
1286
  NeverLine,
1219
1287
  {
1220
1288
  width: dividerWidth,
1221
1289
  segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
1222
1290
  }
1223
1291
  ),
1224
- neverItems.map((item) => /* @__PURE__ */ jsxs8(Fragment2, { children: [
1225
- /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1226
- /* @__PURE__ */ jsx7(
1292
+ neverItems.map((item) => /* @__PURE__ */ jsxs9(Fragment2, { children: [
1293
+ /* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
1294
+ /* @__PURE__ */ jsx8(
1227
1295
  NeverLine,
1228
1296
  {
1229
1297
  width: dividerWidth,
@@ -1235,24 +1303,24 @@ function LearnMore() {
1235
1303
  }
1236
1304
  )
1237
1305
  ] }, item)),
1238
- /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1239
- /* @__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` })
1240
1308
  ] }),
1241
- /* @__PURE__ */ jsx7(Box9, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1242
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1243
- /* @__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 })
1244
1312
  ] }, link.label)) }),
1245
- /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1246
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1247
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
1248
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "esc" }),
1249
- /* @__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" })
1250
1318
  ] }),
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: "enter" }),
1254
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "]" }),
1255
- /* @__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" })
1256
1324
  ] })
1257
1325
  ] })
1258
1326
  ]
@@ -1261,10 +1329,10 @@ function LearnMore() {
1261
1329
  }
1262
1330
 
1263
1331
  // src/ui/Sidebar.tsx
1264
- import { Box as Box12, Text as Text12 } from "ink";
1332
+ import { Box as Box13, Text as Text13 } from "ink";
1265
1333
 
1266
1334
  // src/ui/Steps.tsx
1267
- import { Box as Box10, Text as Text10 } from "ink";
1335
+ import { Box as Box11, Text as Text11 } from "ink";
1268
1336
  import Spinner from "ink-spinner";
1269
1337
 
1270
1338
  // src/core/persistence.ts
@@ -1293,12 +1361,12 @@ async function clearWorkflowState(workflowId) {
1293
1361
  }
1294
1362
 
1295
1363
  // src/ui/Steps.tsx
1296
- import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1364
+ import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
1297
1365
  function Steps() {
1298
1366
  const { steps } = useWizard();
1299
1367
  const visibleSteps = steps.filter(isStepVisible);
1300
- 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: [
1301
- 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],
1302
1370
  " ",
1303
1371
  s.title
1304
1372
  ] }) }, s.id)) });
@@ -1307,27 +1375,27 @@ function CurrentStep() {
1307
1375
  const { steps } = useWizard();
1308
1376
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
1309
1377
  if (!currentStep) return null;
1310
- return /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status.running, children: [
1311
- /* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
1378
+ return /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status.running, children: [
1379
+ /* @__PURE__ */ jsx9(Spinner, { type: "dots" }),
1312
1380
  " ",
1313
1381
  ` ${currentStep.title}`
1314
1382
  ] });
1315
1383
  }
1316
1384
 
1317
1385
  // src/ui/Progress.tsx
1318
- import { Box as Box11, Text as Text11 } from "ink";
1319
- 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";
1320
1388
  function Progress() {
1321
1389
  const { steps, currentStepIndex } = useWizard();
1322
1390
  const visibleSteps = steps.filter(isStepVisible);
1323
1391
  if (visibleSteps.length === 0) return null;
1324
1392
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
1325
1393
  const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
1326
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
1327
- /* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "STEP" }),
1328
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: activeStepNumber }),
1329
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: "/" }),
1330
- /* @__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 })
1331
1399
  ] });
1332
1400
  }
1333
1401
 
@@ -1338,10 +1406,10 @@ var sidebarCommands = [
1338
1406
  ];
1339
1407
 
1340
1408
  // src/ui/Sidebar.tsx
1341
- import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
1409
+ import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
1342
1410
  function Sidebar() {
1343
- return /* @__PURE__ */ jsxs11(
1344
- Box12,
1411
+ return /* @__PURE__ */ jsxs12(
1412
+ Box13,
1345
1413
  {
1346
1414
  backgroundColor: "#14171E",
1347
1415
  width: 30,
@@ -1350,16 +1418,16 @@ function Sidebar() {
1350
1418
  flexDirection: "column",
1351
1419
  justifyContent: "space-between",
1352
1420
  children: [
1353
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
1354
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "PROGRESS" }),
1355
- /* @__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, {})
1356
1424
  ] }),
1357
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
1358
- /* @__PURE__ */ jsx10(Progress, {}),
1359
- /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: sidebarCommands.map((c) => {
1360
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
1361
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1362
- /* @__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 })
1363
1431
  ] });
1364
1432
  }) })
1365
1433
  ] })
@@ -1369,12 +1437,12 @@ function Sidebar() {
1369
1437
  }
1370
1438
 
1371
1439
  // src/ui/Ribbon.tsx
1372
- import { Box as Box13, Text as Text13 } from "ink";
1373
- 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";
1374
1442
  function Ribbon() {
1375
1443
  const firstCommand = sidebarCommands[0];
1376
- return /* @__PURE__ */ jsxs12(
1377
- Box13,
1444
+ return /* @__PURE__ */ jsxs13(
1445
+ Box14,
1378
1446
  {
1379
1447
  backgroundColor: "#14171E",
1380
1448
  flexDirection: "row",
@@ -1382,11 +1450,11 @@ function Ribbon() {
1382
1450
  paddingX: 2,
1383
1451
  paddingY: 1,
1384
1452
  children: [
1385
- /* @__PURE__ */ jsx11(Progress, {}),
1386
- /* @__PURE__ */ jsx11(CurrentStep, {}),
1387
- /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
1388
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1389
- /* @__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 })
1390
1458
  ] })
1391
1459
  ]
1392
1460
  }
@@ -1397,8 +1465,8 @@ function Ribbon() {
1397
1465
  import { useState as useState6 } from "react";
1398
1466
 
1399
1467
  // src/ui/Logs.tsx
1400
- import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
1401
- 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";
1402
1470
  var KIND_COLOR = {
1403
1471
  tool: COLORS.primary,
1404
1472
  prompt: COLORS.badge
@@ -1429,14 +1497,14 @@ function formatTimestamp(ms) {
1429
1497
  function Logs() {
1430
1498
  const logs = useWizard((s) => s.logs);
1431
1499
  const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
1432
- useInput5((_input, key) => {
1500
+ useInput6((_input, key) => {
1433
1501
  if (key.upArrow) scroll.scrollBy(-1);
1434
1502
  else if (key.downArrow) scroll.scrollBy(1);
1435
1503
  });
1436
1504
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
1437
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1438
- logs.length === 0 && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "No logs yet." }),
1439
- /* @__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) => {
1440
1508
  const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
1441
1509
  const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
1442
1510
  const rawPreview = rawInputText(entry.input);
@@ -1446,14 +1514,14 @@ function Logs() {
1446
1514
  const name = truncate2(entry.name, budget);
1447
1515
  budget -= name.length;
1448
1516
  const preview = rawPreview ? truncate2(rawPreview, budget) : "";
1449
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: ROW_GAP, children: [
1450
- /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: timestamp }),
1451
- /* @__PURE__ */ jsx12(Text14, { color: logNameColor(entry), wrap: "truncate", children: name }),
1452
- preview && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, wrap: "truncate", children: preview }),
1453
- 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 })
1454
1522
  ] }, entry.id);
1455
1523
  }) }),
1456
- /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1524
+ /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1457
1525
  ] });
1458
1526
  }
1459
1527
 
@@ -1645,7 +1713,7 @@ function track(event, payload) {
1645
1713
  }
1646
1714
 
1647
1715
  // src/ui/App.tsx
1648
- import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
1716
+ import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
1649
1717
  function App() {
1650
1718
  const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
1651
1719
  const { exit } = useApp();
@@ -1653,7 +1721,7 @@ function App() {
1653
1721
  const [showLogs, setShowLogs] = useState6(false);
1654
1722
  const finished = phase === "done" || phase === "error";
1655
1723
  const currentStep = steps[currentStepIndex];
1656
- useInput6(
1724
+ useInput7(
1657
1725
  (_input, key) => {
1658
1726
  if (key.return) {
1659
1727
  exit();
@@ -1661,7 +1729,7 @@ function App() {
1661
1729
  },
1662
1730
  { isActive: finished }
1663
1731
  );
1664
- useInput6((_input, key) => {
1732
+ useInput7((_input, key) => {
1665
1733
  if (phase === "idle" || phase === "authenticating") return;
1666
1734
  if (key.tab) {
1667
1735
  setShowLogs(!showLogs);
@@ -1672,8 +1740,8 @@ function App() {
1672
1740
  });
1673
1741
  }
1674
1742
  });
1675
- const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1676
- useInput6((_input, key) => {
1743
+ const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
1744
+ useInput7((_input, key) => {
1677
1745
  if (escOwnedElsewhere) return;
1678
1746
  if (key.escape) {
1679
1747
  track("AI Wizard Interaction", {
@@ -1692,8 +1760,8 @@ function App() {
1692
1760
  /* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
1693
1761
  the whole screen, and the scrolling throws off its cursor arithmetic —
1694
1762
  flicker and leftover rows. */
1695
- /* @__PURE__ */ jsxs14(
1696
- Box15,
1763
+ /* @__PURE__ */ jsxs15(
1764
+ Box16,
1697
1765
  {
1698
1766
  backgroundColor: COLORS.bg.main,
1699
1767
  flexDirection: "row",
@@ -1701,16 +1769,16 @@ function App() {
1701
1769
  height: scrollsPastViewport ? void 0 : rows,
1702
1770
  overflow: scrollsPastViewport ? "visible" : "hidden",
1703
1771
  children: [
1704
- mainWindowVisible && /* @__PURE__ */ jsxs14(
1705
- Box15,
1772
+ mainWindowVisible && /* @__PURE__ */ jsxs15(
1773
+ Box16,
1706
1774
  {
1707
1775
  flexDirection,
1708
1776
  width: "100%",
1709
1777
  maxHeight: rows,
1710
1778
  justifyContent: "space-between",
1711
1779
  children: [
1712
- showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : /* @__PURE__ */ jsxs14(
1713
- Box15,
1780
+ showLogs ? /* @__PURE__ */ jsx14(Logs, {}) : /* @__PURE__ */ jsxs15(
1781
+ Box16,
1714
1782
  {
1715
1783
  flexDirection: "column",
1716
1784
  paddingX: 4,
@@ -1718,26 +1786,26 @@ function App() {
1718
1786
  width: showSidebar ? 70 : "100%",
1719
1787
  flexGrow: 1,
1720
1788
  children: [
1721
- phase === "authenticating" && /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginBottom: 1, children: [
1722
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
1723
- /* @__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." })
1724
1792
  ] }),
1725
- /* @__PURE__ */ jsx13(CliOutput, {}),
1726
- /* @__PURE__ */ jsx13(Notices, {}),
1727
- /* @__PURE__ */ jsx13(PromptInput, {}),
1728
- phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1729
- 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: [
1730
1798
  "\u2716 ",
1731
1799
  error
1732
1800
  ] }) })
1733
1801
  ]
1734
1802
  }
1735
1803
  ),
1736
- showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1804
+ showSidebar ? /* @__PURE__ */ jsx14(Sidebar, {}) : /* @__PURE__ */ jsx14(Ribbon, {})
1737
1805
  ]
1738
1806
  }
1739
1807
  ),
1740
- phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1808
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx14(LearnMore, {}) : /* @__PURE__ */ jsx14(Welcome, {}))
1741
1809
  ]
1742
1810
  }
1743
1811
  )
@@ -1817,7 +1885,7 @@ async function ensureConsent() {
1817
1885
  if (config.aiConsent) return;
1818
1886
  const store = useWizard.getState();
1819
1887
  const answer = await store.requestUserInput({
1820
- 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.",
1821
1889
  promptType: "enterToContinue",
1822
1890
  options: []
1823
1891
  });
@@ -2173,7 +2241,7 @@ async function ensureApplication() {
2173
2241
  }
2174
2242
 
2175
2243
  // src/workflows/default.ts
2176
- import { z as z28 } from "zod";
2244
+ import { z as z29 } from "zod";
2177
2245
 
2178
2246
  // src/actions/listIndices.ts
2179
2247
  import { z as z5 } from "zod";
@@ -2896,13 +2964,187 @@ function verifyImplementationTool() {
2896
2964
  });
2897
2965
  }
2898
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
+
2899
3141
  // src/lib/tools/generateRecord.ts
2900
- import { tool as tool9, generateText, Output, NoObjectGeneratedError } from "ai";
3142
+ import { tool as tool10, generateText, Output, NoObjectGeneratedError } from "ai";
2901
3143
  import { createAnthropic } from "@ai-sdk/anthropic";
2902
3144
  import { nanoid as nanoid2 } from "nanoid";
2903
3145
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
2904
3146
  import { dirname as dirname6 } from "node:path";
2905
- import z16 from "zod";
3147
+ import z17 from "zod";
2906
3148
  var DATA_DIR = ".algolia-wizard/data";
2907
3149
  var RECORD_MODEL = "claude-haiku-4-5";
2908
3150
  var MAX_RECORDS = 100;
@@ -2912,19 +3154,19 @@ var anthropic = createAnthropic({
2912
3154
  apiKey: process.env.PROVIDER_API_KEY ?? ""
2913
3155
  });
2914
3156
  function generateRecordTool(ctx) {
2915
- return tool9({
3157
+ return tool10({
2916
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.",
2917
- inputSchema: z16.object({
2918
- entityName: z16.string().describe("Name of the entity to generate records for."),
2919
- attributes: z16.array(z16.string()).describe("Attribute names each record must contain."),
2920
- count: z16.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2921
- 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.")
2922
3164
  }),
2923
3165
  execute: async ({ entityName, attributes, count, hint }) => {
2924
3166
  logger.info({ entityName, count }, "called generateRecord tool");
2925
3167
  try {
2926
- const value = z16.union([z16.string(), z16.number(), z16.boolean(), z16.null()]);
2927
- const recordSchema = z16.object(
3168
+ const value = z17.union([z17.string(), z17.number(), z17.boolean(), z17.null()]);
3169
+ const recordSchema = z17.object(
2928
3170
  Object.fromEntries(attributes.map((attr) => [attr, value]))
2929
3171
  );
2930
3172
  const generateBatch = async (batchCount) => {
@@ -2934,8 +3176,8 @@ function generateRecordTool(ctx) {
2934
3176
  const { output } = await generateText({
2935
3177
  model: anthropic(RECORD_MODEL),
2936
3178
  output: Output.object({
2937
- schema: z16.object({
2938
- records: z16.array(recordSchema).length(batchCount)
3179
+ schema: z17.object({
3180
+ records: z17.array(recordSchema).length(batchCount)
2939
3181
  })
2940
3182
  }),
2941
3183
  prompt: [
@@ -2992,13 +3234,13 @@ function generateRecordTool(ctx) {
2992
3234
  }
2993
3235
 
2994
3236
  // src/lib/tools/notifyUser.ts
2995
- import { tool as tool10 } from "ai";
2996
- import z17 from "zod";
3237
+ import { tool as tool11 } from "ai";
3238
+ import z18 from "zod";
2997
3239
  function notifyUserTool() {
2998
- return tool10({
3240
+ return tool11({
2999
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.`,
3000
- inputSchema: z17.object({
3001
- message: z17.string().describe(
3242
+ inputSchema: z18.object({
3243
+ message: z18.string().describe(
3002
3244
  "Short, plain-language description of what you are doing now."
3003
3245
  )
3004
3246
  }),
@@ -3010,22 +3252,6 @@ function notifyUserTool() {
3010
3252
  });
3011
3253
  }
3012
3254
 
3013
- // src/lib/tools/context.ts
3014
- var DEFAULT_TOOL_LIMITS = {
3015
- list: 10,
3016
- search: 10,
3017
- read: 20,
3018
- match: 100
3019
- };
3020
- function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd()) {
3021
- return {
3022
- root: cwd,
3023
- cwd,
3024
- limits,
3025
- counts: { list: 0, search: 0, read: 0 }
3026
- };
3027
- }
3028
-
3029
3255
  // src/lib/tools/index.ts
3030
3256
  function withLogging(name, def) {
3031
3257
  const execute = def.execute;
@@ -3061,6 +3287,7 @@ function createTools(ctx, { output, tools }) {
3061
3287
  "verifyImplementation",
3062
3288
  verifyImplementationTool()
3063
3289
  ),
3290
+ runShell: withLogging("runShell", runShellTool(ctx)),
3064
3291
  generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
3065
3292
  notifyUser: withLogging("notifyUser", notifyUserTool())
3066
3293
  };
@@ -3095,7 +3322,7 @@ async function runAgent(req) {
3095
3322
  baseURL: PROXY_BASE_URL,
3096
3323
  fetch: proxyFetch
3097
3324
  });
3098
- const toolContext = createToolContext();
3325
+ const toolContext = req.toolContext ?? createToolContext();
3099
3326
  const readTools = ["readFile", "searchFiles", "listFiles"];
3100
3327
  const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
3101
3328
  const instructions = [
@@ -3160,7 +3387,11 @@ async function runAgent(req) {
3160
3387
  "runAgent finished"
3161
3388
  );
3162
3389
  logger.info(
3163
- { counts: toolContext.counts, limits: toolContext.limits },
3390
+ {
3391
+ counts: toolContext.counts,
3392
+ limits: toolContext.limits,
3393
+ commandsRun: toolContext.shell.executions.length
3394
+ },
3164
3395
  "tool usage"
3165
3396
  );
3166
3397
  const toolResults = await stream.toolResults;
@@ -3178,10 +3409,10 @@ async function runAgent(req) {
3178
3409
  }
3179
3410
 
3180
3411
  // src/actions/detectLanguage.ts
3181
- import z20 from "zod";
3182
- var detectLanguageSchema = z20.object({
3183
- languages: z20.array(z20.object({ name: z20.string(), version: z20.string() })),
3184
- 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() }))
3185
3416
  });
3186
3417
  var detectLanguage = () => runAgent({
3187
3418
  instructions: [
@@ -3199,31 +3430,31 @@ var detectLanguage = () => runAgent({
3199
3430
  });
3200
3431
 
3201
3432
  // src/actions/analyzeCodebase.ts
3202
- import z21 from "zod";
3433
+ import z22 from "zod";
3203
3434
  var READONLY_TOOLS = [
3204
3435
  "listFiles",
3205
3436
  "changeDirectory",
3206
3437
  "readFile",
3207
3438
  "searchFiles"
3208
3439
  ];
3209
- var ingestionAnalysisSchema = z21.object({
3210
- ingestionAnalysis: z21.array(
3211
- z21.object({
3212
- name: z21.string(),
3213
- 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()),
3214
3445
  // indexable fields the agent found for this entity
3215
- attributes: z21.array(z21.string())
3446
+ attributes: z22.array(z22.string())
3216
3447
  })
3217
3448
  )
3218
3449
  });
3219
- var searchImplementationAnalysisSchema = z21.object({
3220
- searchImplementationAnalysis: z21.string()
3450
+ var searchImplementationAnalysisSchema = z22.object({
3451
+ searchImplementationAnalysis: z22.string()
3221
3452
  });
3222
- var verificationSchema = z21.object({
3223
- verification: z21.array(z21.string())
3453
+ var verificationSchema = z22.object({
3454
+ verification: z22.array(z22.string())
3224
3455
  });
3225
3456
  var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
3226
- var analyzeCodebaseSchema = z21.object({
3457
+ var analyzeCodebaseSchema = z22.object({
3227
3458
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
3228
3459
  searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
3229
3460
  verification: verificationSchema.shape.verification.optional(),
@@ -3285,7 +3516,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3285
3516
  // package.json
3286
3517
  var package_default = {
3287
3518
  name: "@algolia/wizard",
3288
- version: "0.11.0",
3519
+ version: "0.12.0",
3289
3520
  description: "Magically implement Algolia functionality in your codebase",
3290
3521
  type: "module",
3291
3522
  engines: {
@@ -3404,8 +3635,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
3404
3635
  }
3405
3636
 
3406
3637
  // src/actions/confirmLanguage.ts
3407
- import z23 from "zod";
3408
- var confirmLanguageSchema = z23.object({
3638
+ import z24 from "zod";
3639
+ var confirmLanguageSchema = z24.object({
3409
3640
  languages: detectLanguageSchema.shape.languages
3410
3641
  });
3411
3642
  async function confirmLanguage(ctx) {
@@ -3426,8 +3657,8 @@ async function confirmLanguage(ctx) {
3426
3657
  }
3427
3658
 
3428
3659
  // src/actions/confirmFramework.ts
3429
- import z24 from "zod";
3430
- var confirmFrameworkSchema = z24.object({
3660
+ import z25 from "zod";
3661
+ var confirmFrameworkSchema = z25.object({
3431
3662
  frameworks: detectLanguageSchema.shape.frameworks
3432
3663
  });
3433
3664
  var CURATED_FRAMEWORKS = [
@@ -3555,8 +3786,8 @@ async function promptUser(ctx, params) {
3555
3786
  }
3556
3787
 
3557
3788
  // src/actions/confirmEntities.ts
3558
- import z25 from "zod";
3559
- var confirmEntitiesSchema = z25.object({
3789
+ import z26 from "zod";
3790
+ var confirmEntitiesSchema = z26.object({
3560
3791
  // Final detection — the focused re-run may supersede project-scan's.
3561
3792
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
3562
3793
  confirmedEntities: confirmedEntitiesFieldSchema
@@ -3626,15 +3857,15 @@ async function confirmEntities(ctx) {
3626
3857
  }
3627
3858
 
3628
3859
  // src/actions/review.ts
3629
- import { z as z26 } from "zod";
3630
- var reviewSchema = z26.object({
3860
+ import { z as z27 } from "zod";
3861
+ var reviewSchema = z27.object({
3631
3862
  // Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
3632
3863
  // not one entry per workflow step — a step's raw output can be a long,
3633
3864
  // multi-paragraph blob (see implement.ts's summaries.join), and mirroring
3634
3865
  // that 1:1 is what made the old per-step summary an unreadable wall of text.
3635
- summaryPoints: z26.array(z26.string()),
3636
- reviewPrompt: z26.string(),
3637
- nextSteps: z26.array(z26.string())
3866
+ summaryPoints: z27.array(z27.string()),
3867
+ reviewPrompt: z27.string(),
3868
+ nextSteps: z27.array(z27.string())
3638
3869
  });
3639
3870
  function formatCompletedSteps(steps) {
3640
3871
  if (!steps.length) return "(no prior steps completed)";
@@ -3685,10 +3916,10 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
3685
3916
  };
3686
3917
 
3687
3918
  // src/actions/implement.ts
3688
- import z27 from "zod";
3919
+ import z28 from "zod";
3689
3920
 
3690
3921
  // src/lib/worktree.ts
3691
- import { execFile, spawn as spawn3 } from "node:child_process";
3922
+ import { execFile, spawn as spawn4 } from "node:child_process";
3692
3923
  import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3693
3924
  import {
3694
3925
  basename as basename2,
@@ -3769,7 +4000,7 @@ async function installWorktreeDeps(worktreePath) {
3769
4000
  const pm = await detectPackageManager(worktreePath);
3770
4001
  return new Promise((resolve4) => {
3771
4002
  let output = "";
3772
- const child = spawn3(pm, ["install"], {
4003
+ const child = spawn4(pm, ["install"], {
3773
4004
  cwd: worktreePath,
3774
4005
  stdio: ["ignore", "pipe", "pipe"]
3775
4006
  });
@@ -3838,7 +4069,7 @@ async function runIngestScript(worktreePath, runtime, entrypoint, env = {}) {
3838
4069
  }
3839
4070
  return new Promise((resolveRun) => {
3840
4071
  let output = "";
3841
- const child = spawn3(runtime, [entrypoint], {
4072
+ const child = spawn4(runtime, [entrypoint], {
3842
4073
  cwd: worktreePath,
3843
4074
  shell: false,
3844
4075
  stdio: ["ignore", "pipe", "pipe"],
@@ -4044,34 +4275,34 @@ function shellQuote(value) {
4044
4275
  }
4045
4276
 
4046
4277
  // src/actions/implement.ts
4047
- var implementSchema = z27.object({
4048
- filesChanged: z27.array(z27.string()),
4049
- summary: z27.string(),
4050
- worktreePath: z27.string().optional(),
4051
- ingestCommand: z27.string().optional(),
4052
- ingestScriptRan: z27.boolean().optional(),
4053
- ingestRecordCount: z27.number().optional(),
4054
- ingestDurationMs: z27.number().optional(),
4055
- ingestionSource: z27.enum(["local", "fileUpload", "generated"]),
4056
- searchEnvVars: z27.array(
4057
- z27.object({
4058
- name: z27.string(),
4059
- 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()
4060
4291
  })
4061
4292
  ).optional()
4062
4293
  });
4063
- var implementationOutputSchema = z27.object({
4064
- summary: z27.string(),
4294
+ var implementationOutputSchema = z28.object({
4295
+ summary: z28.string(),
4065
4296
  // Ingestion only: a structured pair the wizard turns into an argv, never a
4066
4297
  // free-form command string. `runtime` is allowlisted and `entrypoint` is
4067
4298
  // validated worktree-relative, so the agent cannot inject extra commands.
4068
- runtime: z27.enum(INGEST_RUNTIMES).optional(),
4069
- entrypoint: z27.string().optional()
4299
+ runtime: z28.enum(INGEST_RUNTIMES).optional(),
4300
+ entrypoint: z28.string().optional()
4070
4301
  });
4071
- var verificationOutputSchema = z27.object({
4072
- summary: z27.string(),
4073
- sufficient: z27.boolean(),
4074
- additionalInstructions: z27.string().optional()
4302
+ var verificationOutputSchema = z28.object({
4303
+ summary: z28.string(),
4304
+ sufficient: z28.boolean(),
4305
+ additionalInstructions: z28.string().optional()
4075
4306
  });
4076
4307
  var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
4077
4308
  var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
@@ -4749,8 +4980,8 @@ var defaultWorkflow = {
4749
4980
  defineStep({
4750
4981
  id: "select-index",
4751
4982
  title: "Set up index",
4752
- outputSchema: z28.object({
4753
- selection: z28.string()
4983
+ outputSchema: z29.object({
4984
+ selection: z29.string()
4754
4985
  }),
4755
4986
  run: (ctx) => selectIndexStep(ctx)
4756
4987
  }),
@@ -5054,7 +5285,7 @@ async function resetProjectState() {
5054
5285
  }
5055
5286
 
5056
5287
  // src/main.tsx
5057
- import { jsx as jsx14 } from "react/jsx-runtime";
5288
+ import { jsx as jsx15 } from "react/jsx-runtime";
5058
5289
  async function startup() {
5059
5290
  setProjectRoot(process.cwd());
5060
5291
  let args;
@@ -5104,7 +5335,7 @@ ${formatStepList(workflow)}`);
5104
5335
  }
5105
5336
  async function run(workflow) {
5106
5337
  const store = useWizard.getState();
5107
- const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
5338
+ const instance = render(/* @__PURE__ */ jsx15(App, {}), { incrementalRendering: true });
5108
5339
  await store.waitForStart();
5109
5340
  let user = await getUser();
5110
5341
  if (!user) {