@algolia/wizard 0.22.0 → 0.24.0-rc.111.205
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -14
- package/dist/main.js +557 -610
- 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
|
|
7
|
+
import { Box as Box17, Text as Text17, useApp, useInput as useInput7, useWindowSize as useWindowSize8 } from "ink";
|
|
8
8
|
|
|
9
9
|
// src/core/store.ts
|
|
10
10
|
import { create } from "zustand";
|
|
@@ -251,6 +251,7 @@ var useWizard = create((set, get) => ({
|
|
|
251
251
|
_noticeTimer: null,
|
|
252
252
|
cliOutput: [],
|
|
253
253
|
targetIndex: null,
|
|
254
|
+
writtenFiles: [],
|
|
254
255
|
logs: [],
|
|
255
256
|
error: null,
|
|
256
257
|
inputReq: null,
|
|
@@ -344,6 +345,8 @@ var useWizard = create((set, get) => ({
|
|
|
344
345
|
})),
|
|
345
346
|
clearCliOutput: () => set({ cliOutput: [] }),
|
|
346
347
|
setTargetIndex: (index) => set({ targetIndex: index }),
|
|
348
|
+
recordWrittenFile: (path) => set((s) => ({ writtenFiles: [...s.writtenFiles, path] })),
|
|
349
|
+
clearWrittenFiles: () => set({ writtenFiles: [] }),
|
|
347
350
|
logStart: (kind, name, input) => {
|
|
348
351
|
const id = nanoid();
|
|
349
352
|
set((s) => ({
|
|
@@ -391,6 +394,7 @@ var useWizard = create((set, get) => ({
|
|
|
391
394
|
notices: [],
|
|
392
395
|
cliOutput: [],
|
|
393
396
|
targetIndex: null,
|
|
397
|
+
writtenFiles: [],
|
|
394
398
|
logs: [],
|
|
395
399
|
error: null,
|
|
396
400
|
inputReq: null,
|
|
@@ -632,7 +636,7 @@ function Notices() {
|
|
|
632
636
|
}
|
|
633
637
|
|
|
634
638
|
// src/ui/PromptInput.tsx
|
|
635
|
-
import { Box as
|
|
639
|
+
import { Box as Box9, Text as Text9, useInput as useInput3 } from "ink";
|
|
636
640
|
import TextInput from "ink-text-input";
|
|
637
641
|
import { useState as useState5 } from "react";
|
|
638
642
|
|
|
@@ -709,7 +713,7 @@ function CommandApproval({
|
|
|
709
713
|
}
|
|
710
714
|
|
|
711
715
|
// src/ui/SelectPrompt.tsx
|
|
712
|
-
import { Box as
|
|
716
|
+
import { Box as Box8, Text as Text8, useInput as useInput2, useWindowSize as useWindowSize5 } from "ink";
|
|
713
717
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
714
718
|
|
|
715
719
|
// src/ui/ScrollView.tsx
|
|
@@ -800,8 +804,47 @@ function ScrollView({ scroll, children }) {
|
|
|
800
804
|
] });
|
|
801
805
|
}
|
|
802
806
|
|
|
803
|
-
// src/ui/
|
|
807
|
+
// src/ui/SelectRow.tsx
|
|
808
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
804
809
|
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
810
|
+
function SelectRow({
|
|
811
|
+
highlighted,
|
|
812
|
+
label,
|
|
813
|
+
width,
|
|
814
|
+
labelWidth,
|
|
815
|
+
highlightBackground = true,
|
|
816
|
+
usePadding = false,
|
|
817
|
+
children
|
|
818
|
+
}) {
|
|
819
|
+
const labelColor = highlighted ? highlightBackground ? COLORS.highlight.fg : COLORS.success : void 0;
|
|
820
|
+
return /* @__PURE__ */ jsxs6(
|
|
821
|
+
Box7,
|
|
822
|
+
{
|
|
823
|
+
width,
|
|
824
|
+
paddingX: usePadding ? 1 : 0,
|
|
825
|
+
paddingY: usePadding ? 1 : 0,
|
|
826
|
+
backgroundColor: highlighted && highlightBackground ? COLORS.highlight.bg : void 0,
|
|
827
|
+
children: [
|
|
828
|
+
/* @__PURE__ */ jsx5(Box7, { width: labelWidth, children: /* @__PURE__ */ jsxs6(
|
|
829
|
+
Text7,
|
|
830
|
+
{
|
|
831
|
+
color: labelColor,
|
|
832
|
+
bold: highlighted && !highlightBackground,
|
|
833
|
+
wrap: "truncate",
|
|
834
|
+
children: [
|
|
835
|
+
highlighted ? "\u276F " : " ",
|
|
836
|
+
label
|
|
837
|
+
]
|
|
838
|
+
}
|
|
839
|
+
) }),
|
|
840
|
+
children
|
|
841
|
+
]
|
|
842
|
+
}
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/ui/SelectPrompt.tsx
|
|
847
|
+
import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
805
848
|
var CANCEL = "cancel";
|
|
806
849
|
var ARROW_WIDTH = 4;
|
|
807
850
|
var COLUMN_GAP = 2;
|
|
@@ -886,56 +929,50 @@ function SelectPrompt({
|
|
|
886
929
|
}
|
|
887
930
|
}
|
|
888
931
|
});
|
|
889
|
-
return /* @__PURE__ */
|
|
890
|
-
/* @__PURE__ */
|
|
891
|
-
error && /* @__PURE__ */
|
|
892
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
893
|
-
table && /* @__PURE__ */
|
|
894
|
-
/* @__PURE__ */
|
|
895
|
-
question && /* @__PURE__ */
|
|
896
|
-
helpText && /* @__PURE__ */
|
|
932
|
+
return /* @__PURE__ */ jsx6(Box8, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, width, children: [
|
|
933
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
|
|
934
|
+
error && /* @__PURE__ */ jsx6(Text8, { color: COLORS.danger, children: error }),
|
|
935
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
936
|
+
table && /* @__PURE__ */ jsx6(Table, { columns: table.columns, rows: table.rows }),
|
|
937
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
938
|
+
question && /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: question }),
|
|
939
|
+
helpText && /* @__PURE__ */ jsx6(Text8, { color: COLORS.dim, children: helpText })
|
|
897
940
|
] })
|
|
898
941
|
] }),
|
|
899
|
-
/* @__PURE__ */
|
|
942
|
+
/* @__PURE__ */ jsx6(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
|
|
900
943
|
const i = scroll.offset + visibleIndex;
|
|
901
944
|
const highlighted = i === index;
|
|
902
945
|
const isCancel = i === cancelIndex;
|
|
903
946
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
904
947
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
905
|
-
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
906
|
-
const label = /* @__PURE__ */ jsxs6(Text7, { color: labelColor, wrap: "truncate", children: [
|
|
907
|
-
highlighted ? "\u276F " : " ",
|
|
908
|
-
bullet,
|
|
909
|
-
option
|
|
910
|
-
] });
|
|
911
948
|
const isText = sec?.kind === "text";
|
|
912
|
-
return /* @__PURE__ */
|
|
913
|
-
|
|
949
|
+
return /* @__PURE__ */ jsxs7(
|
|
950
|
+
SelectRow,
|
|
914
951
|
{
|
|
952
|
+
highlighted,
|
|
915
953
|
width: isText ? "100%" : barWidth,
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
954
|
+
labelWidth: isText ? labelWidth : barLabelWidth,
|
|
955
|
+
label: `${bullet}${option}`,
|
|
956
|
+
usePadding: true,
|
|
919
957
|
children: [
|
|
920
|
-
/* @__PURE__ */
|
|
921
|
-
|
|
922
|
-
Text7,
|
|
958
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx6(Box8, { width: textWidth, children: /* @__PURE__ */ jsx6(
|
|
959
|
+
Text8,
|
|
923
960
|
{
|
|
924
961
|
wrap: "truncate",
|
|
925
962
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
926
963
|
children: sec.value
|
|
927
964
|
}
|
|
928
965
|
) }),
|
|
929
|
-
sec?.kind === "badge" && /* @__PURE__ */
|
|
966
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx6(Box8, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx6(Text8, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
930
967
|
]
|
|
931
968
|
},
|
|
932
969
|
`row-${i}`
|
|
933
970
|
);
|
|
934
971
|
}) }),
|
|
935
|
-
/* @__PURE__ */
|
|
972
|
+
/* @__PURE__ */ jsx6(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx6(Text8, { color: COLORS.dim, children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs7(Text8, { color: COLORS.dim, children: [
|
|
936
973
|
i > 0 ? " " : "",
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
974
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.primary, children: key }),
|
|
975
|
+
/* @__PURE__ */ jsxs7(Text8, { color: COLORS.dim, children: [
|
|
939
976
|
" ",
|
|
940
977
|
label
|
|
941
978
|
] })
|
|
@@ -944,7 +981,7 @@ function SelectPrompt({
|
|
|
944
981
|
}
|
|
945
982
|
|
|
946
983
|
// src/ui/PromptInput.tsx
|
|
947
|
-
import { jsx as
|
|
984
|
+
import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
948
985
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
949
986
|
function EnterToContinuePrompt({
|
|
950
987
|
question,
|
|
@@ -955,12 +992,12 @@ function EnterToContinuePrompt({
|
|
|
955
992
|
if (key.return) onDecide(true);
|
|
956
993
|
else if (key.escape) onDecide(false);
|
|
957
994
|
});
|
|
958
|
-
return /* @__PURE__ */
|
|
959
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
960
|
-
question && /* @__PURE__ */
|
|
961
|
-
/* @__PURE__ */
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
/* @__PURE__ */
|
|
995
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 1, children: [
|
|
996
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
997
|
+
question && /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: question }),
|
|
998
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, flexDirection: "column", children: [
|
|
999
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "continue", keyHint: "enter" }),
|
|
1000
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
964
1001
|
] })
|
|
965
1002
|
] });
|
|
966
1003
|
}
|
|
@@ -968,11 +1005,11 @@ function PromptInput() {
|
|
|
968
1005
|
const { phase, inputReq, submitInput } = useWizard();
|
|
969
1006
|
const [draft, setDraft] = useState5("");
|
|
970
1007
|
if (phase === "done" || phase === "error") {
|
|
971
|
-
return /* @__PURE__ */
|
|
1008
|
+
return /* @__PURE__ */ jsx7(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text9, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
972
1009
|
}
|
|
973
1010
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
974
1011
|
if (inputReq.promptType === "multipleChoice") {
|
|
975
|
-
return /* @__PURE__ */
|
|
1012
|
+
return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
|
|
976
1013
|
SelectPrompt,
|
|
977
1014
|
{
|
|
978
1015
|
question: inputReq.prompt,
|
|
@@ -989,7 +1026,7 @@ function PromptInput() {
|
|
|
989
1026
|
) });
|
|
990
1027
|
}
|
|
991
1028
|
if (inputReq.promptType === "multiSelect") {
|
|
992
|
-
return /* @__PURE__ */
|
|
1029
|
+
return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
|
|
993
1030
|
SelectPrompt,
|
|
994
1031
|
{
|
|
995
1032
|
multi: true,
|
|
@@ -1004,7 +1041,7 @@ function PromptInput() {
|
|
|
1004
1041
|
) });
|
|
1005
1042
|
}
|
|
1006
1043
|
if (inputReq.promptType === "notice") {
|
|
1007
|
-
return /* @__PURE__ */
|
|
1044
|
+
return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
|
|
1008
1045
|
SelectPrompt,
|
|
1009
1046
|
{
|
|
1010
1047
|
question: inputReq.prompt,
|
|
@@ -1015,7 +1052,7 @@ function PromptInput() {
|
|
|
1015
1052
|
) });
|
|
1016
1053
|
}
|
|
1017
1054
|
if (inputReq.promptType === "enterToContinue") {
|
|
1018
|
-
return /* @__PURE__ */
|
|
1055
|
+
return /* @__PURE__ */ jsx7(
|
|
1019
1056
|
EnterToContinuePrompt,
|
|
1020
1057
|
{
|
|
1021
1058
|
question: inputReq.prompt,
|
|
@@ -1025,11 +1062,11 @@ function PromptInput() {
|
|
|
1025
1062
|
);
|
|
1026
1063
|
}
|
|
1027
1064
|
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1028
|
-
return /* @__PURE__ */
|
|
1065
|
+
return /* @__PURE__ */ jsx7(CommandApproval, { command: inputReq.command, onDecide: submitInput });
|
|
1029
1066
|
}
|
|
1030
1067
|
if (inputReq.promptType === "acceptReject") {
|
|
1031
1068
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
1032
|
-
return /* @__PURE__ */
|
|
1069
|
+
return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
|
|
1033
1070
|
SelectPrompt,
|
|
1034
1071
|
{
|
|
1035
1072
|
question: inputReq.prompt,
|
|
@@ -1040,15 +1077,15 @@ function PromptInput() {
|
|
|
1040
1077
|
}
|
|
1041
1078
|
) });
|
|
1042
1079
|
}
|
|
1043
|
-
return /* @__PURE__ */
|
|
1044
|
-
inputReq.error && /* @__PURE__ */
|
|
1045
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
1046
|
-
/* @__PURE__ */
|
|
1047
|
-
/* @__PURE__ */
|
|
1080
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
|
|
1081
|
+
inputReq.error && /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: inputReq.error }),
|
|
1082
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
1083
|
+
/* @__PURE__ */ jsxs8(Box9, { children: [
|
|
1084
|
+
/* @__PURE__ */ jsxs8(Text9, { color: COLORS.primary, children: [
|
|
1048
1085
|
inputReq.prompt,
|
|
1049
1086
|
" "
|
|
1050
1087
|
] }),
|
|
1051
|
-
/* @__PURE__ */
|
|
1088
|
+
/* @__PURE__ */ jsx7(
|
|
1052
1089
|
TextInput,
|
|
1053
1090
|
{
|
|
1054
1091
|
value: draft,
|
|
@@ -1066,7 +1103,8 @@ function PromptInput() {
|
|
|
1066
1103
|
// src/ui/Welcome.tsx
|
|
1067
1104
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1068
1105
|
import { fileURLToPath } from "node:url";
|
|
1069
|
-
import {
|
|
1106
|
+
import { useState as useState6 } from "react";
|
|
1107
|
+
import { Box as Box10, Spacer, Text as Text10, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
1070
1108
|
|
|
1071
1109
|
// src/ui/copy/welcome.ts
|
|
1072
1110
|
var sidebarItems = [
|
|
@@ -1094,7 +1132,7 @@ var sidebarItems = [
|
|
|
1094
1132
|
|
|
1095
1133
|
// src/ui/Welcome.tsx
|
|
1096
1134
|
import Image, { TerminalInfoContext, defaultTerminalInfo } from "ink-picture";
|
|
1097
|
-
import { jsx as
|
|
1135
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1098
1136
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
1099
1137
|
var TERMINAL_INFO = {
|
|
1100
1138
|
...defaultTerminalInfo,
|
|
@@ -1105,14 +1143,14 @@ function SidebarItem({
|
|
|
1105
1143
|
title,
|
|
1106
1144
|
description
|
|
1107
1145
|
}) {
|
|
1108
|
-
return /* @__PURE__ */
|
|
1109
|
-
/* @__PURE__ */
|
|
1110
|
-
/* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1146
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
1147
|
+
/* @__PURE__ */ jsxs9(Box10, { gap: 1, children: [
|
|
1148
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.success, children: "\u2192" }),
|
|
1149
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: title })
|
|
1112
1150
|
] }),
|
|
1113
|
-
/* @__PURE__ */
|
|
1114
|
-
/* @__PURE__ */
|
|
1115
|
-
/* @__PURE__ */
|
|
1151
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 2, children: [
|
|
1152
|
+
/* @__PURE__ */ jsx8(Spacer, {}),
|
|
1153
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: description })
|
|
1116
1154
|
] })
|
|
1117
1155
|
] });
|
|
1118
1156
|
}
|
|
@@ -1120,9 +1158,19 @@ function Welcome() {
|
|
|
1120
1158
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
1121
1159
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
1122
1160
|
const { rows } = useWindowSize6();
|
|
1161
|
+
const actions = [
|
|
1162
|
+
{ label: "start wizard", run: confirmStart },
|
|
1163
|
+
{ label: "learn more", run: openLearnMore }
|
|
1164
|
+
];
|
|
1165
|
+
const [index, setIndex] = useState6(0);
|
|
1123
1166
|
useInput4((input, key) => {
|
|
1124
|
-
if (key.
|
|
1125
|
-
|
|
1167
|
+
if (key.upArrow || input === "k") {
|
|
1168
|
+
setIndex((i) => (i - 1 + actions.length) % actions.length);
|
|
1169
|
+
} else if (key.downArrow || input === "j") {
|
|
1170
|
+
setIndex((i) => (i + 1) % actions.length);
|
|
1171
|
+
} else if (key.return) {
|
|
1172
|
+
actions[index].run();
|
|
1173
|
+
}
|
|
1126
1174
|
});
|
|
1127
1175
|
const scales = {
|
|
1128
1176
|
large: {
|
|
@@ -1138,16 +1186,16 @@ function Welcome() {
|
|
|
1138
1186
|
if (rows < 30) {
|
|
1139
1187
|
layout = scales["small"];
|
|
1140
1188
|
}
|
|
1141
|
-
return /* @__PURE__ */
|
|
1142
|
-
/* @__PURE__ */
|
|
1143
|
-
|
|
1189
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
1190
|
+
/* @__PURE__ */ jsx8(
|
|
1191
|
+
Box10,
|
|
1144
1192
|
{
|
|
1145
1193
|
paddingY: layout.main.padding.y,
|
|
1146
1194
|
paddingX: layout.main.padding.x,
|
|
1147
1195
|
flexDirection: "column",
|
|
1148
1196
|
justifyContent: "center",
|
|
1149
|
-
children: /* @__PURE__ */
|
|
1150
|
-
/* @__PURE__ */
|
|
1197
|
+
children: /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 2, children: [
|
|
1198
|
+
/* @__PURE__ */ jsx8(TerminalInfoContext.Provider, { value: TERMINAL_INFO, children: /* @__PURE__ */ jsx8(
|
|
1151
1199
|
Image,
|
|
1152
1200
|
{
|
|
1153
1201
|
src: IMAGE_PATH,
|
|
@@ -1158,16 +1206,31 @@ function Welcome() {
|
|
|
1158
1206
|
protocol: "halfBlock"
|
|
1159
1207
|
}
|
|
1160
1208
|
) }),
|
|
1161
|
-
/* @__PURE__ */
|
|
1162
|
-
/* @__PURE__ */
|
|
1163
|
-
|
|
1164
|
-
|
|
1209
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1210
|
+
/* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: actions.map((action, i) => /* @__PURE__ */ jsx8(
|
|
1211
|
+
SelectRow,
|
|
1212
|
+
{
|
|
1213
|
+
highlighted: i === index,
|
|
1214
|
+
highlightBackground: false,
|
|
1215
|
+
label: action.label
|
|
1216
|
+
},
|
|
1217
|
+
action.label
|
|
1218
|
+
)) }),
|
|
1219
|
+
/* @__PURE__ */ jsxs9(Box10, { gap: 2, children: [
|
|
1220
|
+
/* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1221
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
|
|
1222
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.dim, children: " move" })
|
|
1223
|
+
] }),
|
|
1224
|
+
/* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1225
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "[enter]" }),
|
|
1226
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.dim, children: " confirm" })
|
|
1227
|
+
] })
|
|
1165
1228
|
] })
|
|
1166
1229
|
] })
|
|
1167
1230
|
}
|
|
1168
1231
|
),
|
|
1169
|
-
/* @__PURE__ */
|
|
1170
|
-
|
|
1232
|
+
/* @__PURE__ */ jsxs9(
|
|
1233
|
+
Box10,
|
|
1171
1234
|
{
|
|
1172
1235
|
backgroundColor: COLORS.bg.sidebar,
|
|
1173
1236
|
width: 40,
|
|
@@ -1177,8 +1240,8 @@ function Welcome() {
|
|
|
1177
1240
|
flexDirection: "column",
|
|
1178
1241
|
justifyContent: "center",
|
|
1179
1242
|
children: [
|
|
1180
|
-
/* @__PURE__ */
|
|
1181
|
-
sidebarItems.map((i, idx) => /* @__PURE__ */
|
|
1243
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
1244
|
+
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx8(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
1182
1245
|
]
|
|
1183
1246
|
}
|
|
1184
1247
|
)
|
|
@@ -1187,7 +1250,7 @@ function Welcome() {
|
|
|
1187
1250
|
|
|
1188
1251
|
// src/ui/LearnMore.tsx
|
|
1189
1252
|
import { Fragment as Fragment2 } from "react";
|
|
1190
|
-
import { Box as
|
|
1253
|
+
import { Box as Box11, Text as Text11, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1191
1254
|
|
|
1192
1255
|
// src/ui/copy/learn-more.ts
|
|
1193
1256
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -1200,7 +1263,7 @@ var accessItems = [
|
|
|
1200
1263
|
{
|
|
1201
1264
|
tag: "WRITE",
|
|
1202
1265
|
title: "Code changes",
|
|
1203
|
-
description: "creates & edits files (search UI, config) in
|
|
1266
|
+
description: "creates & edits files (search UI, config) directly in your branch."
|
|
1204
1267
|
},
|
|
1205
1268
|
{
|
|
1206
1269
|
tag: "EXEC",
|
|
@@ -1215,7 +1278,7 @@ var accessItems = [
|
|
|
1215
1278
|
{
|
|
1216
1279
|
tag: "KEY",
|
|
1217
1280
|
title: "Credentials",
|
|
1218
|
-
description: "writes your Algolia app id and a search-only key (safe to expose) to .env in
|
|
1281
|
+
description: "writes your Algolia app id and a search-only key (safe to expose) to .env in your project."
|
|
1219
1282
|
}
|
|
1220
1283
|
];
|
|
1221
1284
|
var neverItems = [
|
|
@@ -1229,7 +1292,7 @@ var policyLinks = [
|
|
|
1229
1292
|
];
|
|
1230
1293
|
|
|
1231
1294
|
// src/ui/LearnMore.tsx
|
|
1232
|
-
import { jsx as
|
|
1295
|
+
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1233
1296
|
var TAG_COLORS = {
|
|
1234
1297
|
READ: COLORS.success,
|
|
1235
1298
|
WRITE: COLORS.badge,
|
|
@@ -1246,12 +1309,12 @@ function NeverLine({
|
|
|
1246
1309
|
}) {
|
|
1247
1310
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
1248
1311
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
1249
|
-
return /* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1312
|
+
return /* @__PURE__ */ jsxs10(Text11, { color: COLORS.primary, children: [
|
|
1313
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.danger, children: "\u2502" }),
|
|
1251
1314
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
1252
|
-
segments.map((s, i) => /* @__PURE__ */
|
|
1315
|
+
segments.map((s, i) => /* @__PURE__ */ jsx9(Text11, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
1253
1316
|
" ".repeat(rightPad),
|
|
1254
|
-
/* @__PURE__ */
|
|
1317
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.danger, children: "\u2502" })
|
|
1255
1318
|
] });
|
|
1256
1319
|
}
|
|
1257
1320
|
function LearnMore() {
|
|
@@ -1263,8 +1326,8 @@ function LearnMore() {
|
|
|
1263
1326
|
if (key.escape) backToHome();
|
|
1264
1327
|
else if (key.return) confirmStart();
|
|
1265
1328
|
});
|
|
1266
|
-
return /* @__PURE__ */
|
|
1267
|
-
|
|
1329
|
+
return /* @__PURE__ */ jsxs10(
|
|
1330
|
+
Box11,
|
|
1268
1331
|
{
|
|
1269
1332
|
flexDirection: "column",
|
|
1270
1333
|
paddingX: PADDING_X,
|
|
@@ -1272,31 +1335,31 @@ function LearnMore() {
|
|
|
1272
1335
|
width: "100%",
|
|
1273
1336
|
gap: 1,
|
|
1274
1337
|
children: [
|
|
1275
|
-
/* @__PURE__ */
|
|
1276
|
-
/* @__PURE__ */
|
|
1277
|
-
/* @__PURE__ */
|
|
1278
|
-
/* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1280
|
-
/* @__PURE__ */
|
|
1281
|
-
/* @__PURE__ */
|
|
1282
|
-
/* @__PURE__ */
|
|
1283
|
-
/* @__PURE__ */
|
|
1338
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1339
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: accessIntro }),
|
|
1340
|
+
/* @__PURE__ */ jsx9(Box11, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
1341
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1342
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1343
|
+
/* @__PURE__ */ jsx9(Box11, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text11, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1344
|
+
/* @__PURE__ */ jsx9(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs10(Text11, { color: COLORS.primary, children: [
|
|
1345
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1346
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
1284
1347
|
] }) })
|
|
1285
1348
|
] })
|
|
1286
1349
|
] }, item.tag)) }),
|
|
1287
|
-
/* @__PURE__ */
|
|
1288
|
-
/* @__PURE__ */
|
|
1289
|
-
/* @__PURE__ */
|
|
1290
|
-
/* @__PURE__ */
|
|
1350
|
+
/* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexDirection: "column", children: [
|
|
1351
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
1352
|
+
/* @__PURE__ */ jsx9(NeverLine, { width: dividerWidth }),
|
|
1353
|
+
/* @__PURE__ */ jsx9(
|
|
1291
1354
|
NeverLine,
|
|
1292
1355
|
{
|
|
1293
1356
|
width: dividerWidth,
|
|
1294
1357
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1295
1358
|
}
|
|
1296
1359
|
),
|
|
1297
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1298
|
-
/* @__PURE__ */
|
|
1299
|
-
/* @__PURE__ */
|
|
1360
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
1361
|
+
/* @__PURE__ */ jsx9(NeverLine, { width: dividerWidth }),
|
|
1362
|
+
/* @__PURE__ */ jsx9(
|
|
1300
1363
|
NeverLine,
|
|
1301
1364
|
{
|
|
1302
1365
|
width: dividerWidth,
|
|
@@ -1308,24 +1371,24 @@ function LearnMore() {
|
|
|
1308
1371
|
}
|
|
1309
1372
|
)
|
|
1310
1373
|
] }, item)),
|
|
1311
|
-
/* @__PURE__ */
|
|
1312
|
-
/* @__PURE__ */
|
|
1374
|
+
/* @__PURE__ */ jsx9(NeverLine, { width: dividerWidth }),
|
|
1375
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1313
1376
|
] }),
|
|
1314
|
-
/* @__PURE__ */
|
|
1315
|
-
/* @__PURE__ */
|
|
1316
|
-
/* @__PURE__ */
|
|
1377
|
+
/* @__PURE__ */ jsx9(Box11, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1378
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1379
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.accent, children: link.url })
|
|
1317
1380
|
] }, link.label)) }),
|
|
1318
|
-
/* @__PURE__ */
|
|
1319
|
-
/* @__PURE__ */
|
|
1320
|
-
/* @__PURE__ */
|
|
1321
|
-
/* @__PURE__ */
|
|
1322
|
-
/* @__PURE__ */
|
|
1381
|
+
/* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1382
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1383
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "[" }),
|
|
1384
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.primary, children: "esc" }),
|
|
1385
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "] back" })
|
|
1323
1386
|
] }),
|
|
1324
|
-
/* @__PURE__ */
|
|
1325
|
-
/* @__PURE__ */
|
|
1326
|
-
/* @__PURE__ */
|
|
1327
|
-
/* @__PURE__ */
|
|
1328
|
-
/* @__PURE__ */
|
|
1387
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1388
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "[" }),
|
|
1389
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.primary, children: "enter" }),
|
|
1390
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "]" }),
|
|
1391
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1329
1392
|
] })
|
|
1330
1393
|
] })
|
|
1331
1394
|
]
|
|
@@ -1334,10 +1397,10 @@ function LearnMore() {
|
|
|
1334
1397
|
}
|
|
1335
1398
|
|
|
1336
1399
|
// src/ui/Sidebar.tsx
|
|
1337
|
-
import { Box as
|
|
1400
|
+
import { Box as Box14, Text as Text14 } from "ink";
|
|
1338
1401
|
|
|
1339
1402
|
// src/ui/Steps.tsx
|
|
1340
|
-
import { Box as
|
|
1403
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1341
1404
|
import Spinner from "ink-spinner";
|
|
1342
1405
|
|
|
1343
1406
|
// src/core/persistence.ts
|
|
@@ -1366,12 +1429,12 @@ async function clearWorkflowState(workflowId) {
|
|
|
1366
1429
|
}
|
|
1367
1430
|
|
|
1368
1431
|
// src/ui/Steps.tsx
|
|
1369
|
-
import { jsx as
|
|
1432
|
+
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1370
1433
|
function Steps() {
|
|
1371
1434
|
const { steps } = useWizard();
|
|
1372
1435
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1373
|
-
return /* @__PURE__ */
|
|
1374
|
-
s.status === "running" ? /* @__PURE__ */
|
|
1436
|
+
return /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs11(Text12, { color: COLORS.status[s.status], children: [
|
|
1437
|
+
s.status === "running" ? /* @__PURE__ */ jsx10(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1375
1438
|
" ",
|
|
1376
1439
|
s.title
|
|
1377
1440
|
] }) }, s.id)) });
|
|
@@ -1380,27 +1443,27 @@ function CurrentStep() {
|
|
|
1380
1443
|
const { steps } = useWizard();
|
|
1381
1444
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1382
1445
|
if (!currentStep) return null;
|
|
1383
|
-
return /* @__PURE__ */
|
|
1384
|
-
/* @__PURE__ */
|
|
1446
|
+
return /* @__PURE__ */ jsxs11(Text12, { color: COLORS.status.running, children: [
|
|
1447
|
+
/* @__PURE__ */ jsx10(Spinner, { type: "dots" }),
|
|
1385
1448
|
" ",
|
|
1386
1449
|
` ${currentStep.title}`
|
|
1387
1450
|
] });
|
|
1388
1451
|
}
|
|
1389
1452
|
|
|
1390
1453
|
// src/ui/Progress.tsx
|
|
1391
|
-
import { Box as
|
|
1392
|
-
import { jsx as
|
|
1454
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1455
|
+
import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1393
1456
|
function Progress() {
|
|
1394
1457
|
const { steps, currentStepIndex } = useWizard();
|
|
1395
1458
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1396
1459
|
if (visibleSteps.length === 0) return null;
|
|
1397
1460
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1398
1461
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1399
|
-
return /* @__PURE__ */
|
|
1400
|
-
/* @__PURE__ */
|
|
1401
|
-
/* @__PURE__ */
|
|
1402
|
-
/* @__PURE__ */
|
|
1403
|
-
/* @__PURE__ */
|
|
1462
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
|
|
1463
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "STEP" }),
|
|
1464
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: activeStepNumber }),
|
|
1465
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: "/" }),
|
|
1466
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: visibleSteps.length })
|
|
1404
1467
|
] });
|
|
1405
1468
|
}
|
|
1406
1469
|
|
|
@@ -1411,10 +1474,10 @@ var sidebarCommands = [
|
|
|
1411
1474
|
];
|
|
1412
1475
|
|
|
1413
1476
|
// src/ui/Sidebar.tsx
|
|
1414
|
-
import { jsx as
|
|
1477
|
+
import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1415
1478
|
function Sidebar() {
|
|
1416
|
-
return /* @__PURE__ */
|
|
1417
|
-
|
|
1479
|
+
return /* @__PURE__ */ jsxs13(
|
|
1480
|
+
Box14,
|
|
1418
1481
|
{
|
|
1419
1482
|
backgroundColor: "#14171E",
|
|
1420
1483
|
width: 30,
|
|
@@ -1423,16 +1486,16 @@ function Sidebar() {
|
|
|
1423
1486
|
flexDirection: "column",
|
|
1424
1487
|
justifyContent: "space-between",
|
|
1425
1488
|
children: [
|
|
1426
|
-
/* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
/* @__PURE__ */
|
|
1489
|
+
/* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", gap: 1, children: [
|
|
1490
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1491
|
+
/* @__PURE__ */ jsx12(Steps, {})
|
|
1429
1492
|
] }),
|
|
1430
|
-
/* @__PURE__ */
|
|
1431
|
-
/* @__PURE__ */
|
|
1432
|
-
/* @__PURE__ */
|
|
1433
|
-
return /* @__PURE__ */
|
|
1434
|
-
/* @__PURE__ */
|
|
1435
|
-
/* @__PURE__ */
|
|
1493
|
+
/* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", gap: 1, children: [
|
|
1494
|
+
/* @__PURE__ */ jsx12(Progress, {}),
|
|
1495
|
+
/* @__PURE__ */ jsx12(Box14, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1496
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: 1, children: [
|
|
1497
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1498
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: c.description })
|
|
1436
1499
|
] });
|
|
1437
1500
|
}) })
|
|
1438
1501
|
] })
|
|
@@ -1442,12 +1505,12 @@ function Sidebar() {
|
|
|
1442
1505
|
}
|
|
1443
1506
|
|
|
1444
1507
|
// src/ui/Ribbon.tsx
|
|
1445
|
-
import { Box as
|
|
1446
|
-
import { jsx as
|
|
1508
|
+
import { Box as Box15, Text as Text15 } from "ink";
|
|
1509
|
+
import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1447
1510
|
function Ribbon() {
|
|
1448
1511
|
const firstCommand = sidebarCommands[0];
|
|
1449
|
-
return /* @__PURE__ */
|
|
1450
|
-
|
|
1512
|
+
return /* @__PURE__ */ jsxs14(
|
|
1513
|
+
Box15,
|
|
1451
1514
|
{
|
|
1452
1515
|
backgroundColor: "#14171E",
|
|
1453
1516
|
flexDirection: "row",
|
|
@@ -1455,11 +1518,11 @@ function Ribbon() {
|
|
|
1455
1518
|
paddingX: 2,
|
|
1456
1519
|
paddingY: 1,
|
|
1457
1520
|
children: [
|
|
1458
|
-
/* @__PURE__ */
|
|
1459
|
-
/* @__PURE__ */
|
|
1460
|
-
/* @__PURE__ */
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
/* @__PURE__ */
|
|
1521
|
+
/* @__PURE__ */ jsx13(Progress, {}),
|
|
1522
|
+
/* @__PURE__ */ jsx13(CurrentStep, {}),
|
|
1523
|
+
/* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", gap: 1, children: [
|
|
1524
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1525
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: firstCommand.description })
|
|
1463
1526
|
] })
|
|
1464
1527
|
]
|
|
1465
1528
|
}
|
|
@@ -1467,11 +1530,11 @@ function Ribbon() {
|
|
|
1467
1530
|
}
|
|
1468
1531
|
|
|
1469
1532
|
// src/ui/App.tsx
|
|
1470
|
-
import { useState as
|
|
1533
|
+
import { useState as useState7 } from "react";
|
|
1471
1534
|
|
|
1472
1535
|
// src/ui/Logs.tsx
|
|
1473
|
-
import { Box as
|
|
1474
|
-
import { jsx as
|
|
1536
|
+
import { Box as Box16, Text as Text16, useInput as useInput6 } from "ink";
|
|
1537
|
+
import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1475
1538
|
var KIND_COLOR = {
|
|
1476
1539
|
tool: COLORS.primary,
|
|
1477
1540
|
prompt: COLORS.badge
|
|
@@ -1507,9 +1570,9 @@ function Logs() {
|
|
|
1507
1570
|
else if (key.downArrow) scroll.scrollBy(1);
|
|
1508
1571
|
});
|
|
1509
1572
|
const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
1510
|
-
return /* @__PURE__ */
|
|
1511
|
-
logs.length === 0 && /* @__PURE__ */
|
|
1512
|
-
/* @__PURE__ */
|
|
1573
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1574
|
+
logs.length === 0 && /* @__PURE__ */ jsx14(Text16, { color: COLORS.dim, children: "No logs yet." }),
|
|
1575
|
+
/* @__PURE__ */ jsx14(ScrollView, { scroll, children: visible.map((entry) => {
|
|
1513
1576
|
const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
|
|
1514
1577
|
const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
|
|
1515
1578
|
const rawPreview = rawInputText(entry.input);
|
|
@@ -1519,14 +1582,14 @@ function Logs() {
|
|
|
1519
1582
|
const name = truncate2(entry.name, budget);
|
|
1520
1583
|
budget -= name.length;
|
|
1521
1584
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1522
|
-
return /* @__PURE__ */
|
|
1523
|
-
/* @__PURE__ */
|
|
1524
|
-
/* @__PURE__ */
|
|
1525
|
-
preview && /* @__PURE__ */
|
|
1526
|
-
durationText && /* @__PURE__ */
|
|
1585
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1586
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.dim, children: timestamp }),
|
|
1587
|
+
/* @__PURE__ */ jsx14(Text16, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1588
|
+
preview && /* @__PURE__ */ jsx14(Text16, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1589
|
+
durationText && /* @__PURE__ */ jsx14(Text16, { color: COLORS.dim, children: durationText })
|
|
1527
1590
|
] }, entry.id);
|
|
1528
1591
|
}) }),
|
|
1529
|
-
/* @__PURE__ */
|
|
1592
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1530
1593
|
] });
|
|
1531
1594
|
}
|
|
1532
1595
|
|
|
@@ -1718,12 +1781,12 @@ function track(event, payload) {
|
|
|
1718
1781
|
}
|
|
1719
1782
|
|
|
1720
1783
|
// src/ui/App.tsx
|
|
1721
|
-
import { jsx as
|
|
1784
|
+
import { jsx as jsx15, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1722
1785
|
function App() {
|
|
1723
1786
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1724
1787
|
const { exit } = useApp();
|
|
1725
1788
|
const { columns, rows } = useWindowSize8();
|
|
1726
|
-
const [showLogs, setShowLogs] =
|
|
1789
|
+
const [showLogs, setShowLogs] = useState7(false);
|
|
1727
1790
|
const finished = phase === "done" || phase === "error";
|
|
1728
1791
|
const currentStep = steps[currentStepIndex];
|
|
1729
1792
|
useInput7(
|
|
@@ -1765,8 +1828,8 @@ function App() {
|
|
|
1765
1828
|
/* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
|
|
1766
1829
|
the whole screen, and the scrolling throws off its cursor arithmetic —
|
|
1767
1830
|
flicker and leftover rows. */
|
|
1768
|
-
/* @__PURE__ */
|
|
1769
|
-
|
|
1831
|
+
/* @__PURE__ */ jsxs16(
|
|
1832
|
+
Box17,
|
|
1770
1833
|
{
|
|
1771
1834
|
backgroundColor: COLORS.bg.main,
|
|
1772
1835
|
flexDirection: "row",
|
|
@@ -1774,16 +1837,16 @@ function App() {
|
|
|
1774
1837
|
height: scrollsPastViewport ? void 0 : rows,
|
|
1775
1838
|
overflow: scrollsPastViewport ? "visible" : "hidden",
|
|
1776
1839
|
children: [
|
|
1777
|
-
mainWindowVisible && /* @__PURE__ */
|
|
1778
|
-
|
|
1840
|
+
mainWindowVisible && /* @__PURE__ */ jsxs16(
|
|
1841
|
+
Box17,
|
|
1779
1842
|
{
|
|
1780
1843
|
flexDirection,
|
|
1781
1844
|
width: "100%",
|
|
1782
1845
|
maxHeight: rows,
|
|
1783
1846
|
justifyContent: "space-between",
|
|
1784
1847
|
children: [
|
|
1785
|
-
showLogs ? /* @__PURE__ */
|
|
1786
|
-
|
|
1848
|
+
showLogs ? /* @__PURE__ */ jsx15(Logs, {}) : /* @__PURE__ */ jsxs16(
|
|
1849
|
+
Box17,
|
|
1787
1850
|
{
|
|
1788
1851
|
flexDirection: "column",
|
|
1789
1852
|
paddingX: 4,
|
|
@@ -1791,25 +1854,25 @@ function App() {
|
|
|
1791
1854
|
width: showSidebar ? 70 : "100%",
|
|
1792
1855
|
flexGrow: 1,
|
|
1793
1856
|
children: [
|
|
1794
|
-
phase === "authenticating" && /* @__PURE__ */
|
|
1795
|
-
/* @__PURE__ */
|
|
1796
|
-
/* @__PURE__ */
|
|
1857
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", marginBottom: 1, children: [
|
|
1858
|
+
/* @__PURE__ */ jsx15(Text17, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1859
|
+
/* @__PURE__ */ jsx15(Text17, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1797
1860
|
] }),
|
|
1798
|
-
/* @__PURE__ */
|
|
1799
|
-
/* @__PURE__ */
|
|
1800
|
-
/* @__PURE__ */
|
|
1801
|
-
phase === "error" && error && /* @__PURE__ */
|
|
1861
|
+
/* @__PURE__ */ jsx15(CliOutput, {}),
|
|
1862
|
+
/* @__PURE__ */ jsx15(Notices, {}),
|
|
1863
|
+
/* @__PURE__ */ jsx15(PromptInput, {}),
|
|
1864
|
+
phase === "error" && error && /* @__PURE__ */ jsx15(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text17, { color: COLORS.status.error, children: [
|
|
1802
1865
|
"\u2716 ",
|
|
1803
1866
|
error
|
|
1804
1867
|
] }) })
|
|
1805
1868
|
]
|
|
1806
1869
|
}
|
|
1807
1870
|
),
|
|
1808
|
-
showSidebar ? /* @__PURE__ */
|
|
1871
|
+
showSidebar ? /* @__PURE__ */ jsx15(Sidebar, {}) : /* @__PURE__ */ jsx15(Ribbon, {})
|
|
1809
1872
|
]
|
|
1810
1873
|
}
|
|
1811
1874
|
),
|
|
1812
|
-
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */
|
|
1875
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx15(LearnMore, {}) : /* @__PURE__ */ jsx15(Welcome, {}))
|
|
1813
1876
|
]
|
|
1814
1877
|
}
|
|
1815
1878
|
)
|
|
@@ -2495,6 +2558,7 @@ function writeFileTool(ctx) {
|
|
|
2495
2558
|
}
|
|
2496
2559
|
await mkdir3(dirname4(resolved2.target), { recursive: true });
|
|
2497
2560
|
await writeFile3(resolved2.target, content, "utf8");
|
|
2561
|
+
useWizard.getState().recordWrittenFile(resolved2.target);
|
|
2498
2562
|
return `Wrote to ${filePath}`;
|
|
2499
2563
|
} catch (err) {
|
|
2500
2564
|
return `Error writing ${filePath}: ${err.message}`;
|
|
@@ -3275,7 +3339,7 @@ function defaultCreateModel() {
|
|
|
3275
3339
|
}
|
|
3276
3340
|
function generateRecordTool(ctx, createModel = defaultCreateModel) {
|
|
3277
3341
|
return tool10({
|
|
3278
|
-
description: "Generate realistic sample records for an entity and write them to a JSON file
|
|
3342
|
+
description: "Generate realistic sample records for an entity and write them to a JSON file. 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.",
|
|
3279
3343
|
inputSchema: z17.object({
|
|
3280
3344
|
entityName: z17.string().describe("Name of the entity to generate records for."),
|
|
3281
3345
|
attributes: z17.array(z17.string()).describe("Attribute names each record must contain."),
|
|
@@ -3641,7 +3705,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3641
3705
|
// package.json
|
|
3642
3706
|
var package_default = {
|
|
3643
3707
|
name: "@algolia/wizard",
|
|
3644
|
-
version: "0.
|
|
3708
|
+
version: "0.24.0-rc.111.205",
|
|
3645
3709
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3646
3710
|
type: "module",
|
|
3647
3711
|
engines: {
|
|
@@ -4008,11 +4072,10 @@ ${JSON.stringify(s.output, null, 2)}`
|
|
|
4008
4072
|
function formatReviewSummary(result) {
|
|
4009
4073
|
const nextStepLines = result.nextSteps.map((step) => {
|
|
4010
4074
|
const isIngestCommand = step.includes(".algolia-wizard/ingest.sh");
|
|
4011
|
-
const isWorktreeCommand = step.includes("/worktrees/");
|
|
4012
4075
|
return {
|
|
4013
4076
|
text: `\u2192 ${step}`,
|
|
4014
|
-
color: isIngestCommand ? COLORS.brand :
|
|
4015
|
-
bold: isIngestCommand
|
|
4077
|
+
color: isIngestCommand ? COLORS.brand : void 0,
|
|
4078
|
+
bold: isIngestCommand
|
|
4016
4079
|
};
|
|
4017
4080
|
});
|
|
4018
4081
|
return [
|
|
@@ -4047,15 +4110,13 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
4047
4110
|
|
|
4048
4111
|
// src/actions/implement.ts
|
|
4049
4112
|
import z29 from "zod";
|
|
4050
|
-
import { join as join12 } from "node:path";
|
|
4113
|
+
import { join as join12, relative as relative6 } from "node:path";
|
|
4051
4114
|
|
|
4052
|
-
// src/lib/
|
|
4115
|
+
// src/lib/git.ts
|
|
4053
4116
|
import { execFile as execFile2 } from "node:child_process";
|
|
4054
|
-
import { copyFile, mkdir as mkdir6,
|
|
4117
|
+
import { copyFile, mkdir as mkdir6, readFile as readFile8, stat as stat3, writeFile as writeFile7 } from "node:fs/promises";
|
|
4055
4118
|
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "node:path";
|
|
4056
4119
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
4057
|
-
var MAX_WIZARD_WORKTREES = 3;
|
|
4058
|
-
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
4059
4120
|
function git(args) {
|
|
4060
4121
|
return new Promise((resolve4, reject) => {
|
|
4061
4122
|
execFile2("git", args, { maxBuffer: MAX_BUFFER }, (err, stdout, stderr) => {
|
|
@@ -4078,44 +4139,7 @@ async function assertGitRepoWithHead(repoRoot) {
|
|
|
4078
4139
|
);
|
|
4079
4140
|
}
|
|
4080
4141
|
}
|
|
4081
|
-
async function
|
|
4082
|
-
const out = await git(["-C", repoRoot, "status", "--porcelain"]);
|
|
4083
|
-
return out.trim().length > 0;
|
|
4084
|
-
}
|
|
4085
|
-
async function pruneOldWorktrees(repoRoot) {
|
|
4086
|
-
const dir = join10(stateDir(repoRoot), "worktrees");
|
|
4087
|
-
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
4088
|
-
for (const slug of stale) {
|
|
4089
|
-
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
4090
|
-
try {
|
|
4091
|
-
await git([
|
|
4092
|
-
"-C",
|
|
4093
|
-
repoRoot,
|
|
4094
|
-
"worktree",
|
|
4095
|
-
"remove",
|
|
4096
|
-
"--force",
|
|
4097
|
-
join10(dir, slug)
|
|
4098
|
-
]);
|
|
4099
|
-
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
4100
|
-
} catch (err) {
|
|
4101
|
-
logger.warn(
|
|
4102
|
-
{ branch, err: err.message },
|
|
4103
|
-
"createWorktree: failed to prune a stale wizard worktree; continuing"
|
|
4104
|
-
);
|
|
4105
|
-
}
|
|
4106
|
-
}
|
|
4107
|
-
}
|
|
4108
|
-
async function createWorktree(repoRoot) {
|
|
4109
|
-
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
4110
|
-
const dirSlug = branch.replace(/\//g, "-");
|
|
4111
|
-
const path = join10(stateDir(repoRoot), "worktrees", dirSlug);
|
|
4112
|
-
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
4113
|
-
await pruneOldWorktrees(repoRoot);
|
|
4114
|
-
await mkdir6(dirname7(path), { recursive: true });
|
|
4115
|
-
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
4116
|
-
return { path, branch };
|
|
4117
|
-
}
|
|
4118
|
-
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
4142
|
+
async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
|
|
4119
4143
|
const trimmed = sourcePath.trim();
|
|
4120
4144
|
if (!trimmed) {
|
|
4121
4145
|
return { ok: false, reason: "no file path was provided" };
|
|
@@ -4129,7 +4153,10 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
4129
4153
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
4130
4154
|
}
|
|
4131
4155
|
const relPath = join10(ingestDir, basename2(source));
|
|
4132
|
-
const dest = join10(
|
|
4156
|
+
const dest = join10(repoRoot, relPath);
|
|
4157
|
+
if (resolve3(source) === resolve3(dest)) {
|
|
4158
|
+
return { ok: true, relPath };
|
|
4159
|
+
}
|
|
4133
4160
|
try {
|
|
4134
4161
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
4135
4162
|
await copyFile(source, dest);
|
|
@@ -4144,10 +4171,10 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
4144
4171
|
function hasEnvVar(content, name) {
|
|
4145
4172
|
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
4146
4173
|
}
|
|
4147
|
-
async function readEnvVar(
|
|
4174
|
+
async function readEnvVar(repoRoot, name) {
|
|
4148
4175
|
let content;
|
|
4149
4176
|
try {
|
|
4150
|
-
content = await readFile8(join10(
|
|
4177
|
+
content = await readFile8(join10(repoRoot, ".env"), "utf8");
|
|
4151
4178
|
} catch (err) {
|
|
4152
4179
|
if (err.code !== "ENOENT") throw err;
|
|
4153
4180
|
return void 0;
|
|
@@ -4161,8 +4188,8 @@ async function readEnvVar(worktreePath, name) {
|
|
|
4161
4188
|
if (!value || value.startsWith("<")) return void 0;
|
|
4162
4189
|
return value;
|
|
4163
4190
|
}
|
|
4164
|
-
async function writeSearchEnvValues(
|
|
4165
|
-
const target = join10(
|
|
4191
|
+
async function writeSearchEnvValues(repoRoot, vars) {
|
|
4192
|
+
const target = join10(repoRoot, ".env");
|
|
4166
4193
|
let existing = "";
|
|
4167
4194
|
try {
|
|
4168
4195
|
existing = await readFile8(target, "utf8");
|
|
@@ -4177,61 +4204,27 @@ async function writeSearchEnvValues(worktreePath, vars) {
|
|
|
4177
4204
|
await writeFile7(target, existing + prefix + lines, "utf8");
|
|
4178
4205
|
return missing.map((v) => v.name);
|
|
4179
4206
|
}
|
|
4180
|
-
async function listChangedFiles(worktreePath) {
|
|
4181
|
-
const raw = await git(["-C", worktreePath, "status", "--porcelain", "-z"]);
|
|
4182
|
-
const entries = raw.split("\0");
|
|
4183
|
-
const files = [];
|
|
4184
|
-
for (let i = 0; i < entries.length; i += 1) {
|
|
4185
|
-
const entry = entries[i];
|
|
4186
|
-
if (!entry) continue;
|
|
4187
|
-
files.push(entry.slice(3));
|
|
4188
|
-
if (["R", "C"].includes(entry[0]) || ["R", "C"].includes(entry[1])) i += 1;
|
|
4189
|
-
}
|
|
4190
|
-
return files;
|
|
4191
|
-
}
|
|
4192
4207
|
function normalizeFindingPaths(findings) {
|
|
4193
4208
|
return {
|
|
4194
4209
|
...findings,
|
|
4195
4210
|
ingestionAnalysis: findings.ingestionAnalysis?.map((e) => ({
|
|
4196
4211
|
...e,
|
|
4197
|
-
paths: e.paths.map(
|
|
4212
|
+
paths: e.paths.map(toRootRelative)
|
|
4198
4213
|
})),
|
|
4199
4214
|
searchImplementationAnalysis: findings.searchImplementationAnalysis ? normalizeSearchLocation(findings.searchImplementationAnalysis) : void 0,
|
|
4200
4215
|
confirmedEntities: findings.confirmedEntities?.map((e) => ({
|
|
4201
4216
|
...e,
|
|
4202
|
-
paths: e.paths.map(
|
|
4217
|
+
paths: e.paths.map(toRootRelative)
|
|
4203
4218
|
}))
|
|
4204
4219
|
};
|
|
4205
4220
|
}
|
|
4206
4221
|
function normalizeSearchLocation(path) {
|
|
4207
|
-
const normalized = path ?
|
|
4222
|
+
const normalized = path ? toRootRelative(path).trim() : "";
|
|
4208
4223
|
return normalized && normalized.toLowerCase() !== "unknown" ? normalized : void 0;
|
|
4209
4224
|
}
|
|
4210
|
-
function
|
|
4225
|
+
function toRootRelative(p) {
|
|
4211
4226
|
return p.replace(/^\/+/, "");
|
|
4212
4227
|
}
|
|
4213
|
-
async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
4214
|
-
const MAX_LISTED_DIRTY_FILES = 10;
|
|
4215
|
-
const dirty = await listChangedFiles(repoRoot);
|
|
4216
|
-
const shown = dirty.slice(0, MAX_LISTED_DIRTY_FILES);
|
|
4217
|
-
const overflow = dirty.length - shown.length;
|
|
4218
|
-
const answer = await ctx.requestUserInput({
|
|
4219
|
-
prompt: "Proceed using HEAD only? Uncommitted changes will NOT be included in the generated implementation.",
|
|
4220
|
-
promptType: "acceptReject",
|
|
4221
|
-
options: [],
|
|
4222
|
-
messages: [
|
|
4223
|
-
`${dirty.length} uncommitted change(s) detected. The wizard builds an isolated worktree from HEAD, so these are ignored:`,
|
|
4224
|
-
...shown.map((file) => ` \u2022 ${file}`),
|
|
4225
|
-
...overflow > 0 ? [` \u2022 \u2026and ${overflow} more`] : [],
|
|
4226
|
-
"Commit or stash them first to include them in the implementation."
|
|
4227
|
-
]
|
|
4228
|
-
});
|
|
4229
|
-
if (answer !== true) {
|
|
4230
|
-
throw new Error(
|
|
4231
|
-
"implement aborted: commit or stash your changes so they are built into the worktree, then re-run the wizard."
|
|
4232
|
-
);
|
|
4233
|
-
}
|
|
4234
|
-
}
|
|
4235
4228
|
|
|
4236
4229
|
// src/lib/algoliaDocs.ts
|
|
4237
4230
|
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
@@ -4291,11 +4284,6 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
4291
4284
|
return loadAlgoliaDoc("js");
|
|
4292
4285
|
}
|
|
4293
4286
|
|
|
4294
|
-
// src/lib/shell.ts
|
|
4295
|
-
function shellQuote(value) {
|
|
4296
|
-
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
4297
|
-
}
|
|
4298
|
-
|
|
4299
4287
|
// src/actions/resolveEnvVarPrefix.ts
|
|
4300
4288
|
import z28 from "zod";
|
|
4301
4289
|
var resolveEnvVarPrefixSchema = z28.object({
|
|
@@ -4315,9 +4303,7 @@ var resolveEnvVarPrefix = (frameworkName) => runAgent({
|
|
|
4315
4303
|
|
|
4316
4304
|
// src/actions/implement.ts
|
|
4317
4305
|
var implementSchema = z29.object({
|
|
4318
|
-
filesChanged: z29.array(z29.string()),
|
|
4319
4306
|
summary: z29.string(),
|
|
4320
|
-
worktreePath: z29.string().optional(),
|
|
4321
4307
|
ingestCommand: z29.string().optional(),
|
|
4322
4308
|
ingestScriptRan: z29.boolean().optional(),
|
|
4323
4309
|
ingestRecordCount: z29.number().optional(),
|
|
@@ -4370,8 +4356,6 @@ function frameworksForDoc(language) {
|
|
|
4370
4356
|
}
|
|
4371
4357
|
function baseInstructions(input) {
|
|
4372
4358
|
return [
|
|
4373
|
-
// Agents have renamed this (e.g. appending the project name), which the
|
|
4374
|
-
// index-scoped keys then reject with a 403.
|
|
4375
4359
|
`Target Algolia index, to be used exactly as written \u2014 never renamed, re-cased, prefixed, or suffixed: "${input.targetIndex}"`,
|
|
4376
4360
|
`Project languages and frameworks: ${JSON.stringify(input.language)}`,
|
|
4377
4361
|
"Make minimal, idiomatic changes; do not touch unrelated code.",
|
|
@@ -4389,14 +4373,14 @@ function sourceSpecificInstructions(input) {
|
|
|
4389
4373
|
"Add env vars for any local source access (DB URL, API base, file paths) that is not a fixed repo path."
|
|
4390
4374
|
],
|
|
4391
4375
|
fileUpload: [
|
|
4392
|
-
`Records come from the developer's file, already copied into the
|
|
4376
|
+
`Records come from the developer's file, already copied into the project at "${input.uploadFilePath}". Read and parse that exact file.`,
|
|
4393
4377
|
"Parse by extension: JSON = array of objects; CSV/TSV = header row maps to keys.",
|
|
4394
4378
|
"Map parsed columns/fields to the confirmed entity attributes.",
|
|
4395
4379
|
"Never fabricate, hardcode, or substitute a different file."
|
|
4396
4380
|
],
|
|
4397
4381
|
generated: [
|
|
4398
4382
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
4399
|
-
"Call the generateRecord tool once per entity (entityName, attributes, count 20-50); it invents the values and unique objectIDs and writes them to a JSON file
|
|
4383
|
+
"Call the generateRecord tool once per entity (entityName, attributes, count 20-50); it invents the values and unique objectIDs and writes them to a JSON file, returning the file path. Do not write records or objectIDs yourself.",
|
|
4400
4384
|
"In the script, read and parse each returned file path at runtime using your language's standard JSON support, instead of inlining the records as literals.",
|
|
4401
4385
|
"Add a prominent TODO where the developer swaps the generated records (and the JSON file under `.algolia-wizard/data/`) for their real record source."
|
|
4402
4386
|
]
|
|
@@ -4445,14 +4429,11 @@ function searchInstructions(input) {
|
|
|
4445
4429
|
"If a search box already exists, replace it with yours.",
|
|
4446
4430
|
`Read the index name from the ${publicIndexNameVar(input.publicEnvVarPrefix)} env var, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or component name.`,
|
|
4447
4431
|
"Read the App ID, the search-only API key, and the index name from env vars; never hardcode them. A search-only key is safe to expose client-side.",
|
|
4448
|
-
// The key is provisioned only after verification passes,
|
|
4449
|
-
//
|
|
4450
|
-
//
|
|
4451
|
-
// would be reused as if it were real.
|
|
4432
|
+
// The key is provisioned only after verification passes, and the wizard
|
|
4433
|
+
// reads .env to decide whether a key already exists — an agent-invented
|
|
4434
|
+
// value there would be reused as if it were real.
|
|
4452
4435
|
`Add Algolia App ID "${input.appId}"; leave the search-only key as a placeholder. Do not create or edit .env \u2014 the wizard writes the resolved key there itself.`,
|
|
4453
|
-
//
|
|
4454
|
-
// ".env" right after this step, so a renamed prefix would leave the code
|
|
4455
|
-
// reading a var the wizard never wrote.
|
|
4436
|
+
// The wizard writes these exact names into .env right after this step.
|
|
4456
4437
|
`Use exactly these env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4457
4438
|
"Install any Algolia packages you import with the project's own package manager via runShell, and declare them in the project's dependency manifest.",
|
|
4458
4439
|
"Match the styles of the application as closely as possible.",
|
|
@@ -4461,10 +4442,10 @@ function searchInstructions(input) {
|
|
|
4461
4442
|
}
|
|
4462
4443
|
function verificationInstructions(input) {
|
|
4463
4444
|
return [
|
|
4464
|
-
"Verify the Algolia implementation changes
|
|
4445
|
+
"Verify the Algolia implementation changes.",
|
|
4465
4446
|
`Verification tools found in the codebase: ${JSON.stringify(input.findings.verification ?? [])}.`,
|
|
4466
4447
|
"Run the project's own checks (lint, type check, tests) via runShell, using the commands the project actually defines \u2014 its task runner, manifest scripts, or Makefile. Run every check that applies, not just the first.",
|
|
4467
|
-
"
|
|
4448
|
+
"If a check fails because packages or modules are missing, install the dependencies via runShell and re-run it rather than changing the code.",
|
|
4468
4449
|
"For issues caused by the implementation, make minimal fixes with writeFile and re-run the checks.",
|
|
4469
4450
|
"Do not make speculative fixes when no checks exist, a check cannot run, or failures are unrelated to these changes \u2014 note the limitation in your summary.",
|
|
4470
4451
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
@@ -4559,11 +4540,11 @@ function ingestFailure(attempt, executions) {
|
|
|
4559
4540
|
}
|
|
4560
4541
|
return { reason: `it failed (exit ${attempt.exitCode ?? "unknown"})`, detail };
|
|
4561
4542
|
}
|
|
4562
|
-
function makeToolContext(
|
|
4543
|
+
function makeToolContext(root, env = async () => ({})) {
|
|
4563
4544
|
return createToolContext(
|
|
4564
4545
|
DEFAULT_TOOL_LIMITS,
|
|
4565
|
-
|
|
4566
|
-
createShellContext({ env, approve: storeApproval(
|
|
4546
|
+
root,
|
|
4547
|
+
createShellContext({ env, approve: storeApproval(root) })
|
|
4567
4548
|
);
|
|
4568
4549
|
}
|
|
4569
4550
|
function verificationRetryInstructions(verification) {
|
|
@@ -4571,7 +4552,7 @@ function verificationRetryInstructions(verification) {
|
|
|
4571
4552
|
`Implementation insufficient. Address these findings before reporting completion: ${verification.additionalInstructions ?? verification.summary}`
|
|
4572
4553
|
];
|
|
4573
4554
|
}
|
|
4574
|
-
async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES
|
|
4555
|
+
async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
4575
4556
|
const repoRoot = process.cwd();
|
|
4576
4557
|
const scan = ctx.getStepOutput("project-scan");
|
|
4577
4558
|
const entities = ctx.getStepOutput(
|
|
@@ -4652,9 +4633,6 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4652
4633
|
const targetIndex = selected?.selection;
|
|
4653
4634
|
useWizard.getState().setTargetIndex(targetIndex ?? null);
|
|
4654
4635
|
await assertGitRepoWithHead(repoRoot);
|
|
4655
|
-
if (await isWorkingTreeDirty(repoRoot)) {
|
|
4656
|
-
await confirmDirtyWorkingTree(ctx, repoRoot);
|
|
4657
|
-
}
|
|
4658
4636
|
const normalized = normalizeFindingPaths(findings);
|
|
4659
4637
|
const confirmed2 = normalized.confirmedEntities;
|
|
4660
4638
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
@@ -4666,328 +4644,303 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4666
4644
|
if (useCases.includes("ingestion")) {
|
|
4667
4645
|
ingestAppId = appId ?? (await requireApplication()).id;
|
|
4668
4646
|
}
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4647
|
+
let uploadFilePath;
|
|
4648
|
+
let uploadWarning;
|
|
4649
|
+
if (ingestionSource === "fileUpload") {
|
|
4650
|
+
const copied = await copyUploadIntoProject(
|
|
4651
|
+
repoRoot,
|
|
4652
|
+
INGEST_DIR,
|
|
4653
|
+
uploadSourcePath ?? ""
|
|
4654
|
+
);
|
|
4655
|
+
if (copied.ok) {
|
|
4656
|
+
uploadFilePath = copied.relPath;
|
|
4657
|
+
} else {
|
|
4658
|
+
ingestionSource = "generated";
|
|
4659
|
+
uploadWarning = `\u26A0\uFE0F Could not use the uploaded file (${copied.reason}); generating sample records instead.`;
|
|
4660
|
+
logger.warn(
|
|
4661
|
+
{ reason: copied.reason },
|
|
4662
|
+
"implement: file upload unavailable; falling back to generated sample records"
|
|
4663
|
+
);
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
const publicEnvVarPrefix = await publicEnvVarPrefixPromise;
|
|
4667
|
+
const input = {
|
|
4668
|
+
findings: normalized,
|
|
4669
|
+
confirmed: confirmed2,
|
|
4670
|
+
searchLocation,
|
|
4671
|
+
targetIndex,
|
|
4672
|
+
language,
|
|
4673
|
+
publicEnvVarPrefix,
|
|
4674
|
+
appId,
|
|
4675
|
+
searchEnvVars: publicSearchEnvVars(publicEnvVarPrefix, targetIndex, appId),
|
|
4676
|
+
ingestDir: INGEST_DIR,
|
|
4677
|
+
ingestionSource,
|
|
4678
|
+
uploadFilePath,
|
|
4679
|
+
searchUiTarget: searchUiTarget(language)
|
|
4680
|
+
};
|
|
4681
|
+
const summaries = [];
|
|
4682
|
+
if (uploadWarning) summaries.push(uploadWarning);
|
|
4683
|
+
let envSearchKey;
|
|
4684
|
+
let envAppIdMismatch = false;
|
|
4685
|
+
if (useCases.includes("search") && appId) {
|
|
4686
|
+
const envAppId = await readEnvVar(
|
|
4687
|
+
repoRoot,
|
|
4688
|
+
publicAppIdVar(publicEnvVarPrefix)
|
|
4689
|
+
);
|
|
4690
|
+
if (envAppId === appId) {
|
|
4691
|
+
envSearchKey = await readEnvVar(
|
|
4676
4692
|
repoRoot,
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4693
|
+
publicSearchKeyVar(publicEnvVarPrefix)
|
|
4694
|
+
);
|
|
4695
|
+
} else if (envAppId) {
|
|
4696
|
+
envAppIdMismatch = true;
|
|
4697
|
+
const appIdVarName = publicAppIdVar(publicEnvVarPrefix);
|
|
4698
|
+
const searchKeyVarName = publicSearchKeyVar(publicEnvVarPrefix);
|
|
4699
|
+
summaries.push(
|
|
4700
|
+
`\u26A0\uFE0F .env already sets ${appIdVarName}=${envAppId}, but the active Algolia application is ${appId}. The wizard left those values alone \u2014 update ${appIdVarName} and ${searchKeyVarName} by hand, or searches will fail.`
|
|
4701
|
+
);
|
|
4702
|
+
logger.warn(
|
|
4703
|
+
{ envAppId, appId },
|
|
4704
|
+
"implement: .env holds credentials for a different Algolia application; not reusing its search key"
|
|
4680
4705
|
);
|
|
4681
|
-
if (copied.ok) {
|
|
4682
|
-
uploadFilePath = copied.relPath;
|
|
4683
|
-
} else {
|
|
4684
|
-
ingestionSource = "generated";
|
|
4685
|
-
uploadWarning = `\u26A0\uFE0F Could not use the uploaded file (${copied.reason}); generating sample records instead.`;
|
|
4686
|
-
logger.warn(
|
|
4687
|
-
{ reason: copied.reason },
|
|
4688
|
-
"implement: file upload unavailable; falling back to generated sample records"
|
|
4689
|
-
);
|
|
4690
|
-
}
|
|
4691
4706
|
}
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4707
|
+
}
|
|
4708
|
+
let finalSearchEnvVars = input.searchEnvVars;
|
|
4709
|
+
let agentRuns = 0;
|
|
4710
|
+
let ingestCommand;
|
|
4711
|
+
let ingestScriptRan = false;
|
|
4712
|
+
let ingestRecordCount;
|
|
4713
|
+
let ingestDurationMs;
|
|
4714
|
+
let ingestOutcomeMessage;
|
|
4715
|
+
const ingestKeyAppId = ingestAppId;
|
|
4716
|
+
const ingestionTools = ingestKeyAppId ? makeToolContext(repoRoot, async () => ({
|
|
4717
|
+
[APP_ID_VAR]: ingestKeyAppId,
|
|
4718
|
+
[API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
|
|
4719
|
+
[INDEX_NAME_VAR]: targetIndex
|
|
4720
|
+
})) : void 0;
|
|
4721
|
+
const searchTools = makeToolContext(repoRoot);
|
|
4722
|
+
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4723
|
+
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4724
|
+
agentRuns += 1;
|
|
4725
|
+
return runAgent({
|
|
4726
|
+
instructions: buildAgentInstructions(
|
|
4727
|
+
currentUseCase,
|
|
4728
|
+
input,
|
|
4729
|
+
extraInstructions
|
|
4705
4730
|
),
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
if (
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4731
|
+
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4732
|
+
outputSchema: implementationOutputSchema,
|
|
4733
|
+
toolContext: currentUseCase === "ingestion" ? ingestionTools ?? searchTools : searchTools
|
|
4734
|
+
});
|
|
4735
|
+
}
|
|
4736
|
+
async function runVerificationUseCase() {
|
|
4737
|
+
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4738
|
+
agentRuns += 1;
|
|
4739
|
+
return runAgent({
|
|
4740
|
+
instructions: buildAgentInstructions("verification", input),
|
|
4741
|
+
tools: toolsForUseCase("verification"),
|
|
4742
|
+
outputSchema: verificationOutputSchema,
|
|
4743
|
+
toolContext: searchTools
|
|
4744
|
+
});
|
|
4745
|
+
}
|
|
4746
|
+
if (useCases.includes("ingestion")) {
|
|
4747
|
+
let ingestFailureDetail;
|
|
4748
|
+
const result = await runImplementationUseCase("ingestion");
|
|
4749
|
+
summaries.push(formatSummary("ingestion", result.summary));
|
|
4750
|
+
ingestCommand = result.ingestCommand;
|
|
4751
|
+
const ingestionContext = ingestionTools ?? searchTools;
|
|
4752
|
+
const executions = ingestionContext.shell.executions;
|
|
4753
|
+
const {
|
|
4754
|
+
run: ingestRun,
|
|
4755
|
+
attempt: ingestAttempt,
|
|
4756
|
+
recordCount
|
|
4757
|
+
} = ingestOutcome(executions, ingestCommand);
|
|
4758
|
+
ingestScriptRan = ingestRun != null;
|
|
4759
|
+
ingestRecordCount = recordCount;
|
|
4760
|
+
ingestDurationMs = ingestRun?.durationMs;
|
|
4761
|
+
if (ingestScriptRan && ingestionContext.reviewed.length === 0) {
|
|
4762
|
+
summaries.push(
|
|
4763
|
+
"\u26A0\uFE0F The ingestion script ran without being shown to you for review. Read it in your project before trusting the index contents."
|
|
4764
|
+
);
|
|
4765
|
+
logger.warn(
|
|
4766
|
+
{ ingestCommand },
|
|
4767
|
+
"implement: ingestion ran without a reviewScript call"
|
|
4719
4768
|
);
|
|
4720
|
-
if (envAppId === appId) {
|
|
4721
|
-
envSearchKey = await readEnvVar(
|
|
4722
|
-
worktree,
|
|
4723
|
-
publicSearchKeyVar(publicEnvVarPrefix)
|
|
4724
|
-
);
|
|
4725
|
-
} else if (envAppId) {
|
|
4726
|
-
envAppIdMismatch = true;
|
|
4727
|
-
const appIdVarName = publicAppIdVar(publicEnvVarPrefix);
|
|
4728
|
-
const searchKeyVarName = publicSearchKeyVar(publicEnvVarPrefix);
|
|
4729
|
-
summaries.push(
|
|
4730
|
-
`\u26A0\uFE0F .env already sets ${appIdVarName}=${envAppId}, but the active Algolia application is ${appId}. The wizard left those values alone \u2014 update ${appIdVarName} and ${searchKeyVarName} by hand, or searches will fail.`
|
|
4731
|
-
);
|
|
4732
|
-
logger.warn(
|
|
4733
|
-
{ envAppId, appId },
|
|
4734
|
-
"implement: .env holds credentials for a different Algolia application; not reusing its search key"
|
|
4735
|
-
);
|
|
4736
|
-
}
|
|
4737
|
-
}
|
|
4738
|
-
let finalSearchEnvVars = input.searchEnvVars;
|
|
4739
|
-
let agentRuns = 0;
|
|
4740
|
-
let ingestCommand;
|
|
4741
|
-
let ingestScriptRan = false;
|
|
4742
|
-
let ingestRecordCount;
|
|
4743
|
-
let ingestDurationMs;
|
|
4744
|
-
let ingestOutcomeMessage;
|
|
4745
|
-
const ingestKeyAppId = ingestAppId;
|
|
4746
|
-
const ingestionTools = ingestKeyAppId ? makeToolContext(worktree, async () => ({
|
|
4747
|
-
[APP_ID_VAR]: ingestKeyAppId,
|
|
4748
|
-
[API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
|
|
4749
|
-
[INDEX_NAME_VAR]: targetIndex
|
|
4750
|
-
})) : void 0;
|
|
4751
|
-
const searchTools = makeToolContext(worktree);
|
|
4752
|
-
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4753
|
-
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4754
|
-
agentRuns += 1;
|
|
4755
|
-
return runAgent({
|
|
4756
|
-
instructions: buildAgentInstructions(
|
|
4757
|
-
currentUseCase,
|
|
4758
|
-
input,
|
|
4759
|
-
extraInstructions
|
|
4760
|
-
),
|
|
4761
|
-
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4762
|
-
outputSchema: implementationOutputSchema,
|
|
4763
|
-
toolContext: currentUseCase === "ingestion" ? ingestionTools ?? searchTools : searchTools
|
|
4764
|
-
});
|
|
4765
|
-
}
|
|
4766
|
-
async function runVerificationUseCase() {
|
|
4767
|
-
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4768
|
-
agentRuns += 1;
|
|
4769
|
-
return runAgent({
|
|
4770
|
-
instructions: buildAgentInstructions("verification", input),
|
|
4771
|
-
tools: toolsForUseCase("verification"),
|
|
4772
|
-
outputSchema: verificationOutputSchema,
|
|
4773
|
-
toolContext: searchTools
|
|
4774
|
-
});
|
|
4775
4769
|
}
|
|
4776
|
-
if (
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
run: ingestRun,
|
|
4785
|
-
attempt: ingestAttempt,
|
|
4786
|
-
recordCount
|
|
4787
|
-
} = ingestOutcome(executions, ingestCommand);
|
|
4788
|
-
ingestScriptRan = ingestRun != null;
|
|
4789
|
-
ingestRecordCount = recordCount;
|
|
4790
|
-
ingestDurationMs = ingestRun?.durationMs;
|
|
4791
|
-
if (ingestScriptRan && ingestionContext.reviewed.length === 0) {
|
|
4792
|
-
summaries.push(
|
|
4793
|
-
"\u26A0\uFE0F The ingestion script ran without being shown to you for review. Read it in the worktree before trusting the index contents."
|
|
4794
|
-
);
|
|
4795
|
-
logger.warn(
|
|
4796
|
-
{ ingestCommand },
|
|
4797
|
-
"implement: ingestion ran without a reviewScript call"
|
|
4798
|
-
);
|
|
4770
|
+
if (ingestScriptRan) {
|
|
4771
|
+
ingestOutcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4772
|
+
if (ingestRecordCount != null) {
|
|
4773
|
+
track("AI Wizard Ingest Successful", {
|
|
4774
|
+
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4775
|
+
record_count: ingestRecordCount,
|
|
4776
|
+
duration_ms: ingestDurationMs ?? 0
|
|
4777
|
+
});
|
|
4799
4778
|
}
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
duration_ms: ingestDurationMs ?? 0
|
|
4807
|
-
});
|
|
4808
|
-
}
|
|
4809
|
-
} else {
|
|
4810
|
-
const { reason, detail } = ingestFailure(ingestAttempt, executions);
|
|
4811
|
-
ingestOutcomeMessage = `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.${ingestCommand ? " Run the command below when you are ready." : ""}`;
|
|
4812
|
-
ingestFailureDetail = detail;
|
|
4813
|
-
summaries.push(
|
|
4814
|
-
`\u26A0\uFE0F Ingestion did not complete: ${reason}.${detail ? `
|
|
4779
|
+
} else {
|
|
4780
|
+
const { reason, detail } = ingestFailure(ingestAttempt, executions);
|
|
4781
|
+
ingestOutcomeMessage = `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.${ingestCommand ? " Run the command below when you are ready." : ""}`;
|
|
4782
|
+
ingestFailureDetail = detail;
|
|
4783
|
+
summaries.push(
|
|
4784
|
+
`\u26A0\uFE0F Ingestion did not complete: ${reason}.${detail ? `
|
|
4815
4785
|
${detail}` : ""}`
|
|
4816
|
-
|
|
4817
|
-
|
|
4786
|
+
);
|
|
4787
|
+
logger.warn(
|
|
4788
|
+
{
|
|
4789
|
+
ingestCommand,
|
|
4790
|
+
reason,
|
|
4791
|
+
approved: ingestAttempt?.approved,
|
|
4792
|
+
exitCode: ingestAttempt?.exitCode,
|
|
4793
|
+
timedOut: ingestAttempt?.timedOut,
|
|
4794
|
+
commandsRun: executions.length
|
|
4795
|
+
},
|
|
4796
|
+
"implement: ingestion script did not complete successfully"
|
|
4797
|
+
);
|
|
4798
|
+
track("Error", {
|
|
4799
|
+
step: "Push Data",
|
|
4800
|
+
error: `ingestion did not complete: ${reason}`,
|
|
4801
|
+
product_area: "AI Wizard"
|
|
4802
|
+
});
|
|
4803
|
+
}
|
|
4804
|
+
const commandMessages = ingestCommand ? [`Ingestion command: ${ingestCommand}`] : [];
|
|
4805
|
+
await ctx.requestUserInput({
|
|
4806
|
+
prompt: "",
|
|
4807
|
+
promptType: "enterToContinue",
|
|
4808
|
+
options: [],
|
|
4809
|
+
// One message per line: Notices.tsx counts a message as one wrapped
|
|
4810
|
+
// line, so an embedded newline overflows the panel's height accounting.
|
|
4811
|
+
messages: [
|
|
4812
|
+
ingestOutcomeMessage,
|
|
4813
|
+
...ingestFailureDetail?.split("\n").filter((l) => l.trim()) ?? [],
|
|
4814
|
+
...commandMessages
|
|
4815
|
+
]
|
|
4816
|
+
});
|
|
4817
|
+
}
|
|
4818
|
+
if (useCases.includes("search")) {
|
|
4819
|
+
let extraInstructions = [];
|
|
4820
|
+
useWizard.getState().clearWrittenFiles();
|
|
4821
|
+
for (let attempt = 1; attempt <= MAX_IMPLEMENT_VERIFICATION_ATTEMPTS; attempt++) {
|
|
4822
|
+
if (attempt > 1) {
|
|
4823
|
+
logger.info(
|
|
4818
4824
|
{
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
exitCode: ingestAttempt?.exitCode,
|
|
4823
|
-
timedOut: ingestAttempt?.timedOut,
|
|
4824
|
-
commandsRun: executions.length
|
|
4825
|
+
attempt,
|
|
4826
|
+
maxAttempts: MAX_IMPLEMENT_VERIFICATION_ATTEMPTS,
|
|
4827
|
+
extraInstructions
|
|
4825
4828
|
},
|
|
4826
|
-
"implement:
|
|
4829
|
+
"implement: retrying search implementation after failed verification"
|
|
4827
4830
|
);
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4831
|
+
}
|
|
4832
|
+
const { summary } = await runImplementationUseCase(
|
|
4833
|
+
"search",
|
|
4834
|
+
extraInstructions
|
|
4835
|
+
);
|
|
4836
|
+
summaries.push(formatSummary("search", summary));
|
|
4837
|
+
const verification = await runVerificationUseCase();
|
|
4838
|
+
summaries.push(formatSummary("verification", verification.summary));
|
|
4839
|
+
if (verification.sufficient) {
|
|
4840
|
+
ctx.setUserInput("implementation", "success");
|
|
4841
|
+
const searchFilesChanged = [
|
|
4842
|
+
...new Set(useWizard.getState().writtenFiles)
|
|
4843
|
+
].map((file) => relative6(repoRoot, file));
|
|
4844
|
+
track("AI Wizard Frontend Component Generated", {
|
|
4845
|
+
filePaths: searchFilesChanged
|
|
4832
4846
|
});
|
|
4847
|
+
track("AI Wizard Wired to UI", {
|
|
4848
|
+
location_heuristic: searchLocation ?? "unknown"
|
|
4849
|
+
});
|
|
4850
|
+
break;
|
|
4833
4851
|
}
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4852
|
+
if (attempt === MAX_IMPLEMENT_VERIFICATION_ATTEMPTS) {
|
|
4853
|
+
ctx.setUserInput("implementation", "fail");
|
|
4854
|
+
throw new Error(
|
|
4855
|
+
`Implementation verification failed after ${MAX_IMPLEMENT_VERIFICATION_ATTEMPTS} attempts: ${verification.additionalInstructions ?? verification.summary}`
|
|
4856
|
+
);
|
|
4837
4857
|
}
|
|
4838
|
-
|
|
4839
|
-
prompt: "",
|
|
4840
|
-
promptType: "enterToContinue",
|
|
4841
|
-
options: [],
|
|
4842
|
-
// The wizard never streams command output, so a failed run's tail is the
|
|
4843
|
-
// only place the developer sees why it failed. One message per line:
|
|
4844
|
-
// the panel's height accounting counts a message as one wrapped line
|
|
4845
|
-
// (see Notices.tsx), so an embedded newline overflows it.
|
|
4846
|
-
messages: [
|
|
4847
|
-
ingestOutcomeMessage,
|
|
4848
|
-
...ingestFailureDetail?.split("\n").filter((l) => l.trim()) ?? [],
|
|
4849
|
-
...commandMessages
|
|
4850
|
-
]
|
|
4851
|
-
});
|
|
4858
|
+
extraInstructions = verificationRetryInstructions(verification);
|
|
4852
4859
|
}
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4860
|
+
let searchKey;
|
|
4861
|
+
let searchKeyError;
|
|
4862
|
+
if (appId) {
|
|
4863
|
+
try {
|
|
4864
|
+
const resolved2 = await resolveSearchOnlyKey(
|
|
4865
|
+
targetIndex,
|
|
4866
|
+
appId,
|
|
4867
|
+
envSearchKey
|
|
4868
|
+
);
|
|
4869
|
+
searchKey = resolved2.key;
|
|
4870
|
+
summaries.push(
|
|
4871
|
+
resolved2.source === "created" ? `Created a new search-only Algolia API key for the "${targetIndex}" index in app ${appId} \u2014 safe to expose in frontend code.` : `Reused the existing search-only Algolia API key for the "${targetIndex}" index in app ${appId}.`
|
|
4872
|
+
);
|
|
4873
|
+
} catch (err) {
|
|
4874
|
+
searchKeyError = err.message;
|
|
4875
|
+
logger.warn(
|
|
4876
|
+
{ err: searchKeyError },
|
|
4877
|
+
"implement: could not provision a search-only API key; the .env value stays a placeholder"
|
|
4870
4878
|
);
|
|
4871
|
-
summaries.push(formatSummary("search", summary));
|
|
4872
|
-
const verification = await runVerificationUseCase();
|
|
4873
|
-
summaries.push(formatSummary("verification", verification.summary));
|
|
4874
|
-
if (verification.sufficient) {
|
|
4875
|
-
ctx.setUserInput("implementation", "success");
|
|
4876
|
-
const searchFilesChanged = (await listChangedFiles(worktree)).filter(
|
|
4877
|
-
(file) => !preSearchFiles.has(file)
|
|
4878
|
-
);
|
|
4879
|
-
track("AI Wizard Frontend Component Generated", {
|
|
4880
|
-
filePaths: searchFilesChanged
|
|
4881
|
-
});
|
|
4882
|
-
track("AI Wizard Wired to UI", {
|
|
4883
|
-
location_heuristic: searchLocation ?? "unknown"
|
|
4884
|
-
});
|
|
4885
|
-
break;
|
|
4886
|
-
}
|
|
4887
|
-
if (attempt === MAX_IMPLEMENT_VERIFICATION_ATTEMPTS) {
|
|
4888
|
-
ctx.setUserInput("implementation", "fail");
|
|
4889
|
-
throw new Error(
|
|
4890
|
-
`Implementation verification failed after ${MAX_IMPLEMENT_VERIFICATION_ATTEMPTS} attempts: ${verification.additionalInstructions ?? verification.summary}`
|
|
4891
|
-
);
|
|
4892
|
-
}
|
|
4893
|
-
extraInstructions = verificationRetryInstructions(verification);
|
|
4894
|
-
}
|
|
4895
|
-
let searchKey;
|
|
4896
|
-
let searchKeyError;
|
|
4897
|
-
if (appId) {
|
|
4898
|
-
try {
|
|
4899
|
-
const resolved2 = await resolveSearchOnlyKey(
|
|
4900
|
-
targetIndex,
|
|
4901
|
-
appId,
|
|
4902
|
-
envSearchKey
|
|
4903
|
-
);
|
|
4904
|
-
searchKey = resolved2.key;
|
|
4905
|
-
summaries.push(
|
|
4906
|
-
resolved2.source === "created" ? `Created a new search-only Algolia API key for the "${targetIndex}" index in app ${appId} \u2014 safe to expose in frontend code.` : `Reused the existing search-only Algolia API key for the "${targetIndex}" index in app ${appId}.`
|
|
4907
|
-
);
|
|
4908
|
-
} catch (err) {
|
|
4909
|
-
searchKeyError = err.message;
|
|
4910
|
-
logger.warn(
|
|
4911
|
-
{ err: searchKeyError },
|
|
4912
|
-
"implement: could not provision a search-only API key; the .env value stays a placeholder"
|
|
4913
|
-
);
|
|
4914
|
-
}
|
|
4915
4879
|
}
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4880
|
+
}
|
|
4881
|
+
finalSearchEnvVars = publicSearchEnvVars(
|
|
4882
|
+
publicEnvVarPrefix,
|
|
4883
|
+
targetIndex,
|
|
4884
|
+
appId,
|
|
4885
|
+
searchKey
|
|
4886
|
+
);
|
|
4887
|
+
const resolvedSearchEnvVars = finalSearchEnvVars.filter(
|
|
4888
|
+
(v) => !v.value.startsWith("<")
|
|
4889
|
+
);
|
|
4890
|
+
if (resolvedSearchEnvVars.length > 0) {
|
|
4891
|
+
const written = await writeSearchEnvValues(
|
|
4892
|
+
repoRoot,
|
|
4893
|
+
resolvedSearchEnvVars
|
|
4924
4894
|
);
|
|
4925
|
-
if (
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4895
|
+
if (written.length > 0) {
|
|
4896
|
+
summaries.push(`Wrote ${written.join(", ")} to .env.`);
|
|
4897
|
+
}
|
|
4898
|
+
const ignored = await ensureGitIgnored(repoRoot, join12(repoRoot, ".env"));
|
|
4899
|
+
if (ignored === "added") {
|
|
4900
|
+
summaries.push("Added .env to .gitignore.");
|
|
4901
|
+
} else if (ignored === "tracked") {
|
|
4902
|
+
summaries.push(
|
|
4903
|
+
'\u26A0\uFE0F .env is tracked by git, so a .gitignore rule cannot un-stage it. Run "git rm --cached .env" before committing, or the credentials go into history.'
|
|
4929
4904
|
);
|
|
4930
|
-
if (written.length > 0) {
|
|
4931
|
-
summaries.push(`Wrote ${written.join(", ")} to .env.`);
|
|
4932
|
-
}
|
|
4933
|
-
const ignored = await ensureGitIgnored(worktree, join12(worktree, ".env"));
|
|
4934
|
-
if (ignored === "added") {
|
|
4935
|
-
summaries.push("Added .env to .gitignore.");
|
|
4936
|
-
} else if (ignored === "tracked") {
|
|
4937
|
-
summaries.push(
|
|
4938
|
-
'\u26A0\uFE0F .env is tracked by git, so a .gitignore rule cannot un-stage it. Run "git rm --cached .env" before committing, or the credentials go into history.'
|
|
4939
|
-
);
|
|
4940
|
-
}
|
|
4941
|
-
const stale = [];
|
|
4942
|
-
for (const v of resolvedSearchEnvVars) {
|
|
4943
|
-
if (written.includes(v.name)) continue;
|
|
4944
|
-
const current = await readEnvVar(worktree, v.name);
|
|
4945
|
-
if (current && current !== v.value) stale.push(v);
|
|
4946
|
-
}
|
|
4947
|
-
if (stale.length > 0 && !envAppIdMismatch) {
|
|
4948
|
-
summaries.push(
|
|
4949
|
-
`\u26A0\uFE0F .env already assigns a different value to ${stale.map((v) => `${v.name} (should be ${v.value})`).join(", ")} \u2014 the wizard left it alone. Fix it by hand, or searches will fail.`
|
|
4950
|
-
);
|
|
4951
|
-
logger.warn(
|
|
4952
|
-
{ vars: stale.map((v) => v.name) },
|
|
4953
|
-
"implement: .env holds different values for the resolved search credentials; not overwriting them"
|
|
4954
|
-
);
|
|
4955
|
-
}
|
|
4956
4905
|
}
|
|
4957
|
-
const
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4906
|
+
const stale = [];
|
|
4907
|
+
for (const v of resolvedSearchEnvVars) {
|
|
4908
|
+
if (written.includes(v.name)) continue;
|
|
4909
|
+
const current = await readEnvVar(repoRoot, v.name);
|
|
4910
|
+
if (current && current !== v.value) stale.push(v);
|
|
4911
|
+
}
|
|
4912
|
+
if (stale.length > 0 && !envAppIdMismatch) {
|
|
4961
4913
|
summaries.push(
|
|
4962
|
-
|
|
4963
|
-
|
|
4914
|
+
`\u26A0\uFE0F .env already assigns a different value to ${stale.map((v) => `${v.name} (should be ${v.value})`).join(", ")} \u2014 the wizard left it alone. Fix it by hand, or searches will fail.`
|
|
4915
|
+
);
|
|
4916
|
+
logger.warn(
|
|
4917
|
+
{ vars: stale.map((v) => v.name) },
|
|
4918
|
+
"implement: .env holds different values for the resolved search credentials; not overwriting them"
|
|
4964
4919
|
);
|
|
4965
4920
|
}
|
|
4966
|
-
} else {
|
|
4967
|
-
ctx.setUserInput("implementation", "success");
|
|
4968
4921
|
}
|
|
4969
|
-
const
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4922
|
+
const unresolvedSearchEnvVars = finalSearchEnvVars.filter(
|
|
4923
|
+
(v) => v.value.startsWith("<")
|
|
4924
|
+
);
|
|
4925
|
+
if (unresolvedSearchEnvVars.length > 0) {
|
|
4926
|
+
summaries.push(
|
|
4927
|
+
`Could not resolve a value for ${unresolvedSearchEnvVars.map((v) => v.name).join(", ")} \u2014 fill it in manually in .env.` + (searchKeyError ? ` Reason: ${searchKeyError}` : "")
|
|
4973
4928
|
);
|
|
4974
4929
|
}
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
filesChanged,
|
|
4978
|
-
summary: summaries.join("\n\n"),
|
|
4979
|
-
worktreePath: worktree,
|
|
4980
|
-
...useCases.includes("ingestion") && ingestCommand ? {
|
|
4981
|
-
ingestCommand,
|
|
4982
|
-
ingestScriptRan,
|
|
4983
|
-
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4984
|
-
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
4985
|
-
} : {},
|
|
4986
|
-
...useCases.includes("search") ? { searchEnvVars: finalSearchEnvVars } : {}
|
|
4987
|
-
};
|
|
4988
|
-
} finally {
|
|
4989
|
-
process.chdir(repoRoot);
|
|
4930
|
+
} else {
|
|
4931
|
+
ctx.setUserInput("implementation", "success");
|
|
4990
4932
|
}
|
|
4933
|
+
return {
|
|
4934
|
+
ingestionSource,
|
|
4935
|
+
summary: summaries.join("\n\n"),
|
|
4936
|
+
...useCases.includes("ingestion") && ingestCommand ? {
|
|
4937
|
+
ingestCommand,
|
|
4938
|
+
ingestScriptRan,
|
|
4939
|
+
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4940
|
+
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
4941
|
+
} : {},
|
|
4942
|
+
...useCases.includes("search") ? { searchEnvVars: finalSearchEnvVars } : {}
|
|
4943
|
+
};
|
|
4991
4944
|
}
|
|
4992
4945
|
|
|
4993
4946
|
// src/workflows/default.ts
|
|
@@ -5059,10 +5012,7 @@ var defaultWorkflow = {
|
|
|
5059
5012
|
ctx.notify({
|
|
5060
5013
|
messages: ["Building your Algolia search experience\u2026"]
|
|
5061
5014
|
});
|
|
5062
|
-
|
|
5063
|
-
"ingestion"
|
|
5064
|
-
);
|
|
5065
|
-
return implement(ctx, ["search"], ingestion2?.worktreePath);
|
|
5015
|
+
return implement(ctx, ["search"]);
|
|
5066
5016
|
}
|
|
5067
5017
|
}),
|
|
5068
5018
|
defineStep({
|
|
@@ -5077,9 +5027,8 @@ var defaultWorkflow = {
|
|
|
5077
5027
|
"ingestion"
|
|
5078
5028
|
);
|
|
5079
5029
|
return reviewStep(ctx, {
|
|
5080
|
-
//
|
|
5081
|
-
//
|
|
5082
|
-
// an LLM-paraphrased command risks being wrong.
|
|
5030
|
+
// ingestCommand was already shown verbatim as a notice; an
|
|
5031
|
+
// LLM-paraphrased restatement in nextSteps risks being wrong.
|
|
5083
5032
|
nextStepsGuidance: ingestion2?.ingestScriptRan ? "The wizard already ran the ingestion script and records are in the index. Do NOT tell the user to run it again; instead point them at the target index to confirm the records. Do not restate the ingestion command \u2014 the wizard already showed it to them." : "Tell the user to run the ingestion script; do not restate the exact command \u2014 the wizard already showed it to them above."
|
|
5084
5033
|
});
|
|
5085
5034
|
}
|
|
@@ -5126,7 +5075,6 @@ var selectIndex = {
|
|
|
5126
5075
|
selection: "wizard_seed_products"
|
|
5127
5076
|
};
|
|
5128
5077
|
var ingestion = {
|
|
5129
|
-
filesChanged: ["algolia/ingest.mjs", "algolia/records.json", "package.json"],
|
|
5130
5078
|
summary: "Generated sample Product records and an ingestion script that pushes them to the target index with algoliasearch.",
|
|
5131
5079
|
ingestCommand: "node algolia/ingest.mjs",
|
|
5132
5080
|
ingestScriptRan: true,
|
|
@@ -5138,7 +5086,6 @@ var confirmFramework2 = {
|
|
|
5138
5086
|
frameworks: projectScan2.frameworks
|
|
5139
5087
|
};
|
|
5140
5088
|
var search = {
|
|
5141
|
-
filesChanged: ["src/components/Search.tsx", "src/components/Header.tsx", ".env"],
|
|
5142
5089
|
summary: "Added an InstantSearch-powered search box and results list, mounted in the shared header component.",
|
|
5143
5090
|
ingestionSource: "generated",
|
|
5144
5091
|
searchEnvVars: [
|
|
@@ -5151,7 +5098,7 @@ var review = {
|
|
|
5151
5098
|
"Ingested 25 generated Product records into wizard_seed_products.",
|
|
5152
5099
|
"Added an InstantSearch search experience to the shared header."
|
|
5153
5100
|
],
|
|
5154
|
-
reviewPrompt: "Review the Algolia ingestion and search changes
|
|
5101
|
+
reviewPrompt: "Review the Algolia ingestion and search changes.",
|
|
5155
5102
|
nextSteps: ["Point the ingestion script at your real product data."]
|
|
5156
5103
|
};
|
|
5157
5104
|
var SEEDS = {
|
|
@@ -5268,9 +5215,9 @@ Options:
|
|
|
5268
5215
|
steps pre-filled with test data. Pass with no value to print
|
|
5269
5216
|
the step ids. See CONTRIBUTING.md.
|
|
5270
5217
|
--no-telemetry Send no telemetry or analytics for this run.
|
|
5271
|
-
--reset-on-run Wipe this project's wizard state (run state, AI consent
|
|
5272
|
-
|
|
5273
|
-
|
|
5218
|
+
--reset-on-run Wipe this project's wizard state (run state, AI consent)
|
|
5219
|
+
before starting, so the run behaves like a first-ever
|
|
5220
|
+
run. Also drops every API key the wizard has
|
|
5274
5221
|
stored in your keychain (or, where the platform has none,
|
|
5275
5222
|
the encrypted file it falls back to \u2014 see CONTRIBUTING.md),
|
|
5276
5223
|
for this project and any other, so later runs create new
|
|
@@ -5310,7 +5257,7 @@ function parseCliArgs(argv) {
|
|
|
5310
5257
|
}
|
|
5311
5258
|
|
|
5312
5259
|
// src/lib/resetState.ts
|
|
5313
|
-
import { readdir as
|
|
5260
|
+
import { readdir as readdir3, rm as rm2 } from "node:fs/promises";
|
|
5314
5261
|
import { join as join13 } from "node:path";
|
|
5315
5262
|
var KEEP = ["wizard.log"];
|
|
5316
5263
|
async function resetProjectState() {
|
|
@@ -5318,7 +5265,7 @@ async function resetProjectState() {
|
|
|
5318
5265
|
await forgetResolvedKeys();
|
|
5319
5266
|
let entries;
|
|
5320
5267
|
try {
|
|
5321
|
-
entries = await
|
|
5268
|
+
entries = await readdir3(dir);
|
|
5322
5269
|
} catch {
|
|
5323
5270
|
return { dir, removed: [] };
|
|
5324
5271
|
}
|
|
@@ -5371,7 +5318,7 @@ function delay(ms) {
|
|
|
5371
5318
|
}
|
|
5372
5319
|
|
|
5373
5320
|
// src/main.tsx
|
|
5374
|
-
import { jsx as
|
|
5321
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
5375
5322
|
async function startup() {
|
|
5376
5323
|
setProjectRoot(process.cwd());
|
|
5377
5324
|
let args;
|
|
@@ -5421,7 +5368,7 @@ ${formatStepList(workflow)}`);
|
|
|
5421
5368
|
}
|
|
5422
5369
|
async function run(workflow) {
|
|
5423
5370
|
const store = useWizard.getState();
|
|
5424
|
-
const instance = render(/* @__PURE__ */
|
|
5371
|
+
const instance = render(/* @__PURE__ */ jsx16(App, {}), { incrementalRendering: true });
|
|
5425
5372
|
await store.waitForStart();
|
|
5426
5373
|
let user = await getUser();
|
|
5427
5374
|
if (!user) {
|