@algolia/wizard 0.61.0 → 0.63.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 +682 -433
  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 Box20, Text as Text21, useApp, useInput as useInput8, useWindowSize as useWindowSize7 } from "ink";
7
+ import { Box as Box20, Text as Text21, useApp, useInput as useInput9, useWindowSize as useWindowSize7 } from "ink";
8
8
  import Spinner3 from "ink-spinner";
9
9
 
10
10
  // src/core/store.ts
@@ -651,12 +651,13 @@ function Notices({
651
651
  }
652
652
 
653
653
  // src/ui/PromptInput.tsx
654
- import { Box as Box9, Text as Text10 } from "ink";
654
+ import { Box as Box10, Text as Text11 } from "ink";
655
655
  import Spinner from "ink-spinner";
656
- import { useState as useState7 } from "react";
656
+ import { useState as useState9 } from "react";
657
657
 
658
658
  // src/ui/CommandApproval.tsx
659
- import { Box as Box6, Text as Text6 } from "ink";
659
+ import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
660
+ import { useLayoutEffect as useLayoutEffect3, useState as useState6 } from "react";
660
661
 
661
662
  // src/ui/DecisionSelect.tsx
662
663
  import { Box as Box5, Text as Text5, useInput } from "ink";
@@ -707,13 +708,14 @@ function SelectRow({
707
708
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
708
709
  function DecisionSelect({
709
710
  actions,
710
- onDecide
711
+ onDecide,
712
+ extraHints = []
711
713
  }) {
712
714
  const [index, setIndex] = useState3(0);
713
- useInput((input, key) => {
714
- if (key.upArrow || input === "k") {
715
+ useInput((_input, key) => {
716
+ if (key.upArrow) {
715
717
  setIndex((i) => (i - 1 + actions.length) % actions.length);
716
- } else if (key.downArrow || input === "j") {
718
+ } else if (key.downArrow) {
717
719
  setIndex((i) => (i + 1) % actions.length);
718
720
  } else if (key.return) {
719
721
  onDecide(actions[index].value);
@@ -732,6 +734,13 @@ function DecisionSelect({
732
734
  action.label
733
735
  )) }),
734
736
  /* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
737
+ extraHints.map(({ key, label }) => /* @__PURE__ */ jsxs4(Text5, { children: [
738
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: key }),
739
+ /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
740
+ " ",
741
+ label
742
+ ] })
743
+ ] }, label)),
735
744
  /* @__PURE__ */ jsxs4(Text5, { children: [
736
745
  /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
737
746
  /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: " move" })
@@ -744,112 +753,14 @@ function DecisionSelect({
744
753
  ] });
745
754
  }
746
755
 
747
- // src/ui/CommandApproval.tsx
748
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
749
- function CommandApproval({
750
- command,
751
- messages,
752
- onDecide
753
- }) {
754
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
755
- messages?.map((message, index) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: message }, `msg-${index}`)),
756
- /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, bold: true, children: "Run this command?" }),
757
- /* @__PURE__ */ jsxs5(
758
- Box6,
759
- {
760
- flexDirection: "column",
761
- paddingLeft: 2,
762
- borderStyle: "single",
763
- borderColor: COLORS.success,
764
- borderTop: false,
765
- borderBottom: false,
766
- borderRight: false,
767
- gap: 1,
768
- children: [
769
- /* @__PURE__ */ jsxs5(Box6, { children: [
770
- /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: "$ " }),
771
- /* @__PURE__ */ jsx5(Text6, { color: COLORS.strong, wrap: "wrap", children: command.command })
772
- ] }),
773
- /* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
774
- /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: "in:" }),
775
- /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
776
- ] }),
777
- command.explanation && /* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
778
- /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: "why:" }),
779
- /* @__PURE__ */ jsx5(Text6, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
780
- ] })
781
- ]
782
- }
783
- ),
784
- /* @__PURE__ */ jsx5(
785
- DecisionSelect,
786
- {
787
- actions: [
788
- { label: "approve", value: "approve" },
789
- { label: "reject", value: "reject" }
790
- ],
791
- onDecide
792
- }
793
- )
794
- ] });
795
- }
796
-
797
- // src/ui/FreeTextInput.tsx
798
- import { Text as Text7, useInput as useInput2 } from "ink";
799
- import { useState as useState4 } from "react";
800
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
801
- function FreeTextInput({
802
- value,
803
- onChange,
804
- onSubmit
805
- }) {
806
- const [cursor, setCursor] = useState4(value.length);
807
- const at = Math.min(cursor, value.length);
808
- useInput2((input, key) => {
809
- if (key.upArrow || key.downArrow || key.tab || key.ctrl && input === "c") {
810
- return;
811
- }
812
- if (key.return) {
813
- onSubmit(value);
814
- return;
815
- }
816
- if (key.leftArrow) {
817
- setCursor(Math.max(0, at - 1));
818
- return;
819
- }
820
- if (key.rightArrow) {
821
- setCursor(Math.min(value.length, at + 1));
822
- return;
823
- }
824
- if (key.backspace || key.delete) {
825
- if (at === 0) return;
826
- onChange(value.slice(0, at - 1) + value.slice(at));
827
- setCursor(at - 1);
828
- return;
829
- }
830
- if (input) {
831
- onChange(value.slice(0, at) + input + value.slice(at));
832
- setCursor(at + input.length);
833
- }
834
- });
835
- const before = value.slice(0, at);
836
- const current = value.slice(at, at + 1) || " ";
837
- const after = value.slice(at + 1);
838
- return /* @__PURE__ */ jsxs6(Text7, { color: COLORS.strong, children: [
839
- before,
840
- /* @__PURE__ */ jsx6(Text7, { inverse: true, children: current }),
841
- after
842
- ] });
843
- }
844
-
845
- // src/ui/SelectPrompt.tsx
846
- import { Box as Box8, Text as Text9, useInput as useInput3, useWindowSize as useWindowSize4 } from "ink";
847
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState6 } from "react";
756
+ // src/ui/Review.tsx
757
+ import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
758
+ import { useLayoutEffect as useLayoutEffect2, useState as useState5 } from "react";
848
759
 
849
760
  // src/ui/ScrollView.tsx
850
- import { Box as Box7, Text as Text8, measureElement as measureElement2, useWindowSize as useWindowSize3 } from "ink";
851
- import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState5 } from "react";
852
- import { jsxs as jsxs7 } from "react/jsx-runtime";
761
+ import { Box as Box6, Text as Text6, measureElement as measureElement2, useWindowSize as useWindowSize3 } from "ink";
762
+ import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState4 } from "react";
763
+ import { jsxs as jsxs5 } from "react/jsx-runtime";
853
764
  var INDICATOR_ROWS = 2;
854
765
  function fittedWidth(node, columns) {
855
766
  let left = 0;
@@ -865,7 +776,7 @@ function useScrollWindow({
865
776
  }) {
866
777
  const viewportRef = useRef2(null);
867
778
  const { columns } = useWindowSize3();
868
- const [size, setSize] = useState5(
779
+ const [size, setSize] = useState4(
869
780
  null
870
781
  );
871
782
  useLayoutEffect(() => {
@@ -878,7 +789,7 @@ function useScrollWindow({
878
789
  });
879
790
  const capacity = size === null || itemCount * rowHeight <= size.height ? itemCount : Math.max(Math.floor((size.height - INDICATOR_ROWS) / rowHeight), 1);
880
791
  const maxOffset = Math.max(itemCount - capacity, 0);
881
- const [offset, setOffset] = useState5(0);
792
+ const [offset, setOffset] = useState4(0);
882
793
  const prevMaxOffsetRef = useRef2(0);
883
794
  useLayoutEffect(() => {
884
795
  const wasAtBottom = offset >= prevMaxOffsetRef.current;
@@ -920,14 +831,14 @@ function useScrollWindow({
920
831
  };
921
832
  }
922
833
  function ScrollView({ scroll, children }) {
923
- return /* @__PURE__ */ jsxs7(Box7, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
924
- scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs7(Text8, { color: COLORS.dim, children: [
834
+ return /* @__PURE__ */ jsxs5(Box6, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
835
+ scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
925
836
  "\u2191 ",
926
837
  scroll.hiddenAbove,
927
838
  " more"
928
839
  ] }),
929
840
  children,
930
- scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs7(Text8, { color: COLORS.dim, children: [
841
+ scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
931
842
  "\u2193 ",
932
843
  scroll.hiddenBelow,
933
844
  " more"
@@ -935,8 +846,285 @@ function ScrollView({ scroll, children }) {
935
846
  ] });
936
847
  }
937
848
 
938
- // src/ui/SelectPrompt.tsx
849
+ // src/ui/Review.tsx
850
+ import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
851
+ var INGEST_SCRIPT = ".algolia-wizard/ingest.sh";
852
+ function wrapText(text, width) {
853
+ if (width <= 0) return [text];
854
+ const lines = [];
855
+ let line = "";
856
+ for (const word of text.split(/\s+/).filter(Boolean)) {
857
+ const candidate = line ? `${line} ${word}` : word;
858
+ if (candidate.length <= width) {
859
+ line = candidate;
860
+ continue;
861
+ }
862
+ if (line) lines.push(line);
863
+ let rest = word;
864
+ while (rest.length > width) {
865
+ lines.push(rest.slice(0, width));
866
+ rest = rest.slice(width);
867
+ }
868
+ line = rest;
869
+ }
870
+ if (line) lines.push(line);
871
+ return lines.length > 0 ? lines : [""];
872
+ }
873
+ function heading(text) {
874
+ return { prefix: "", text, color: COLORS.strong, bold: true };
875
+ }
876
+ var BLANK = { prefix: "", text: " ", color: COLORS.dim };
877
+ function itemRows(items, width, prefixFor, colorFor) {
878
+ return items.flatMap((item, i) => {
879
+ const { prefix, color: prefixColor } = prefixFor(i);
880
+ const { color, bold } = colorFor(item);
881
+ return wrapText(item, width - prefix.length).map((line, j) => ({
882
+ prefix: j === 0 ? prefix : " ".repeat(prefix.length),
883
+ prefixColor,
884
+ text: line,
885
+ color,
886
+ bold
887
+ }));
888
+ });
889
+ }
890
+ function buildRows(review2, width) {
891
+ const { summaryPoints, nextSteps, reviewPrompt } = review2;
892
+ const numberWidth = String(nextSteps.length).length + 2;
893
+ const rows = [];
894
+ if (summaryPoints.length > 0) {
895
+ rows.push(heading("What we did"));
896
+ rows.push(
897
+ ...itemRows(
898
+ summaryPoints,
899
+ width,
900
+ () => ({ prefix: `${MARKER.done} `, color: COLORS.status.done }),
901
+ () => ({ color: COLORS.primary, bold: false })
902
+ )
903
+ );
904
+ }
905
+ if (nextSteps.length > 0) {
906
+ if (rows.length > 0) rows.push(BLANK);
907
+ rows.push(heading("What to do next"));
908
+ rows.push(
909
+ ...itemRows(
910
+ nextSteps,
911
+ width,
912
+ (i) => ({
913
+ prefix: `${i + 1}.`.padEnd(numberWidth),
914
+ color: COLORS.secondary
915
+ }),
916
+ (step) => step.includes(INGEST_SCRIPT) ? { color: COLORS.brand, bold: true } : { color: COLORS.primary, bold: false }
917
+ )
918
+ );
919
+ }
920
+ if (reviewPrompt) {
921
+ if (rows.length > 0) rows.push(BLANK);
922
+ rows.push(
923
+ ...wrapText(reviewPrompt, width).map((line) => ({
924
+ prefix: "",
925
+ text: line,
926
+ color: COLORS.brand
927
+ }))
928
+ );
929
+ }
930
+ return rows;
931
+ }
932
+ function ReviewPanel({ review: review2 }) {
933
+ const [width, setWidth] = useState5(0);
934
+ const rows = width > 0 ? buildRows(review2, width) : [];
935
+ const scroll = useScrollWindow({ itemCount: rows.length });
936
+ useLayoutEffect2(() => {
937
+ if (scroll.measured) setWidth(scroll.width);
938
+ }, [scroll.measured, scroll.width]);
939
+ const scrollable = scroll.maxOffset > 0;
940
+ useInput2(
941
+ (input, key) => {
942
+ if (input === "k" || key.upArrow) scroll.scrollBy(-1);
943
+ else if (input === "j" || key.downArrow) scroll.scrollBy(1);
944
+ },
945
+ { isActive: scrollable }
946
+ );
947
+ const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
948
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", flexGrow: 1, children: [
949
+ /* @__PURE__ */ jsx5(ScrollView, { scroll, children: visible.map((row, i) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
950
+ row.prefix && /* @__PURE__ */ jsx5(Text7, { color: row.prefixColor ?? row.color, bold: row.bold, children: row.prefix }),
951
+ /* @__PURE__ */ jsx5(Text7, { color: row.color, bold: row.bold, children: row.text })
952
+ ] }, `review-row-${i}`)) }),
953
+ scrollable && /* @__PURE__ */ jsxs6(Text7, { children: [
954
+ /* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: "[j] [k]" }),
955
+ /* @__PURE__ */ jsx5(Text7, { color: COLORS.dim, children: " scroll" })
956
+ ] })
957
+ ] });
958
+ }
959
+ function Review() {
960
+ const review2 = useWizard((s) => s.review);
961
+ if (!review2) return null;
962
+ return /* @__PURE__ */ jsx5(ReviewPanel, { review: review2 });
963
+ }
964
+
965
+ // src/ui/CommandApproval.tsx
966
+ import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
967
+ var BLANK2 = { prefix: "", text: " ", color: COLORS.dim };
968
+ var COMMAND_PREFIX = "$ ";
969
+ function wrapExactLine(line, width) {
970
+ if (line.length === 0) return [" "];
971
+ const lines = [];
972
+ let i = 0;
973
+ while (i < line.length) {
974
+ if (line.length - i <= width) {
975
+ lines.push(line.slice(i));
976
+ break;
977
+ }
978
+ let end = i + width;
979
+ if (line.charAt(end) !== " ") {
980
+ const lastSpace = line.lastIndexOf(" ", end - 1);
981
+ if (lastSpace >= i) end = lastSpace + 1;
982
+ }
983
+ if (end <= i) end = i + width;
984
+ lines.push(line.slice(i, end));
985
+ i = end;
986
+ }
987
+ return lines;
988
+ }
989
+ function wrapExact(text, width) {
990
+ if (width <= 0) return [text || " "];
991
+ return text.split(/\r\n|\n|\r/).flatMap((line) => wrapExactLine(line, width));
992
+ }
993
+ function fieldLines(prefix, value, color, width) {
994
+ return wrapText(value, Math.max(width - prefix.length, 1)).map((text, i) => ({
995
+ prefix: i === 0 ? prefix : " ".repeat(prefix.length),
996
+ text,
997
+ color
998
+ }));
999
+ }
1000
+ function buildLines(command, width) {
1001
+ const lines = wrapExact(
1002
+ command.command,
1003
+ Math.max(width - COMMAND_PREFIX.length, 1)
1004
+ ).map((text, i) => ({
1005
+ prefix: i === 0 ? COMMAND_PREFIX : " ".repeat(COMMAND_PREFIX.length),
1006
+ text,
1007
+ color: COLORS.strong
1008
+ }));
1009
+ lines.push(BLANK2);
1010
+ lines.push(...fieldLines("in: ", command.cwd, COLORS.muted, width));
1011
+ if (command.explanation) {
1012
+ lines.push(BLANK2);
1013
+ lines.push(
1014
+ ...fieldLines("why: ", command.explanation, COLORS.accent, width)
1015
+ );
1016
+ }
1017
+ return lines;
1018
+ }
1019
+ function CommandApproval({
1020
+ command,
1021
+ messages,
1022
+ onDecide
1023
+ }) {
1024
+ const [width, setWidth] = useState6(0);
1025
+ const lines = width > 0 ? buildLines(command, width) : [];
1026
+ const scroll = useScrollWindow({ itemCount: lines.length });
1027
+ useLayoutEffect3(() => {
1028
+ if (scroll.measured) setWidth(scroll.width);
1029
+ }, [scroll.measured, scroll.width]);
1030
+ const scrollable = scroll.maxOffset > 0;
1031
+ useInput3(
1032
+ (input) => {
1033
+ if (input === "k") scroll.scrollBy(-1);
1034
+ else if (input === "j") scroll.scrollBy(1);
1035
+ },
1036
+ { isActive: scrollable }
1037
+ );
1038
+ const visible = lines.slice(scroll.offset, scroll.offset + scroll.capacity);
1039
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, width: "100%", flexGrow: 1, flexShrink: 1, children: [
1040
+ messages?.map((message, index) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: message }, `msg-${index}`)),
1041
+ /* @__PURE__ */ jsx6(Text8, { color: COLORS.primary, bold: true, children: "Run this command?" }),
1042
+ /* @__PURE__ */ jsx6(
1043
+ Box8,
1044
+ {
1045
+ flexDirection: "column",
1046
+ paddingLeft: 2,
1047
+ borderStyle: "single",
1048
+ borderColor: COLORS.success,
1049
+ borderTop: false,
1050
+ borderBottom: false,
1051
+ borderRight: false,
1052
+ width: "100%",
1053
+ flexGrow: 1,
1054
+ flexShrink: 1,
1055
+ minHeight: 3,
1056
+ children: /* @__PURE__ */ jsx6(ScrollView, { scroll, children: visible.map((line, i) => /* @__PURE__ */ jsxs7(Text8, { wrap: "truncate", children: [
1057
+ line.prefix ? /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: line.prefix }) : null,
1058
+ /* @__PURE__ */ jsx6(Text8, { color: line.color, children: line.text })
1059
+ ] }, `cmd-${scroll.offset + i}`)) })
1060
+ }
1061
+ ),
1062
+ /* @__PURE__ */ jsx6(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx6(
1063
+ DecisionSelect,
1064
+ {
1065
+ actions: [
1066
+ { label: "approve", value: "approve" },
1067
+ { label: "reject", value: "reject" }
1068
+ ],
1069
+ onDecide,
1070
+ extraHints: scrollable ? [{ key: "[j] [k]", label: "scroll" }] : void 0
1071
+ }
1072
+ ) })
1073
+ ] });
1074
+ }
1075
+
1076
+ // src/ui/FreeTextInput.tsx
1077
+ import { Text as Text9, useInput as useInput4 } from "ink";
1078
+ import { useState as useState7 } from "react";
939
1079
  import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
1080
+ function FreeTextInput({
1081
+ value,
1082
+ onChange,
1083
+ onSubmit
1084
+ }) {
1085
+ const [cursor, setCursor] = useState7(value.length);
1086
+ const at = Math.min(cursor, value.length);
1087
+ useInput4((input, key) => {
1088
+ if (key.upArrow || key.downArrow || key.tab || key.ctrl && input === "c") {
1089
+ return;
1090
+ }
1091
+ if (key.return) {
1092
+ onSubmit(value);
1093
+ return;
1094
+ }
1095
+ if (key.leftArrow) {
1096
+ setCursor(Math.max(0, at - 1));
1097
+ return;
1098
+ }
1099
+ if (key.rightArrow) {
1100
+ setCursor(Math.min(value.length, at + 1));
1101
+ return;
1102
+ }
1103
+ if (key.backspace || key.delete) {
1104
+ if (at === 0) return;
1105
+ onChange(value.slice(0, at - 1) + value.slice(at));
1106
+ setCursor(at - 1);
1107
+ return;
1108
+ }
1109
+ if (input) {
1110
+ onChange(value.slice(0, at) + input + value.slice(at));
1111
+ setCursor(at + input.length);
1112
+ }
1113
+ });
1114
+ const before = value.slice(0, at);
1115
+ const current = value.slice(at, at + 1) || " ";
1116
+ const after = value.slice(at + 1);
1117
+ return /* @__PURE__ */ jsxs8(Text9, { color: COLORS.strong, children: [
1118
+ before,
1119
+ /* @__PURE__ */ jsx7(Text9, { inverse: true, children: current }),
1120
+ after
1121
+ ] });
1122
+ }
1123
+
1124
+ // src/ui/SelectPrompt.tsx
1125
+ import { Box as Box9, Text as Text10, useInput as useInput5, useWindowSize as useWindowSize4 } from "ink";
1126
+ import { useLayoutEffect as useLayoutEffect4, useRef as useRef3, useState as useState8 } from "react";
1127
+ import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
940
1128
  var CANCEL = "cancel";
941
1129
  var ARROW_WIDTH = 4;
942
1130
  var COLUMN_GAP = 2;
@@ -968,19 +1156,19 @@ function SelectPrompt({
968
1156
  const rows = hasCancel ? [...options, "Cancel"] : options;
969
1157
  const cancelIndex = hasCancel ? options.length : -1;
970
1158
  const isDisabled = (i) => i !== cancelIndex && Boolean(disabled?.[i]);
971
- const [index, setIndex] = useState6(() => {
1159
+ const [index, setIndex] = useState8(() => {
972
1160
  const start = defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0;
973
1161
  return isDisabled(start) ? nextSelectableIndex(start, 1, rows.length, isDisabled) : start;
974
1162
  });
975
- const [checked, setChecked] = useState6(() => /* @__PURE__ */ new Set());
1163
+ const [checked, setChecked] = useState8(() => /* @__PURE__ */ new Set());
976
1164
  const hints = [];
977
1165
  if (rows.length > 1) hints.push({ key: "[\u2191] [\u2193]", label: "move" });
978
1166
  if (multi) hints.push({ key: "[space]", label: "select" });
979
1167
  hints.push({ key: "[enter]", label: "confirm" });
980
1168
  const containerRef = useRef3(null);
981
1169
  const { columns } = useWindowSize4();
982
- const [width, setWidth] = useState6(columns);
983
- useLayoutEffect2(() => {
1170
+ const [width, setWidth] = useState8(columns);
1171
+ useLayoutEffect4(() => {
984
1172
  if (!containerRef.current) return;
985
1173
  const measured = fittedWidth(containerRef.current, columns);
986
1174
  setWidth((prev) => prev === measured ? prev : measured);
@@ -1005,11 +1193,11 @@ function SelectPrompt({
1005
1193
  rowHeight: ROW_HEIGHT
1006
1194
  });
1007
1195
  const { revealIndex } = scroll;
1008
- useLayoutEffect2(() => {
1196
+ useLayoutEffect4(() => {
1009
1197
  revealIndex(index);
1010
1198
  }, [index, revealIndex]);
1011
1199
  const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
1012
- useInput3((input, key) => {
1200
+ useInput5((input, key) => {
1013
1201
  if (rows.length === 0) return;
1014
1202
  if (key.upArrow || input === "k") {
1015
1203
  setIndex((i) => nextSelectableIndex(i, -1, rows.length, isDisabled));
@@ -1034,17 +1222,17 @@ function SelectPrompt({
1034
1222
  }
1035
1223
  }
1036
1224
  });
1037
- return /* @__PURE__ */ jsx7(Box8, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, width, children: [
1038
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
1039
- error && /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: error }),
1040
- messages?.map((m, i) => /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: m }, `msg-${i}`)),
1041
- table && /* @__PURE__ */ jsx7(Table, { columns: table.columns, rows: table.rows }),
1042
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1043
- question && /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: question }),
1044
- helpText && /* @__PURE__ */ jsx7(Text9, { color: COLORS.dim, children: helpText })
1225
+ return /* @__PURE__ */ jsx8(Box9, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, width, children: [
1226
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
1227
+ error && /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: error }),
1228
+ messages?.map((m, i) => /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: m }, `msg-${i}`)),
1229
+ table && /* @__PURE__ */ jsx8(Table, { columns: table.columns, rows: table.rows }),
1230
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1231
+ question && /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: question }),
1232
+ helpText && /* @__PURE__ */ jsx8(Text10, { color: COLORS.dim, children: helpText })
1045
1233
  ] })
1046
1234
  ] }),
1047
- /* @__PURE__ */ jsx7(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
1235
+ /* @__PURE__ */ jsx8(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
1048
1236
  const i = scroll.offset + visibleIndex;
1049
1237
  const highlighted = i === index;
1050
1238
  const isCancel = i === cancelIndex;
@@ -1052,7 +1240,7 @@ function SelectPrompt({
1052
1240
  const sec = isCancel ? void 0 : secondary?.[i];
1053
1241
  const isText = sec?.kind === "text";
1054
1242
  const rowDisabled = isDisabled(i);
1055
- return /* @__PURE__ */ jsxs8(
1243
+ return /* @__PURE__ */ jsxs9(
1056
1244
  SelectRow,
1057
1245
  {
1058
1246
  highlighted,
@@ -1062,24 +1250,24 @@ function SelectPrompt({
1062
1250
  label: `${bullet}${option}`,
1063
1251
  usePadding: true,
1064
1252
  children: [
1065
- isText && textWidth > 0 && /* @__PURE__ */ jsx7(Box8, { width: textWidth, children: /* @__PURE__ */ jsx7(
1066
- Text9,
1253
+ isText && textWidth > 0 && /* @__PURE__ */ jsx8(Box9, { width: textWidth, children: /* @__PURE__ */ jsx8(
1254
+ Text10,
1067
1255
  {
1068
1256
  wrap: "truncate",
1069
1257
  color: rowDisabled ? COLORS.dim : highlighted ? COLORS.primary : COLORS.muted,
1070
1258
  children: sec.value
1071
1259
  }
1072
1260
  ) }),
1073
- sec?.kind === "badge" && /* @__PURE__ */ jsx7(Box8, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx7(Text9, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
1261
+ sec?.kind === "badge" && /* @__PURE__ */ jsx8(Box9, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx8(Text10, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
1074
1262
  ]
1075
1263
  },
1076
1264
  `row-${i}`
1077
1265
  );
1078
1266
  }) }),
1079
- /* @__PURE__ */ jsx7(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx7(Text9, { color: COLORS.dim, children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs8(Text9, { color: COLORS.dim, children: [
1267
+ /* @__PURE__ */ jsx8(Box9, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text10, { color: COLORS.dim, children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs9(Text10, { color: COLORS.dim, children: [
1080
1268
  i > 0 ? " " : "",
1081
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: key }),
1082
- /* @__PURE__ */ jsxs8(Text9, { color: COLORS.dim, children: [
1269
+ /* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: key }),
1270
+ /* @__PURE__ */ jsxs9(Text10, { color: COLORS.dim, children: [
1083
1271
  " ",
1084
1272
  label
1085
1273
  ] })
@@ -1088,17 +1276,17 @@ function SelectPrompt({
1088
1276
  }
1089
1277
 
1090
1278
  // src/ui/PromptInput.tsx
1091
- import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1279
+ import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
1092
1280
  var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
1093
1281
  function EnterToContinuePrompt({
1094
1282
  question,
1095
1283
  messages,
1096
1284
  onDecide
1097
1285
  }) {
1098
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
1099
- messages?.map((m, i) => /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: m }, `msg-${i}`)),
1100
- question && /* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: question }),
1101
- /* @__PURE__ */ jsx8(
1286
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", gap: 1, children: [
1287
+ messages?.map((m, i) => /* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: m }, `msg-${i}`)),
1288
+ question && /* @__PURE__ */ jsx9(Text11, { color: COLORS.primary, children: question }),
1289
+ /* @__PURE__ */ jsx9(
1102
1290
  DecisionSelect,
1103
1291
  {
1104
1292
  actions: [
@@ -1112,17 +1300,17 @@ function EnterToContinuePrompt({
1112
1300
  }
1113
1301
  function PromptInput() {
1114
1302
  const { phase, inputReq, submitInput } = useWizard();
1115
- const [draft, setDraft] = useState7("");
1116
- const [submittedReq, setSubmittedReq] = useState7(null);
1303
+ const [draft, setDraft] = useState9("");
1304
+ const [submittedReq, setSubmittedReq] = useState9(null);
1117
1305
  const promptKey = inputReq && `${inputReq.promptType}:${inputReq.prompt}`;
1118
1306
  const submitting = inputReq != null && submittedReq === inputReq;
1119
1307
  if (phase === "done" || phase === "error") {
1120
- return /* @__PURE__ */ jsx8(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text10, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
1308
+ return /* @__PURE__ */ jsx9(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text11, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
1121
1309
  }
1122
1310
  if (phase !== "awaitingInput" || !inputReq) return null;
1123
1311
  if (submitting) {
1124
- return /* @__PURE__ */ jsx8(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsxs9(Text10, { color: COLORS.strong, bold: true, children: [
1125
- /* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
1312
+ return /* @__PURE__ */ jsx9(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsxs10(Text11, { color: COLORS.strong, bold: true, children: [
1313
+ /* @__PURE__ */ jsx9(Spinner, { type: "dots" }),
1126
1314
  " Continuing\u2026"
1127
1315
  ] }) });
1128
1316
  }
@@ -1131,7 +1319,7 @@ function PromptInput() {
1131
1319
  submitInput(value);
1132
1320
  };
1133
1321
  if (inputReq.promptType === "multipleChoice") {
1134
- return /* @__PURE__ */ jsx8(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx8(
1322
+ return /* @__PURE__ */ jsx9(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx9(
1135
1323
  SelectPrompt,
1136
1324
  {
1137
1325
  question: inputReq.prompt,
@@ -1150,7 +1338,7 @@ function PromptInput() {
1150
1338
  ) });
1151
1339
  }
1152
1340
  if (inputReq.promptType === "multiSelect") {
1153
- return /* @__PURE__ */ jsx8(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx8(
1341
+ return /* @__PURE__ */ jsx9(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx9(
1154
1342
  SelectPrompt,
1155
1343
  {
1156
1344
  multi: true,
@@ -1166,7 +1354,7 @@ function PromptInput() {
1166
1354
  ) });
1167
1355
  }
1168
1356
  if (inputReq.promptType === "notice") {
1169
- return /* @__PURE__ */ jsx8(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx8(
1357
+ return /* @__PURE__ */ jsx9(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx9(
1170
1358
  SelectPrompt,
1171
1359
  {
1172
1360
  messages: inputReq.messages,
@@ -1177,7 +1365,7 @@ function PromptInput() {
1177
1365
  ) });
1178
1366
  }
1179
1367
  if (inputReq.promptType === "enterToContinue") {
1180
- return /* @__PURE__ */ jsx8(
1368
+ return /* @__PURE__ */ jsx9(
1181
1369
  EnterToContinuePrompt,
1182
1370
  {
1183
1371
  question: inputReq.prompt,
@@ -1187,173 +1375,58 @@ function PromptInput() {
1187
1375
  );
1188
1376
  }
1189
1377
  if (inputReq.promptType === "commandApproval" && inputReq.command) {
1190
- return /* @__PURE__ */ jsx8(
1378
+ return /* @__PURE__ */ jsx9(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx9(
1191
1379
  CommandApproval,
1192
1380
  {
1193
1381
  command: inputReq.command,
1194
1382
  messages: inputReq.messages,
1195
1383
  onDecide: submitInput
1196
1384
  }
1197
- );
1385
+ ) });
1198
1386
  }
1199
1387
  if (inputReq.promptType === "acceptReject") {
1200
1388
  const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
1201
- return /* @__PURE__ */ jsxs9(Box9, { flexGrow: 1, flexDirection: "column", gap: 1, children: [
1202
- inputReq.detectedLanguages && /* @__PURE__ */ jsxs9(Text10, { color: COLORS.muted, children: [
1389
+ return /* @__PURE__ */ jsxs10(Box10, { flexGrow: 1, flexDirection: "column", gap: 1, children: [
1390
+ inputReq.detectedLanguages && /* @__PURE__ */ jsxs10(Text11, { color: COLORS.muted, children: [
1203
1391
  "Languages:",
1204
1392
  " ",
1205
- /* @__PURE__ */ jsx8(Text10, { color: COLORS.success, children: inputReq.detectedLanguages })
1393
+ /* @__PURE__ */ jsx9(Text11, { color: COLORS.success, children: inputReq.detectedLanguages })
1206
1394
  ] }),
1207
- /* @__PURE__ */ jsx8(
1208
- SelectPrompt,
1209
- {
1210
- question: inputReq.prompt,
1211
- messages: inputReq.messages,
1212
- options: labels,
1213
- secondary: inputReq.secondary,
1214
- onSelect: (value) => handleSubmit(value === labels[0])
1215
- },
1216
- promptKey
1217
- )
1218
- ] });
1219
- }
1220
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1221
- inputReq.error && /* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: inputReq.error }),
1222
- inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: m }, `msg-${i}`)),
1223
- /* @__PURE__ */ jsxs9(Box9, { children: [
1224
- /* @__PURE__ */ jsxs9(Text10, { color: COLORS.primary, children: [
1225
- inputReq.prompt,
1226
- " "
1227
- ] }),
1228
- /* @__PURE__ */ jsx8(
1229
- FreeTextInput,
1230
- {
1231
- value: draft,
1232
- onChange: setDraft,
1233
- onSubmit: (v) => {
1234
- submitInput(v);
1235
- setDraft("");
1236
- }
1237
- }
1238
- )
1239
- ] })
1240
- ] });
1241
- }
1242
-
1243
- // src/ui/Review.tsx
1244
- import { Box as Box10, Text as Text11, useInput as useInput4 } from "ink";
1245
- import { useLayoutEffect as useLayoutEffect3, useState as useState8 } from "react";
1246
- import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
1247
- var INGEST_SCRIPT = ".algolia-wizard/ingest.sh";
1248
- function wrapText(text, width) {
1249
- if (width <= 0) return [text];
1250
- const lines = [];
1251
- let line = "";
1252
- for (const word of text.split(/\s+/).filter(Boolean)) {
1253
- const candidate = line ? `${line} ${word}` : word;
1254
- if (candidate.length <= width) {
1255
- line = candidate;
1256
- continue;
1257
- }
1258
- if (line) lines.push(line);
1259
- let rest = word;
1260
- while (rest.length > width) {
1261
- lines.push(rest.slice(0, width));
1262
- rest = rest.slice(width);
1263
- }
1264
- line = rest;
1265
- }
1266
- if (line) lines.push(line);
1267
- return lines.length > 0 ? lines : [""];
1268
- }
1269
- function heading(text) {
1270
- return { prefix: "", text, color: COLORS.strong, bold: true };
1271
- }
1272
- var BLANK = { prefix: "", text: " ", color: COLORS.dim };
1273
- function itemRows(items, width, prefixFor, colorFor) {
1274
- return items.flatMap((item, i) => {
1275
- const { prefix, color: prefixColor } = prefixFor(i);
1276
- const { color, bold } = colorFor(item);
1277
- return wrapText(item, width - prefix.length).map((line, j) => ({
1278
- prefix: j === 0 ? prefix : " ".repeat(prefix.length),
1279
- prefixColor,
1280
- text: line,
1281
- color,
1282
- bold
1283
- }));
1284
- });
1285
- }
1286
- function buildRows(review2, width) {
1287
- const { summaryPoints, nextSteps, reviewPrompt } = review2;
1288
- const numberWidth = String(nextSteps.length).length + 2;
1289
- const rows = [];
1290
- if (summaryPoints.length > 0) {
1291
- rows.push(heading("What we did"));
1292
- rows.push(
1293
- ...itemRows(
1294
- summaryPoints,
1295
- width,
1296
- () => ({ prefix: `${MARKER.done} `, color: COLORS.status.done }),
1297
- () => ({ color: COLORS.primary, bold: false })
1298
- )
1299
- );
1300
- }
1301
- if (nextSteps.length > 0) {
1302
- if (rows.length > 0) rows.push(BLANK);
1303
- rows.push(heading("What to do next"));
1304
- rows.push(
1305
- ...itemRows(
1306
- nextSteps,
1307
- width,
1308
- (i) => ({
1309
- prefix: `${i + 1}.`.padEnd(numberWidth),
1310
- color: COLORS.secondary
1311
- }),
1312
- (step) => step.includes(INGEST_SCRIPT) ? { color: COLORS.brand, bold: true } : { color: COLORS.primary, bold: false }
1395
+ /* @__PURE__ */ jsx9(
1396
+ SelectPrompt,
1397
+ {
1398
+ question: inputReq.prompt,
1399
+ messages: inputReq.messages,
1400
+ options: labels,
1401
+ secondary: inputReq.secondary,
1402
+ onSelect: (value) => handleSubmit(value === labels[0])
1403
+ },
1404
+ promptKey
1313
1405
  )
1314
- );
1315
- }
1316
- if (reviewPrompt) {
1317
- if (rows.length > 0) rows.push(BLANK);
1318
- rows.push(
1319
- ...wrapText(reviewPrompt, width).map((line) => ({
1320
- prefix: "",
1321
- text: line,
1322
- color: COLORS.brand
1323
- }))
1324
- );
1406
+ ] });
1325
1407
  }
1326
- return rows;
1327
- }
1328
- function ReviewPanel({ review: review2 }) {
1329
- const [width, setWidth] = useState8(0);
1330
- const rows = width > 0 ? buildRows(review2, width) : [];
1331
- const scroll = useScrollWindow({ itemCount: rows.length });
1332
- useLayoutEffect3(() => {
1333
- if (scroll.measured) setWidth(scroll.width);
1334
- }, [scroll.measured, scroll.width]);
1335
- const scrollable = scroll.maxOffset > 0;
1336
- useInput4(
1337
- (_input, key) => {
1338
- if (key.upArrow) scroll.scrollBy(-1);
1339
- else if (key.downArrow) scroll.scrollBy(1);
1340
- },
1341
- { isActive: scrollable }
1342
- );
1343
- const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
1344
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", flexGrow: 1, children: [
1345
- /* @__PURE__ */ jsx9(ScrollView, { scroll, children: visible.map((row, i) => /* @__PURE__ */ jsxs10(Text11, { wrap: "truncate", children: [
1346
- row.prefix && /* @__PURE__ */ jsx9(Text11, { color: row.prefixColor ?? row.color, bold: row.bold, children: row.prefix }),
1347
- /* @__PURE__ */ jsx9(Text11, { color: row.color, bold: row.bold, children: row.text })
1348
- ] }, `review-row-${i}`)) }),
1349
- scrollable && /* @__PURE__ */ jsx9(Text11, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1408
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
1409
+ inputReq.error && /* @__PURE__ */ jsx9(Text11, { color: COLORS.danger, children: inputReq.error }),
1410
+ inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: m }, `msg-${i}`)),
1411
+ /* @__PURE__ */ jsxs10(Box10, { children: [
1412
+ /* @__PURE__ */ jsxs10(Text11, { color: COLORS.primary, children: [
1413
+ inputReq.prompt,
1414
+ " "
1415
+ ] }),
1416
+ /* @__PURE__ */ jsx9(
1417
+ FreeTextInput,
1418
+ {
1419
+ value: draft,
1420
+ onChange: setDraft,
1421
+ onSubmit: (v) => {
1422
+ submitInput(v);
1423
+ setDraft("");
1424
+ }
1425
+ }
1426
+ )
1427
+ ] })
1350
1428
  ] });
1351
1429
  }
1352
- function Review() {
1353
- const review2 = useWizard((s) => s.review);
1354
- if (!review2) return null;
1355
- return /* @__PURE__ */ jsx9(ReviewPanel, { review: review2 });
1356
- }
1357
1430
 
1358
1431
  // src/workflows/default.ts
1359
1432
  import { z as z29 } from "zod";
@@ -1624,23 +1697,137 @@ function trackWorkflowError(ctx) {
1624
1697
 
1625
1698
  // src/lib/events.ts
1626
1699
  import "zod";
1627
- function track(event, payload) {
1700
+ var analyticsAppId = void 0;
1701
+ var analyticsAgentRunId = void 0;
1702
+ function setAnalyticsContext(next) {
1703
+ analyticsAppId = next.appId;
1704
+ analyticsAgentRunId = next.agentRunId;
1705
+ }
1706
+ function wizardErrorType(actionId, step, error) {
1707
+ if (actionId === "project-scan" || step === "project scan") {
1708
+ return "Scan Codebase";
1709
+ }
1710
+ if (actionId === "ingestion" || step === "Push Data" || step === "ingest records") {
1711
+ return "Ingest Records";
1712
+ }
1713
+ if (actionId === "search" || step === "create search ui") {
1714
+ if (error && error.toLowerCase().includes("verification failed")) {
1715
+ return "Wire Component to UI";
1716
+ }
1717
+ return "Generate UI";
1718
+ }
1719
+ return void 0;
1720
+ }
1721
+ function commonEventProperties() {
1722
+ const properties = {};
1723
+ if (analyticsAppId) properties.app_id = analyticsAppId;
1724
+ if (analyticsAgentRunId) properties.agent_run_id = analyticsAgentRunId;
1725
+ return properties;
1726
+ }
1727
+ function postAnalytics(path, body, label) {
1628
1728
  if (isTelemetryOptedOut()) return;
1629
1729
  const token = getAuthToken();
1630
1730
  if (!token) return;
1631
1731
  const userId = useWizard.getState().user?.userId;
1632
1732
  if (!userId) return;
1633
- void proxyFetch(`${PROXY_BASE_URL}/events`, {
1733
+ void proxyFetch(`${PROXY_BASE_URL}${path}`, {
1634
1734
  method: "POST",
1635
1735
  headers: {
1636
1736
  "content-type": "application/json",
1637
1737
  authorization: `Bearer ${token}`
1638
1738
  },
1639
- body: JSON.stringify({ userId, event, properties: payload })
1739
+ body: JSON.stringify(body)
1640
1740
  }).catch((err) => {
1641
- logger.warn({ err, event }, "failed to send analytics event");
1741
+ logger.warn({ err, event: label }, "failed to send analytics event");
1642
1742
  });
1643
1743
  }
1744
+ function track(event, payload) {
1745
+ postAnalytics(
1746
+ "/events",
1747
+ {
1748
+ userId: useWizard.getState().user?.userId,
1749
+ event,
1750
+ properties: { ...payload, ...commonEventProperties() }
1751
+ },
1752
+ event
1753
+ );
1754
+ }
1755
+ function identify(traits) {
1756
+ const userId = useWizard.getState().user?.userId;
1757
+ postAnalytics("/identify", { userId, traits }, "identify");
1758
+ }
1759
+
1760
+ // package.json
1761
+ var package_default = {
1762
+ name: "@algolia/wizard",
1763
+ version: "0.63.0",
1764
+ description: "Magically implement Algolia functionality in your codebase",
1765
+ type: "module",
1766
+ engines: {
1767
+ node: ">=24"
1768
+ },
1769
+ bin: {
1770
+ wizard: "dist/main.js"
1771
+ },
1772
+ files: [
1773
+ "dist",
1774
+ "docs"
1775
+ ],
1776
+ scripts: {
1777
+ "build:proxy": "node scripts/build.mjs proxy",
1778
+ build: "node scripts/build.mjs",
1779
+ "dev:proxy": "touch .env && NODE_OPTIONS=--use-system-ca tsx watch --env-file=.env src/proxy/index.ts",
1780
+ dev: "touch .env && tsx --env-file=.env ./src/main.tsx",
1781
+ "env:load": "pnpm exec -- varlock load",
1782
+ prepare: "husky",
1783
+ prepublishOnly: "pnpm build",
1784
+ reset: "tsx ./scripts/reset-state.ts",
1785
+ "test:fixtures": "touch .env && tsx --env-file=.env ./fixtures/run-fixtures.ts",
1786
+ "test:tools": "tsx ./tool-evals/toolEval.ts",
1787
+ "test:tools:improve": "tsx ./tool-evals/improveFromPlan.ts",
1788
+ test: "vitest",
1789
+ typecheck: "tsc --noEmit -p tsconfig.json"
1790
+ },
1791
+ keywords: [],
1792
+ author: "",
1793
+ license: "ISC",
1794
+ packageManager: "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
1795
+ devDependencies: {
1796
+ "@clack/prompts": "^1.6.0",
1797
+ "@types/ioredis-mock": "^8.2.7",
1798
+ "@types/node": "^25.9.1",
1799
+ "@types/react": "^19.2.16",
1800
+ esbuild: "^0.28.0",
1801
+ husky: "^9.1.7",
1802
+ "ink-testing-library": "^4.0.0",
1803
+ "ioredis-mock": "^8.13.1",
1804
+ "lint-staged": "^17.0.8",
1805
+ tsx: "^4.22.3",
1806
+ typescript: "^6.0.3",
1807
+ vitest: "^4.1.8"
1808
+ },
1809
+ dependencies: {
1810
+ "@ai-sdk/anthropic": "^3.0.81",
1811
+ "@ai-sdk/openai-compatible": "^2.0.47",
1812
+ "@hono/node-server": "^2.0.10",
1813
+ "@segment/analytics-node": "^3.1.0",
1814
+ ai: "^6.0.190",
1815
+ "cross-keychain": "^1.1.0",
1816
+ dotenv: "^17.4.2",
1817
+ hono: "^4.12.27",
1818
+ ink: "^7.0.5",
1819
+ "ink-picture": "^2.1.0",
1820
+ "ink-spinner": "^5.0.0",
1821
+ ioredis: "^5.11.1",
1822
+ nanoid: "^5.1.15",
1823
+ pino: "^10.3.1",
1824
+ react: "^19.2.7",
1825
+ "terminal-link": "^5.0.0",
1826
+ varlock: "^1.5.1",
1827
+ zod: "^4.4.3",
1828
+ zustand: "^5.0.14"
1829
+ }
1830
+ };
1644
1831
 
1645
1832
  // src/core/orchestrator.ts
1646
1833
  function defineStep(step) {
@@ -1693,9 +1880,37 @@ function initWorkflowState(workflow, now, appId) {
1693
1880
  status: "pending"
1694
1881
  })),
1695
1882
  algoliaState: appId ? { appId } : {},
1696
- userInputs: {}
1883
+ userInputs: {},
1884
+ agentRunId: crypto.randomUUID()
1885
+ };
1886
+ }
1887
+ function ensureAgentRunId(state) {
1888
+ if (!state.agentRunId) state.agentRunId = crypto.randomUUID();
1889
+ }
1890
+ function completeEventProperties(state, totalDurationMs, stepCount) {
1891
+ const indexName = state.steps.find((s) => s.id === "select-index")?.output?.selection;
1892
+ const framework = state.steps.find((s) => s.id === "confirm-framework")?.output?.frameworks?.[0]?.name;
1893
+ return {
1894
+ total_duration_ms: totalDurationMs,
1895
+ duration_ms: totalDurationMs,
1896
+ step_count: stepCount,
1897
+ index_name: indexName ?? "unknown",
1898
+ framework: framework ?? "unknown"
1697
1899
  };
1698
1900
  }
1901
+ function errorCodeFrom(err) {
1902
+ let current = err;
1903
+ for (let i = 0; i < 4 && current; i++) {
1904
+ if (current && typeof current === "object" && "statusCode" in current) {
1905
+ const code = current.statusCode;
1906
+ if (typeof code === "number" || typeof code === "string") {
1907
+ return String(code);
1908
+ }
1909
+ }
1910
+ current = current instanceof Error ? current.cause : void 0;
1911
+ }
1912
+ return void 0;
1913
+ }
1699
1914
  async function ensureConsent() {
1700
1915
  const config = await loadConfig();
1701
1916
  if (config.aiConsent) return;
@@ -1808,6 +2023,8 @@ async function runWorkflow(workflow, appId, options) {
1808
2023
  try {
1809
2024
  const state = await resolveWorkflowState(workflow, appId, options);
1810
2025
  ensureExecutedStepCount(state);
2026
+ ensureAgentRunId(state);
2027
+ setAnalyticsContext({ appId, agentRunId: state.agentRunId });
1811
2028
  store.startWorkflow(
1812
2029
  {
1813
2030
  id: workflow.id,
@@ -1818,6 +2035,11 @@ async function runWorkflow(workflow, appId, options) {
1818
2035
  );
1819
2036
  await ensureConsent();
1820
2037
  trackWorkflowStart({ workflowId: workflow.id, appId });
2038
+ track("AI Wizard Started", {
2039
+ version: package_default.version,
2040
+ wizard_version: package_default.version,
2041
+ os: process.platform
2042
+ });
1821
2043
  for (let i = state.currentStepIndex; i < workflow.steps.length; i++) {
1822
2044
  await runStep(state, i, workflow.steps[i], appId);
1823
2045
  }
@@ -1829,10 +2051,11 @@ async function runWorkflow(workflow, appId, options) {
1829
2051
  total_duration: Math.round(totalDurationMs / 1e3),
1830
2052
  total_steps: stepCount
1831
2053
  });
1832
- track("AI Wizard Complete", {
1833
- total_duration_ms: totalDurationMs,
1834
- step_count: stepCount
1835
- });
2054
+ track(
2055
+ "AI Wizard Completed",
2056
+ completeEventProperties(state, totalDurationMs, stepCount)
2057
+ );
2058
+ identify({ is_Wizard_user: true });
1836
2059
  await recordWorkflowRun(workflow.id, nowIso());
1837
2060
  await clearWorkflowState(workflow.id);
1838
2061
  store.setDone();
@@ -1873,10 +2096,15 @@ async function runWorkflow(workflow, appId, options) {
1873
2096
  actionId: failedActionId,
1874
2097
  cause: err
1875
2098
  });
2099
+ const errorType = wizardErrorType(failedActionId, step, message);
2100
+ const errorCode2 = errorCodeFrom(err);
1876
2101
  track("Error", {
1877
2102
  step,
1878
2103
  error: message,
1879
- product_area: "AI Wizard"
2104
+ error_message: message,
2105
+ product_area: "AI Wizard",
2106
+ ...errorType ? { error_type: errorType } : {},
2107
+ ...errorCode2 ? { error_code: errorCode2 } : {}
1880
2108
  });
1881
2109
  store.setError(message);
1882
2110
  }
@@ -3456,7 +3684,7 @@ function generateRecordTool(ctx, createModel = defaultCreateModel2) {
3456
3684
  try {
3457
3685
  const anthropic = createModel();
3458
3686
  const value = z17.union([z17.string(), z17.number(), z17.boolean(), z17.null()]);
3459
- const recordSchema = z17.object(
3687
+ const recordSchema2 = z17.object(
3460
3688
  Object.fromEntries(attributes.map((attr) => [attr, value]))
3461
3689
  );
3462
3690
  const generateBatch = async (batchCount) => {
@@ -3467,7 +3695,7 @@ function generateRecordTool(ctx, createModel = defaultCreateModel2) {
3467
3695
  model: anthropic(RECORD_MODEL),
3468
3696
  output: Output2.object({
3469
3697
  schema: z17.object({
3470
- records: z17.array(recordSchema).length(batchCount)
3698
+ records: z17.array(recordSchema2).length(batchCount)
3471
3699
  })
3472
3700
  }),
3473
3701
  prompt: [
@@ -3902,78 +4130,6 @@ async function runAnalysis(mode, extraInstructions = []) {
3902
4130
  return Object.assign({}, ...results);
3903
4131
  }
3904
4132
 
3905
- // package.json
3906
- var package_default = {
3907
- name: "@algolia/wizard",
3908
- version: "0.61.0",
3909
- description: "Magically implement Algolia functionality in your codebase",
3910
- type: "module",
3911
- engines: {
3912
- node: ">=24"
3913
- },
3914
- bin: {
3915
- wizard: "dist/main.js"
3916
- },
3917
- files: [
3918
- "dist",
3919
- "docs"
3920
- ],
3921
- scripts: {
3922
- "build:proxy": "node scripts/build.mjs proxy",
3923
- build: "node scripts/build.mjs",
3924
- "dev:proxy": "touch .env && NODE_OPTIONS=--use-system-ca tsx watch --env-file=.env src/proxy/index.ts",
3925
- dev: "touch .env && tsx --env-file=.env ./src/main.tsx",
3926
- "env:load": "pnpm exec -- varlock load",
3927
- prepare: "husky",
3928
- prepublishOnly: "pnpm build",
3929
- reset: "tsx ./scripts/reset-state.ts",
3930
- "test:fixtures": "touch .env && tsx --env-file=.env ./fixtures/run-fixtures.ts",
3931
- "test:tools": "tsx ./tool-evals/toolEval.ts",
3932
- "test:tools:improve": "tsx ./tool-evals/improveFromPlan.ts",
3933
- test: "vitest",
3934
- typecheck: "tsc --noEmit -p tsconfig.json"
3935
- },
3936
- keywords: [],
3937
- author: "",
3938
- license: "ISC",
3939
- packageManager: "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
3940
- devDependencies: {
3941
- "@clack/prompts": "^1.6.0",
3942
- "@types/ioredis-mock": "^8.2.7",
3943
- "@types/node": "^25.9.1",
3944
- "@types/react": "^19.2.16",
3945
- esbuild: "^0.28.0",
3946
- husky: "^9.1.7",
3947
- "ink-testing-library": "^4.0.0",
3948
- "ioredis-mock": "^8.13.1",
3949
- "lint-staged": "^17.0.8",
3950
- tsx: "^4.22.3",
3951
- typescript: "^6.0.3",
3952
- vitest: "^4.1.8"
3953
- },
3954
- dependencies: {
3955
- "@ai-sdk/anthropic": "^3.0.81",
3956
- "@ai-sdk/openai-compatible": "^2.0.47",
3957
- "@hono/node-server": "^2.0.10",
3958
- "@segment/analytics-node": "^3.1.0",
3959
- ai: "^6.0.190",
3960
- "cross-keychain": "^1.1.0",
3961
- dotenv: "^17.4.2",
3962
- hono: "^4.12.27",
3963
- ink: "^7.0.5",
3964
- "ink-picture": "^2.1.0",
3965
- "ink-spinner": "^5.0.0",
3966
- ioredis: "^5.11.1",
3967
- nanoid: "^5.1.15",
3968
- pino: "^10.3.1",
3969
- react: "^19.2.7",
3970
- "terminal-link": "^5.0.0",
3971
- varlock: "^1.5.1",
3972
- zod: "^4.4.3",
3973
- zustand: "^5.0.14"
3974
- }
3975
- };
3976
-
3977
4133
  // src/actions/projectScan.ts
3978
4134
  import "zod";
3979
4135
  var projectScanSchema = detectLanguageSchema.extend({
@@ -3982,16 +4138,19 @@ var projectScanSchema = detectLanguageSchema.extend({
3982
4138
  verification: analyzeCodebaseSchema.shape.verification
3983
4139
  });
3984
4140
  async function projectScan(ctx) {
4141
+ const startedAt = Date.now();
3985
4142
  const [detected, analyses] = await Promise.all([
3986
4143
  detectLanguage(),
3987
4144
  runAnalysis()
3988
4145
  ]);
3989
- track("AI Wizard Started", {
3990
- wizard_version: package_default.version,
3991
- os: process.platform
3992
- });
4146
+ const entities = toEntitySummary(analyses.ingestionAnalysis ?? []);
3993
4147
  track("AI Wizard Scan Completed", {
3994
- entities: toEntitySummary(analyses.ingestionAnalysis ?? [])
4148
+ languages: detected.languages,
4149
+ frameworks: detected.frameworks,
4150
+ data_models_found: entities.map((entity) => entity.name),
4151
+ back_end_language_detected: detected.languages[0]?.name ?? null,
4152
+ front_end_framework_detected: detected.frameworks[0]?.name ?? null,
4153
+ duration_ms: Date.now() - startedAt
3995
4154
  });
3996
4155
  for (const [key, value] of Object.entries(analyses)) {
3997
4156
  ctx.setUserInput(key, value);
@@ -4089,7 +4248,7 @@ var isSameFramework = (a, b) => {
4089
4248
  };
4090
4249
  function confirmed(name, version) {
4091
4250
  const frameworks = [{ name, version: version ?? "unknown" }];
4092
- track("AI Wizard Frontend Framework Confirmed", { frameworks });
4251
+ track("AI Wizard Frontend Framework Confirmed", { frameworks, framework: name });
4093
4252
  return { frameworks };
4094
4253
  }
4095
4254
  async function askOtherFramework(ctx) {
@@ -4193,6 +4352,11 @@ var confirmEntitiesSchema = z26.object({
4193
4352
  confirmedEntities: confirmedEntitiesFieldSchema
4194
4353
  });
4195
4354
  var SUGGEST_ENTITY = "Suggest a different entity\u2026";
4355
+ function usedDetectedBackend(detected, confirmed2) {
4356
+ if (!detected?.length || !confirmed2?.length) return false;
4357
+ const detectedNames = new Set(detected.map((entry) => entry.name.toLowerCase()));
4358
+ return confirmed2.every((entry) => detectedNames.has(entry.name.toLowerCase()));
4359
+ }
4196
4360
  async function askEntityName(ctx, opts) {
4197
4361
  const blankHint = opts?.blankHint ?? "cancel";
4198
4362
  const declared = await promptUser(ctx, {
@@ -4299,8 +4463,14 @@ async function confirmEntities(ctx) {
4299
4463
  throw new Error("User cancelled entity selection \u2014 analysis halted.");
4300
4464
  }
4301
4465
  ctx.setUserInput("confirmedEntities", confirmed2);
4302
- track("AI Wizard Entities Confirmed", {
4303
- entities: toEntitySummary(confirmed2)
4466
+ const languages = ctx.getStepOutput(
4467
+ "confirm-language"
4468
+ )?.languages;
4469
+ const summary = toEntitySummary(confirmed2);
4470
+ track("AI Wizard Data Model Confirmed", {
4471
+ data_model: summary[0]?.name ?? "unknown",
4472
+ attribute_count: summary[0]?.attributes.length ?? 0,
4473
+ used_detected_back_end: usedDetectedBackend(scan.languages, languages)
4304
4474
  });
4305
4475
  return { ingestionAnalysis: entities, confirmedEntities: confirmed2 };
4306
4476
  }
@@ -4350,7 +4520,7 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
4350
4520
 
4351
4521
  // src/actions/implement.ts
4352
4522
  import z28 from "zod";
4353
- import { mkdir as mkdir5 } from "node:fs/promises";
4523
+ import { mkdir as mkdir5, readFile as readFile8 } from "node:fs/promises";
4354
4524
  import { join as join10, relative as relative6 } from "node:path";
4355
4525
 
4356
4526
  // src/lib/git.ts
@@ -4739,6 +4909,50 @@ function outputTail(output) {
4739
4909
  if (!trimmed) return void 0;
4740
4910
  return trimmed.length > INGEST_OUTPUT_TAIL_CHARS ? `\u2026${trimmed.slice(-INGEST_OUTPUT_TAIL_CHARS)}` : trimmed;
4741
4911
  }
4912
+ function jsTypeName(value) {
4913
+ if (value === null) return "null";
4914
+ if (Array.isArray(value)) return "array";
4915
+ const type = typeof value;
4916
+ if (type === "string" || type === "number" || type === "boolean" || type === "object") {
4917
+ return type;
4918
+ }
4919
+ return "unknown";
4920
+ }
4921
+ async function firstRecordIn(filePath) {
4922
+ try {
4923
+ const parsed = JSON.parse(await readFile8(filePath, "utf8"));
4924
+ const first = Array.isArray(parsed) ? parsed[0] : parsed;
4925
+ if (first && typeof first === "object" && !Array.isArray(first)) {
4926
+ return first;
4927
+ }
4928
+ } catch {
4929
+ return void 0;
4930
+ }
4931
+ return void 0;
4932
+ }
4933
+ async function ingestedRecordSample(repoRoot, entityName, uploadFilePath) {
4934
+ const candidates = [];
4935
+ if (entityName) {
4936
+ const slug = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
4937
+ candidates.push(join10(repoRoot, INGEST_DIR, "data", `${slug}.json`));
4938
+ }
4939
+ if (uploadFilePath?.toLowerCase().endsWith(".json")) {
4940
+ candidates.push(join10(repoRoot, uploadFilePath));
4941
+ }
4942
+ for (const candidate of candidates) {
4943
+ const sample = await firstRecordIn(candidate);
4944
+ if (sample) return sample;
4945
+ }
4946
+ return void 0;
4947
+ }
4948
+ function recordSchema(sample, attributes) {
4949
+ if (sample) {
4950
+ return Object.fromEntries(
4951
+ Object.entries(sample).map(([key, value]) => [key, jsTypeName(value)])
4952
+ );
4953
+ }
4954
+ return Object.fromEntries(attributes.map((key) => [key, "unknown"]));
4955
+ }
4742
4956
  function ingestFailure(attempt, executions) {
4743
4957
  if (!attempt) {
4744
4958
  return {
@@ -4968,11 +5182,24 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4968
5182
  if (ingestScriptRan) {
4969
5183
  ingestOutcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
4970
5184
  if (ingestRecordCount != null) {
5185
+ const data_model = confirmed2?.[0]?.name ?? "unknown";
5186
+ const sample = await ingestedRecordSample(
5187
+ repoRoot,
5188
+ confirmed2?.[0]?.name,
5189
+ uploadFilePath
5190
+ );
4971
5191
  track("AI Wizard Ingest Successful", {
4972
- entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
5192
+ data_model,
4973
5193
  record_count: ingestRecordCount,
4974
- duration_ms: ingestDurationMs ?? 0
5194
+ duration_ms: ingestDurationMs ?? 0,
5195
+ index_name: targetIndex,
5196
+ record_schema: recordSchema(sample, confirmed2?.[0]?.attributes ?? [])
4975
5197
  });
5198
+ } else {
5199
+ logger.warn(
5200
+ { ingestCommand },
5201
+ "implement: ingestion ran but printed no record count; skipping Ingest Successful event"
5202
+ );
4976
5203
  }
4977
5204
  } else {
4978
5205
  const { reason, detail } = ingestFailure(ingestAttempt, executions);
@@ -4996,7 +5223,10 @@ ${detail}` : ""}`
4996
5223
  track("Error", {
4997
5224
  step: "Push Data",
4998
5225
  error: `ingestion did not complete: ${reason}`,
4999
- product_area: "AI Wizard"
5226
+ error_message: `ingestion did not complete: ${reason}`,
5227
+ error_type: "Ingest Records",
5228
+ product_area: "AI Wizard",
5229
+ ...ingestAttempt?.exitCode != null ? { error_code: String(ingestAttempt.exitCode) } : {}
5000
5230
  });
5001
5231
  }
5002
5232
  if (ingestScriptRan) {
@@ -5030,6 +5260,29 @@ ${detail}` : ""}`
5030
5260
  let searchConfigFile;
5031
5261
  if (useCases.includes("search")) {
5032
5262
  let extraInstructions = [];
5263
+ const confirmedFrameworks = language.frameworks ?? [];
5264
+ const detectedFrameworks = scan.frameworks ?? [];
5265
+ const framework = confirmedFrameworks[0]?.name ?? "unknown";
5266
+ const used_detected_framework = confirmedFrameworks.some(
5267
+ (confirmedFw) => detectedFrameworks.some(
5268
+ (detectedFw) => isSameFramework(confirmedFw.name, detectedFw.name)
5269
+ )
5270
+ );
5271
+ const searchStartedAt = Date.now();
5272
+ const trackComponentGenerated = (mounted_successfully, attempts) => {
5273
+ const searchFilesChanged = [
5274
+ ...new Set(useWizard.getState().writtenFiles)
5275
+ ].map((file) => relative6(repoRoot, file));
5276
+ track("AI Wizard Frontend Component Generated", {
5277
+ file_paths: searchFilesChanged,
5278
+ framework,
5279
+ duration_ms: Date.now() - searchStartedAt,
5280
+ used_detected_framework,
5281
+ mounted_successfully,
5282
+ location_heuristic: searchLocation ?? "unknown",
5283
+ attempts
5284
+ });
5285
+ };
5033
5286
  useWizard.getState().clearWrittenFiles();
5034
5287
  for (let attempt = 1; attempt <= MAX_IMPLEMENT_VERIFICATION_ATTEMPTS; attempt++) {
5035
5288
  if (attempt > 1) {
@@ -5054,19 +5307,12 @@ ${detail}` : ""}`
5054
5307
  summaries.push(formatSummary("verification", verification.summary));
5055
5308
  if (verification.sufficient) {
5056
5309
  ctx.setUserInput("implementation", "success");
5057
- const searchFilesChanged = [
5058
- ...new Set(useWizard.getState().writtenFiles)
5059
- ].map((file) => relative6(repoRoot, file));
5060
- track("AI Wizard Frontend Component Generated", {
5061
- filePaths: searchFilesChanged
5062
- });
5063
- track("AI Wizard Wired to UI", {
5064
- location_heuristic: searchLocation ?? "unknown"
5065
- });
5310
+ trackComponentGenerated(true, attempt);
5066
5311
  break;
5067
5312
  }
5068
5313
  if (attempt === MAX_IMPLEMENT_VERIFICATION_ATTEMPTS) {
5069
5314
  ctx.setUserInput("implementation", "fail");
5315
+ trackComponentGenerated(false, attempt);
5070
5316
  throw new Error(
5071
5317
  `Implementation verification failed after ${MAX_IMPLEMENT_VERIFICATION_ATTEMPTS} attempts: ${verification.additionalInstructions ?? verification.summary}`
5072
5318
  );
@@ -5383,8 +5629,8 @@ function getWorkflow(id) {
5383
5629
  // src/ui/Welcome.tsx
5384
5630
  import { dirname as dirname5, join as join11 } from "node:path";
5385
5631
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5386
- import { useState as useState9 } from "react";
5387
- import { Box as Box11, Spacer, Text as Text12, useInput as useInput5, useWindowSize as useWindowSize5 } from "ink";
5632
+ import { useState as useState10 } from "react";
5633
+ import { Box as Box11, Spacer, Text as Text12, useInput as useInput6, useWindowSize as useWindowSize5 } from "ink";
5388
5634
 
5389
5635
  // src/ui/copy/welcome.ts
5390
5636
  var sidebarItems = [
@@ -5442,8 +5688,8 @@ function Welcome() {
5442
5688
  { label: "start wizard", run: confirmStart },
5443
5689
  { label: "learn more", run: openLearnMore }
5444
5690
  ];
5445
- const [index, setIndex] = useState9(0);
5446
- useInput5((input, key) => {
5691
+ const [index, setIndex] = useState10(0);
5692
+ useInput6((input, key) => {
5447
5693
  if (key.upArrow || input === "k") {
5448
5694
  setIndex((i) => (i - 1 + actions.length) % actions.length);
5449
5695
  } else if (key.downArrow || input === "j") {
@@ -5530,7 +5776,7 @@ function Welcome() {
5530
5776
 
5531
5777
  // src/ui/LearnMore.tsx
5532
5778
  import { Fragment } from "react";
5533
- import { Box as Box12, Text as Text13, useInput as useInput6, useWindowSize as useWindowSize6 } from "ink";
5779
+ import { Box as Box12, Text as Text13, useInput as useInput7, useWindowSize as useWindowSize6 } from "ink";
5534
5780
 
5535
5781
  // src/ui/copy/learn-more.ts
5536
5782
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -5602,7 +5848,7 @@ function LearnMore() {
5602
5848
  const backToHome = useWizard((s) => s.backToHome);
5603
5849
  const { columns } = useWindowSize6();
5604
5850
  const dividerWidth = Math.max(0, columns - PADDING_X * 2);
5605
- useInput6((_input, key) => {
5851
+ useInput7((_input, key) => {
5606
5852
  if (key.escape) backToHome();
5607
5853
  else if (key.return) confirmStart();
5608
5854
  });
@@ -5783,10 +6029,10 @@ function Ribbon() {
5783
6029
  }
5784
6030
 
5785
6031
  // src/ui/App.tsx
5786
- import { useState as useState11 } from "react";
6032
+ import { useState as useState12 } from "react";
5787
6033
 
5788
6034
  // src/ui/Logs.tsx
5789
- import { Box as Box17, Text as Text18, useInput as useInput7 } from "ink";
6035
+ import { Box as Box17, Text as Text18, useInput as useInput8 } from "ink";
5790
6036
  import { jsx as jsx16, jsxs as jsxs17 } from "react/jsx-runtime";
5791
6037
  var KIND_COLOR = {
5792
6038
  tool: COLORS.primary,
@@ -5818,9 +6064,9 @@ function formatTimestamp(ms) {
5818
6064
  function Logs() {
5819
6065
  const logs = useWizard((s) => s.logs);
5820
6066
  const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
5821
- useInput7((_input, key) => {
5822
- if (key.upArrow) scroll.scrollBy(-1);
5823
- else if (key.downArrow) scroll.scrollBy(1);
6067
+ useInput8((input, key) => {
6068
+ if (input === "k" || key.upArrow) scroll.scrollBy(-1);
6069
+ else if (input === "j" || key.downArrow) scroll.scrollBy(1);
5824
6070
  });
5825
6071
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
5826
6072
  return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
@@ -5842,12 +6088,15 @@ function Logs() {
5842
6088
  durationText && /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: durationText })
5843
6089
  ] }, entry.id);
5844
6090
  }) }),
5845
- /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
6091
+ /* @__PURE__ */ jsxs17(Text18, { children: [
6092
+ /* @__PURE__ */ jsx16(Text18, { color: COLORS.primary, children: "[j] [k]" }),
6093
+ /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: " scroll" })
6094
+ ] })
5846
6095
  ] });
5847
6096
  }
5848
6097
 
5849
6098
  // src/ui/Tips.tsx
5850
- import { useEffect as useEffect3, useRef as useRef4, useState as useState10 } from "react";
6099
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState11 } from "react";
5851
6100
  import { Box as Box19, Text as Text20 } from "ink";
5852
6101
  import terminalLink from "terminal-link";
5853
6102
 
@@ -6164,8 +6413,8 @@ function Tips({
6164
6413
  progress.tipIndex = 0;
6165
6414
  progress.revealed = 0;
6166
6415
  }
6167
- const [tipIndex, setTipIndex] = useState10(resuming ? progress.tipIndex : 0);
6168
- const [revealed, setRevealed] = useState10(resuming ? progress.revealed : 0);
6416
+ const [tipIndex, setTipIndex] = useState11(resuming ? progress.tipIndex : 0);
6417
+ const [revealed, setRevealed] = useState11(resuming ? progress.revealed : 0);
6169
6418
  const tip = tips2.length > 0 ? tips2[tipIndex % tips2.length] : void 0;
6170
6419
  const contentLength = tip?.chunks.reduce((sum, c) => sum + c.value.length, 0) ?? 0;
6171
6420
  const totalLength = (tip?.title.length ?? 0) + contentLength;
@@ -6247,8 +6496,8 @@ function App() {
6247
6496
  } = useWizard();
6248
6497
  const { exit } = useApp();
6249
6498
  const { columns, rows } = useWindowSize7();
6250
- const [showLogs, setShowLogs] = useState11(false);
6251
- const [tipState, setTipState] = useState11("idle");
6499
+ const [showLogs, setShowLogs] = useState12(false);
6500
+ const [tipState, setTipState] = useState12("idle");
6252
6501
  const finished = phase === "done" || phase === "error";
6253
6502
  const currentStep = steps[currentStepIndex];
6254
6503
  const stepDef = workflow ? getWorkflow(workflow.id)?.steps.find((s) => s.id === currentStep?.id) : void 0;
@@ -6261,7 +6510,7 @@ function App() {
6261
6510
  const showTips = stepHasTips && (phase === "running" && currentStep?.status === "running" || isCommandApprovalPrompt || holdForTip);
6262
6511
  const showNoticesInMain = !stepHasTips;
6263
6512
  const showNotices = (!isAwaitingUserInput || holdForTip) && !review2;
6264
- useInput8(
6513
+ useInput9(
6265
6514
  (_input, key) => {
6266
6515
  if (key.return) {
6267
6516
  exit();
@@ -6269,7 +6518,7 @@ function App() {
6269
6518
  },
6270
6519
  { isActive: finished }
6271
6520
  );
6272
- useInput8((_input, key) => {
6521
+ useInput9((_input, key) => {
6273
6522
  if (phase === "idle" || phase === "authenticating") return;
6274
6523
  if (key.tab) {
6275
6524
  setShowLogs(!showLogs);
@@ -6281,7 +6530,7 @@ function App() {
6281
6530
  }
6282
6531
  });
6283
6532
  const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && !holdForTip && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
6284
- useInput8((_input, key) => {
6533
+ useInput9((_input, key) => {
6285
6534
  if (escOwnedElsewhere) return;
6286
6535
  if (key.escape) {
6287
6536
  track("AI Wizard Interaction", {
@@ -6383,7 +6632,7 @@ function App() {
6383
6632
  }
6384
6633
 
6385
6634
  // src/lib/envAppId.ts
6386
- import { readFile as readFile8 } from "node:fs/promises";
6635
+ import { readFile as readFile9 } from "node:fs/promises";
6387
6636
  import { join as join12 } from "node:path";
6388
6637
  var ENV_FILES = [".env", ".env.local"];
6389
6638
  var APP_ID_LINE = /^[ \t]*(?:export[ \t]+)?([A-Z0-9_]*ALGOLIA_APP(?:LICATION)?_ID)[ \t]*=[ \t]*(.*)$/gm;
@@ -6391,7 +6640,7 @@ async function findEnvApplicationId(root = process.cwd()) {
6391
6640
  for (const file of ENV_FILES) {
6392
6641
  let content;
6393
6642
  try {
6394
- content = await readFile8(join12(root, file), "utf8");
6643
+ content = await readFile9(join12(root, file), "utf8");
6395
6644
  } catch (err) {
6396
6645
  if (err.code !== "ENOENT") {
6397
6646
  logger.warn(
@@ -6744,7 +6993,7 @@ function delay(ms) {
6744
6993
  // package.json with { type: 'json' }
6745
6994
  var package_default2 = {
6746
6995
  name: "@algolia/wizard",
6747
- version: "0.61.0",
6996
+ version: "0.63.0",
6748
6997
  description: "Magically implement Algolia functionality in your codebase",
6749
6998
  type: "module",
6750
6999
  engines: {