@algolia/wizard 0.9.0-rc.88.85 → 0.9.0-rc.93.108
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/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 Box16, Text as Text16, useApp, useInput as useInput7, useWindowSize as useWindowSize8 } from "ink";
|
|
8
8
|
|
|
9
9
|
// src/core/store.ts
|
|
10
10
|
import { create } from "zustand";
|
|
@@ -17,10 +17,8 @@ function npxArgs(args) {
|
|
|
17
17
|
return ["--yes", "@algolia/cli@latest", ...args];
|
|
18
18
|
}
|
|
19
19
|
var shell = process.platform === "win32";
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
const { ALGOLIA_API_KEY: _adminKey, ...rest } = process.env;
|
|
23
|
-
return rest;
|
|
20
|
+
function mask(text, secret) {
|
|
21
|
+
return secret ? text.replaceAll(secret, "***") : text;
|
|
24
22
|
}
|
|
25
23
|
function lineSplitter(emit) {
|
|
26
24
|
let buffer = "";
|
|
@@ -45,14 +43,12 @@ var stderrSink = (stream, line) => {
|
|
|
45
43
|
if (stream === "stdout") return;
|
|
46
44
|
wizardSink(stream, line);
|
|
47
45
|
};
|
|
48
|
-
function runAlgoliaCli(args, { onOutput,
|
|
46
|
+
function runAlgoliaCli(args, { onOutput, redact } = {}) {
|
|
49
47
|
const store = useWizard.getState();
|
|
50
|
-
const
|
|
48
|
+
const command = mask(args.join(" "), redact);
|
|
49
|
+
const logId = store.logStart("tool", `algolia ${command}`);
|
|
51
50
|
return new Promise((resolve4, reject) => {
|
|
52
|
-
const child = spawn("npx", npxArgs(args), {
|
|
53
|
-
shell,
|
|
54
|
-
env: childEnv(withoutAdminKey)
|
|
55
|
-
});
|
|
51
|
+
const child = spawn("npx", npxArgs(args), { shell });
|
|
56
52
|
let stdout = "";
|
|
57
53
|
let stderr = "";
|
|
58
54
|
const splitters = {
|
|
@@ -79,13 +75,13 @@ function runAlgoliaCli(args, { onOutput, withoutAdminKey } = {}) {
|
|
|
79
75
|
const failed = stderr.trim();
|
|
80
76
|
let detail = "";
|
|
81
77
|
if (failed) {
|
|
82
|
-
detail = `: ${failed}`;
|
|
78
|
+
detail = `: ${mask(failed, redact)}`;
|
|
83
79
|
} else if (stdout.trim()) {
|
|
84
80
|
detail = " (no stderr; stdout withheld \u2014 it may contain credentials)";
|
|
85
81
|
}
|
|
86
82
|
reject(
|
|
87
83
|
new Error(
|
|
88
|
-
`Algolia CLI \`${
|
|
84
|
+
`Algolia CLI \`${command}\` failed (exit ${code})${detail}`
|
|
89
85
|
)
|
|
90
86
|
);
|
|
91
87
|
}
|
|
@@ -626,10 +622,13 @@ function Notices() {
|
|
|
626
622
|
}
|
|
627
623
|
|
|
628
624
|
// src/ui/PromptInput.tsx
|
|
629
|
-
import { Box as
|
|
625
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
630
626
|
import TextInput from "ink-text-input";
|
|
631
627
|
import { useState as useState5 } from "react";
|
|
632
628
|
|
|
629
|
+
// src/ui/CommandApproval.tsx
|
|
630
|
+
import { Box as Box5, Text as Text5, useInput } from "ink";
|
|
631
|
+
|
|
633
632
|
// src/ui/NextAction.tsx
|
|
634
633
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
635
634
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -653,14 +652,60 @@ function NextAction({
|
|
|
653
652
|
] });
|
|
654
653
|
}
|
|
655
654
|
|
|
655
|
+
// src/ui/CommandApproval.tsx
|
|
656
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
657
|
+
function CommandApproval({
|
|
658
|
+
command,
|
|
659
|
+
onDecide
|
|
660
|
+
}) {
|
|
661
|
+
useInput((_input, key) => {
|
|
662
|
+
if (key.return) onDecide("approve");
|
|
663
|
+
else if (key.escape) onDecide("reject");
|
|
664
|
+
});
|
|
665
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
|
|
666
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, bold: true, children: "Run this command?" }),
|
|
667
|
+
/* @__PURE__ */ jsxs4(
|
|
668
|
+
Box5,
|
|
669
|
+
{
|
|
670
|
+
flexDirection: "column",
|
|
671
|
+
paddingLeft: 2,
|
|
672
|
+
borderStyle: "single",
|
|
673
|
+
borderColor: COLORS.success,
|
|
674
|
+
borderTop: false,
|
|
675
|
+
borderBottom: false,
|
|
676
|
+
borderRight: false,
|
|
677
|
+
gap: 1,
|
|
678
|
+
children: [
|
|
679
|
+
/* @__PURE__ */ jsxs4(Box5, { children: [
|
|
680
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "$ " }),
|
|
681
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.strong, wrap: "wrap", children: command.command })
|
|
682
|
+
] }),
|
|
683
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
684
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "in:" }),
|
|
685
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
|
|
686
|
+
] }),
|
|
687
|
+
command.explanation && /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
688
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "why:" }),
|
|
689
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
|
|
690
|
+
] })
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
),
|
|
694
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
695
|
+
/* @__PURE__ */ jsx4(NextAction, { action: "approve", keyHint: "enter" }),
|
|
696
|
+
/* @__PURE__ */ jsx4(NextAction, { action: "reject", keyHint: "esc", hierarchy: "secondary" })
|
|
697
|
+
] })
|
|
698
|
+
] });
|
|
699
|
+
}
|
|
700
|
+
|
|
656
701
|
// src/ui/SelectPrompt.tsx
|
|
657
|
-
import { Box as
|
|
702
|
+
import { Box as Box7, Text as Text7, useInput as useInput2, useWindowSize as useWindowSize5 } from "ink";
|
|
658
703
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
659
704
|
|
|
660
705
|
// src/ui/ScrollView.tsx
|
|
661
|
-
import { Box as
|
|
706
|
+
import { Box as Box6, Text as Text6, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
|
|
662
707
|
import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
663
|
-
import { jsxs as
|
|
708
|
+
import { jsxs as jsxs5 } from "react/jsx-runtime";
|
|
664
709
|
var INDICATOR_ROWS = 2;
|
|
665
710
|
function fittedWidth(node, columns) {
|
|
666
711
|
let left = 0;
|
|
@@ -730,14 +775,14 @@ function useScrollWindow({
|
|
|
730
775
|
};
|
|
731
776
|
}
|
|
732
777
|
function ScrollView({ scroll, children }) {
|
|
733
|
-
return /* @__PURE__ */
|
|
734
|
-
scroll.hiddenAbove > 0 && /* @__PURE__ */
|
|
778
|
+
return /* @__PURE__ */ jsxs5(Box6, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
779
|
+
scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
735
780
|
"\u2191 ",
|
|
736
781
|
scroll.hiddenAbove,
|
|
737
782
|
" more"
|
|
738
783
|
] }),
|
|
739
784
|
children,
|
|
740
|
-
scroll.hiddenBelow > 0 && /* @__PURE__ */
|
|
785
|
+
scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
741
786
|
"\u2193 ",
|
|
742
787
|
scroll.hiddenBelow,
|
|
743
788
|
" more"
|
|
@@ -746,7 +791,7 @@ function ScrollView({ scroll, children }) {
|
|
|
746
791
|
}
|
|
747
792
|
|
|
748
793
|
// src/ui/SelectPrompt.tsx
|
|
749
|
-
import { jsx as
|
|
794
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
750
795
|
var CANCEL = "cancel";
|
|
751
796
|
var ARROW_WIDTH = 4;
|
|
752
797
|
var COLUMN_GAP = 2;
|
|
@@ -808,7 +853,7 @@ function SelectPrompt({
|
|
|
808
853
|
revealIndex(index);
|
|
809
854
|
}, [index, revealIndex]);
|
|
810
855
|
const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
811
|
-
|
|
856
|
+
useInput2((input, key) => {
|
|
812
857
|
if (rows.length === 0) return;
|
|
813
858
|
if (key.upArrow || input === "k") {
|
|
814
859
|
setIndex((i) => (i - 1 + rows.length) % rows.length);
|
|
@@ -831,56 +876,56 @@ function SelectPrompt({
|
|
|
831
876
|
}
|
|
832
877
|
}
|
|
833
878
|
});
|
|
834
|
-
return /* @__PURE__ */
|
|
835
|
-
/* @__PURE__ */
|
|
836
|
-
error && /* @__PURE__ */
|
|
837
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
838
|
-
table && /* @__PURE__ */
|
|
839
|
-
/* @__PURE__ */
|
|
840
|
-
question && /* @__PURE__ */
|
|
841
|
-
helpText && /* @__PURE__ */
|
|
879
|
+
return /* @__PURE__ */ jsx5(Box7, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, width, children: [
|
|
880
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
|
|
881
|
+
error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: error }),
|
|
882
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
883
|
+
table && /* @__PURE__ */ jsx5(Table, { columns: table.columns, rows: table.rows }),
|
|
884
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
885
|
+
question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: question }),
|
|
886
|
+
helpText && /* @__PURE__ */ jsx5(Text7, { color: COLORS.dim, children: helpText })
|
|
842
887
|
] })
|
|
843
888
|
] }),
|
|
844
|
-
/* @__PURE__ */
|
|
889
|
+
/* @__PURE__ */ jsx5(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
|
|
845
890
|
const i = scroll.offset + visibleIndex;
|
|
846
891
|
const highlighted = i === index;
|
|
847
892
|
const isCancel = i === cancelIndex;
|
|
848
893
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
849
894
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
850
895
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
851
|
-
const label = /* @__PURE__ */
|
|
896
|
+
const label = /* @__PURE__ */ jsxs6(Text7, { color: labelColor, wrap: "truncate", children: [
|
|
852
897
|
highlighted ? "\u276F " : " ",
|
|
853
898
|
bullet,
|
|
854
899
|
option
|
|
855
900
|
] });
|
|
856
901
|
const isText = sec?.kind === "text";
|
|
857
|
-
return /* @__PURE__ */
|
|
858
|
-
|
|
902
|
+
return /* @__PURE__ */ jsxs6(
|
|
903
|
+
Box7,
|
|
859
904
|
{
|
|
860
905
|
width: isText ? "100%" : barWidth,
|
|
861
906
|
paddingX: 1,
|
|
862
907
|
paddingY: 1,
|
|
863
908
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
864
909
|
children: [
|
|
865
|
-
/* @__PURE__ */
|
|
866
|
-
isText && textWidth > 0 && /* @__PURE__ */
|
|
867
|
-
|
|
910
|
+
/* @__PURE__ */ jsx5(Box7, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
911
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx5(Box7, { width: textWidth, children: /* @__PURE__ */ jsx5(
|
|
912
|
+
Text7,
|
|
868
913
|
{
|
|
869
914
|
wrap: "truncate",
|
|
870
915
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
871
916
|
children: sec.value
|
|
872
917
|
}
|
|
873
918
|
) }),
|
|
874
|
-
sec?.kind === "badge" && /* @__PURE__ */
|
|
919
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx5(Box7, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx5(Text7, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
875
920
|
]
|
|
876
921
|
},
|
|
877
922
|
`row-${i}`
|
|
878
923
|
);
|
|
879
924
|
}) }),
|
|
880
|
-
/* @__PURE__ */
|
|
925
|
+
/* @__PURE__ */ jsx5(Box7, { flexShrink: 0, children: /* @__PURE__ */ jsx5(Text7, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
881
926
|
i > 0 ? " " : "",
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
/* @__PURE__ */
|
|
927
|
+
/* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: key }),
|
|
928
|
+
/* @__PURE__ */ jsxs6(Text7, { color: COLORS.dim, children: [
|
|
884
929
|
" ",
|
|
885
930
|
label
|
|
886
931
|
] })
|
|
@@ -889,23 +934,23 @@ function SelectPrompt({
|
|
|
889
934
|
}
|
|
890
935
|
|
|
891
936
|
// src/ui/PromptInput.tsx
|
|
892
|
-
import { jsx as
|
|
937
|
+
import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
893
938
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
894
939
|
function EnterToContinuePrompt({
|
|
895
940
|
question,
|
|
896
941
|
messages,
|
|
897
942
|
onDecide
|
|
898
943
|
}) {
|
|
899
|
-
|
|
944
|
+
useInput3((_input, key) => {
|
|
900
945
|
if (key.return) onDecide(true);
|
|
901
946
|
else if (key.escape) onDecide(false);
|
|
902
947
|
});
|
|
903
|
-
return /* @__PURE__ */
|
|
904
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
905
|
-
question && /* @__PURE__ */
|
|
906
|
-
/* @__PURE__ */
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
/* @__PURE__ */
|
|
948
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, children: [
|
|
949
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
950
|
+
question && /* @__PURE__ */ jsx6(Text8, { color: COLORS.primary, children: question }),
|
|
951
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
|
|
952
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "continue", keyHint: "enter" }),
|
|
953
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
909
954
|
] })
|
|
910
955
|
] });
|
|
911
956
|
}
|
|
@@ -913,11 +958,11 @@ function PromptInput() {
|
|
|
913
958
|
const { phase, inputReq, submitInput } = useWizard();
|
|
914
959
|
const [draft, setDraft] = useState5("");
|
|
915
960
|
if (phase === "done" || phase === "error") {
|
|
916
|
-
return /* @__PURE__ */
|
|
961
|
+
return /* @__PURE__ */ jsx6(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text8, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
917
962
|
}
|
|
918
963
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
919
964
|
if (inputReq.promptType === "multipleChoice") {
|
|
920
|
-
return /* @__PURE__ */
|
|
965
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
921
966
|
SelectPrompt,
|
|
922
967
|
{
|
|
923
968
|
question: inputReq.prompt,
|
|
@@ -934,7 +979,7 @@ function PromptInput() {
|
|
|
934
979
|
) });
|
|
935
980
|
}
|
|
936
981
|
if (inputReq.promptType === "multiSelect") {
|
|
937
|
-
return /* @__PURE__ */
|
|
982
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
938
983
|
SelectPrompt,
|
|
939
984
|
{
|
|
940
985
|
multi: true,
|
|
@@ -949,7 +994,7 @@ function PromptInput() {
|
|
|
949
994
|
) });
|
|
950
995
|
}
|
|
951
996
|
if (inputReq.promptType === "notice") {
|
|
952
|
-
return /* @__PURE__ */
|
|
997
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
953
998
|
SelectPrompt,
|
|
954
999
|
{
|
|
955
1000
|
question: inputReq.prompt,
|
|
@@ -960,7 +1005,7 @@ function PromptInput() {
|
|
|
960
1005
|
) });
|
|
961
1006
|
}
|
|
962
1007
|
if (inputReq.promptType === "enterToContinue") {
|
|
963
|
-
return /* @__PURE__ */
|
|
1008
|
+
return /* @__PURE__ */ jsx6(
|
|
964
1009
|
EnterToContinuePrompt,
|
|
965
1010
|
{
|
|
966
1011
|
question: inputReq.prompt,
|
|
@@ -969,9 +1014,12 @@ function PromptInput() {
|
|
|
969
1014
|
}
|
|
970
1015
|
);
|
|
971
1016
|
}
|
|
1017
|
+
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1018
|
+
return /* @__PURE__ */ jsx6(CommandApproval, { command: inputReq.command, onDecide: submitInput });
|
|
1019
|
+
}
|
|
972
1020
|
if (inputReq.promptType === "acceptReject") {
|
|
973
1021
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
974
|
-
return /* @__PURE__ */
|
|
1022
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
975
1023
|
SelectPrompt,
|
|
976
1024
|
{
|
|
977
1025
|
question: inputReq.prompt,
|
|
@@ -982,15 +1030,15 @@ function PromptInput() {
|
|
|
982
1030
|
}
|
|
983
1031
|
) });
|
|
984
1032
|
}
|
|
985
|
-
return /* @__PURE__ */
|
|
986
|
-
inputReq.error && /* @__PURE__ */
|
|
987
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
989
|
-
/* @__PURE__ */
|
|
1033
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1034
|
+
inputReq.error && /* @__PURE__ */ jsx6(Text8, { color: COLORS.danger, children: inputReq.error }),
|
|
1035
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
1036
|
+
/* @__PURE__ */ jsxs7(Box8, { children: [
|
|
1037
|
+
/* @__PURE__ */ jsxs7(Text8, { color: COLORS.primary, children: [
|
|
990
1038
|
inputReq.prompt,
|
|
991
1039
|
" "
|
|
992
1040
|
] }),
|
|
993
|
-
/* @__PURE__ */
|
|
1041
|
+
/* @__PURE__ */ jsx6(
|
|
994
1042
|
TextInput,
|
|
995
1043
|
{
|
|
996
1044
|
value: draft,
|
|
@@ -1008,7 +1056,7 @@ function PromptInput() {
|
|
|
1008
1056
|
// src/ui/Welcome.tsx
|
|
1009
1057
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1010
1058
|
import { fileURLToPath } from "node:url";
|
|
1011
|
-
import { Box as
|
|
1059
|
+
import { Box as Box9, Spacer, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
1012
1060
|
|
|
1013
1061
|
// src/ui/copy/welcome.ts
|
|
1014
1062
|
var sidebarItems = [
|
|
@@ -1021,12 +1069,12 @@ var sidebarItems = [
|
|
|
1021
1069
|
description: "push 100 records to Algolia in seconds"
|
|
1022
1070
|
},
|
|
1023
1071
|
{
|
|
1024
|
-
title: "detect your
|
|
1025
|
-
description: "
|
|
1072
|
+
title: "detect your stack",
|
|
1073
|
+
description: "whatever language and framework you already use"
|
|
1026
1074
|
},
|
|
1027
1075
|
{
|
|
1028
1076
|
title: "scaffold a search UI",
|
|
1029
|
-
description: "a
|
|
1077
|
+
description: "a search box and results, wired into your app"
|
|
1030
1078
|
},
|
|
1031
1079
|
{
|
|
1032
1080
|
title: "ship it",
|
|
@@ -1036,20 +1084,20 @@ var sidebarItems = [
|
|
|
1036
1084
|
|
|
1037
1085
|
// src/ui/Welcome.tsx
|
|
1038
1086
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
1039
|
-
import { jsx as
|
|
1087
|
+
import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1040
1088
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
1041
1089
|
function SidebarItem({
|
|
1042
1090
|
title,
|
|
1043
1091
|
description
|
|
1044
1092
|
}) {
|
|
1045
|
-
return /* @__PURE__ */
|
|
1046
|
-
/* @__PURE__ */
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1093
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
|
|
1094
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, children: [
|
|
1095
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.success, children: "\u2192" }),
|
|
1096
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: title })
|
|
1049
1097
|
] }),
|
|
1050
|
-
/* @__PURE__ */
|
|
1051
|
-
/* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1098
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 2, children: [
|
|
1099
|
+
/* @__PURE__ */ jsx7(Spacer, {}),
|
|
1100
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: description })
|
|
1053
1101
|
] })
|
|
1054
1102
|
] });
|
|
1055
1103
|
}
|
|
@@ -1057,7 +1105,7 @@ function Welcome() {
|
|
|
1057
1105
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
1058
1106
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
1059
1107
|
const { rows } = useWindowSize6();
|
|
1060
|
-
|
|
1108
|
+
useInput4((input, key) => {
|
|
1061
1109
|
if (key.return) confirmStart();
|
|
1062
1110
|
else if (input === "i") openLearnMore();
|
|
1063
1111
|
});
|
|
@@ -1075,16 +1123,16 @@ function Welcome() {
|
|
|
1075
1123
|
if (rows < 30) {
|
|
1076
1124
|
layout = scales["small"];
|
|
1077
1125
|
}
|
|
1078
|
-
return /* @__PURE__ */
|
|
1079
|
-
/* @__PURE__ */
|
|
1080
|
-
|
|
1126
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
1127
|
+
/* @__PURE__ */ jsx7(
|
|
1128
|
+
Box9,
|
|
1081
1129
|
{
|
|
1082
1130
|
paddingY: layout.main.padding.y,
|
|
1083
1131
|
paddingX: layout.main.padding.x,
|
|
1084
1132
|
flexDirection: "column",
|
|
1085
1133
|
justifyContent: "center",
|
|
1086
|
-
children: /* @__PURE__ */
|
|
1087
|
-
/* @__PURE__ */
|
|
1134
|
+
children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 2, children: [
|
|
1135
|
+
/* @__PURE__ */ jsx7(InkPictureProvider, { children: /* @__PURE__ */ jsx7(
|
|
1088
1136
|
Image,
|
|
1089
1137
|
{
|
|
1090
1138
|
src: IMAGE_PATH,
|
|
@@ -1095,16 +1143,16 @@ function Welcome() {
|
|
|
1095
1143
|
protocol: "halfBlock"
|
|
1096
1144
|
}
|
|
1097
1145
|
) }),
|
|
1098
|
-
/* @__PURE__ */
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1146
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1147
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, flexDirection: "column", children: [
|
|
1148
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
1149
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
1102
1150
|
] })
|
|
1103
1151
|
] })
|
|
1104
1152
|
}
|
|
1105
1153
|
),
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
|
|
1154
|
+
/* @__PURE__ */ jsxs8(
|
|
1155
|
+
Box9,
|
|
1108
1156
|
{
|
|
1109
1157
|
backgroundColor: COLORS.bg.sidebar,
|
|
1110
1158
|
width: 40,
|
|
@@ -1114,8 +1162,8 @@ function Welcome() {
|
|
|
1114
1162
|
flexDirection: "column",
|
|
1115
1163
|
justifyContent: "center",
|
|
1116
1164
|
children: [
|
|
1117
|
-
/* @__PURE__ */
|
|
1118
|
-
sidebarItems.map((i, idx) => /* @__PURE__ */
|
|
1165
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
1166
|
+
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx7(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
1119
1167
|
]
|
|
1120
1168
|
}
|
|
1121
1169
|
)
|
|
@@ -1124,7 +1172,7 @@ function Welcome() {
|
|
|
1124
1172
|
|
|
1125
1173
|
// src/ui/LearnMore.tsx
|
|
1126
1174
|
import { Fragment as Fragment2 } from "react";
|
|
1127
|
-
import { Box as
|
|
1175
|
+
import { Box as Box10, Text as Text10, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1128
1176
|
|
|
1129
1177
|
// src/ui/copy/learn-more.ts
|
|
1130
1178
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -1132,12 +1180,17 @@ var accessItems = [
|
|
|
1132
1180
|
{
|
|
1133
1181
|
tag: "READ",
|
|
1134
1182
|
title: "Project files",
|
|
1135
|
-
description: "reads
|
|
1183
|
+
description: "reads manifests, configs & source to detect your stack. Read-only; nothing is uploaded."
|
|
1136
1184
|
},
|
|
1137
1185
|
{
|
|
1138
1186
|
tag: "WRITE",
|
|
1139
1187
|
title: "Code changes",
|
|
1140
|
-
description: "creates & edits files (search UI, config)
|
|
1188
|
+
description: "creates & edits files (search UI, config) in a throwaway git worktree \u2014 your checkout is never touched."
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
tag: "EXEC",
|
|
1192
|
+
title: "Setup commands",
|
|
1193
|
+
description: "runs dependency installs, the ingestion script & your own checks. Every command is shown in full and needs your OK; its output is shown as-is, so a command that prints a secret will display it."
|
|
1141
1194
|
},
|
|
1142
1195
|
{
|
|
1143
1196
|
tag: "NET",
|
|
@@ -1147,13 +1200,13 @@ var accessItems = [
|
|
|
1147
1200
|
{
|
|
1148
1201
|
tag: "KEY",
|
|
1149
1202
|
title: "Credentials",
|
|
1150
|
-
description: "
|
|
1203
|
+
description: "writes your Algolia app id and a search-only key (safe to expose) to .env in the worktree."
|
|
1151
1204
|
}
|
|
1152
1205
|
];
|
|
1153
1206
|
var neverItems = [
|
|
1154
1207
|
"Send your source code to a model or third party",
|
|
1155
1208
|
"Commit or push to git",
|
|
1156
|
-
"
|
|
1209
|
+
"Run a command you haven't approved"
|
|
1157
1210
|
];
|
|
1158
1211
|
var policyLinks = [
|
|
1159
1212
|
{ label: "Terms", url: "https://www.algolia.com/policies/terms" },
|
|
@@ -1161,10 +1214,11 @@ var policyLinks = [
|
|
|
1161
1214
|
];
|
|
1162
1215
|
|
|
1163
1216
|
// src/ui/LearnMore.tsx
|
|
1164
|
-
import { jsx as
|
|
1217
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1165
1218
|
var TAG_COLORS = {
|
|
1166
1219
|
READ: COLORS.success,
|
|
1167
1220
|
WRITE: COLORS.badge,
|
|
1221
|
+
EXEC: COLORS.danger,
|
|
1168
1222
|
NET: COLORS.accent,
|
|
1169
1223
|
KEY: COLORS.muted
|
|
1170
1224
|
};
|
|
@@ -1177,12 +1231,12 @@ function NeverLine({
|
|
|
1177
1231
|
}) {
|
|
1178
1232
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
1179
1233
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
1180
|
-
return /* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
1234
|
+
return /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1235
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" }),
|
|
1182
1236
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
1183
|
-
segments.map((s, i) => /* @__PURE__ */
|
|
1237
|
+
segments.map((s, i) => /* @__PURE__ */ jsx8(Text10, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
1184
1238
|
" ".repeat(rightPad),
|
|
1185
|
-
/* @__PURE__ */
|
|
1239
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" })
|
|
1186
1240
|
] });
|
|
1187
1241
|
}
|
|
1188
1242
|
function LearnMore() {
|
|
@@ -1190,12 +1244,12 @@ function LearnMore() {
|
|
|
1190
1244
|
const backToHome = useWizard((s) => s.backToHome);
|
|
1191
1245
|
const { columns } = useWindowSize7();
|
|
1192
1246
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
1193
|
-
|
|
1247
|
+
useInput5((_input, key) => {
|
|
1194
1248
|
if (key.escape) backToHome();
|
|
1195
1249
|
else if (key.return) confirmStart();
|
|
1196
1250
|
});
|
|
1197
|
-
return /* @__PURE__ */
|
|
1198
|
-
|
|
1251
|
+
return /* @__PURE__ */ jsxs9(
|
|
1252
|
+
Box10,
|
|
1199
1253
|
{
|
|
1200
1254
|
flexDirection: "column",
|
|
1201
1255
|
paddingX: PADDING_X,
|
|
@@ -1203,31 +1257,31 @@ function LearnMore() {
|
|
|
1203
1257
|
width: "100%",
|
|
1204
1258
|
gap: 1,
|
|
1205
1259
|
children: [
|
|
1206
|
-
/* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1208
|
-
/* @__PURE__ */
|
|
1209
|
-
/* @__PURE__ */
|
|
1210
|
-
/* @__PURE__ */
|
|
1211
|
-
/* @__PURE__ */
|
|
1212
|
-
/* @__PURE__ */
|
|
1213
|
-
/* @__PURE__ */
|
|
1214
|
-
/* @__PURE__ */
|
|
1260
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1261
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: accessIntro }),
|
|
1262
|
+
/* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
1263
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1264
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1265
|
+
/* @__PURE__ */ jsx8(Box10, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text10, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1266
|
+
/* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1267
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1268
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
1215
1269
|
] }) })
|
|
1216
1270
|
] })
|
|
1217
1271
|
] }, item.tag)) }),
|
|
1218
|
-
/* @__PURE__ */
|
|
1219
|
-
/* @__PURE__ */
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1272
|
+
/* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "column", children: [
|
|
1273
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
1274
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1275
|
+
/* @__PURE__ */ jsx8(
|
|
1222
1276
|
NeverLine,
|
|
1223
1277
|
{
|
|
1224
1278
|
width: dividerWidth,
|
|
1225
1279
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1226
1280
|
}
|
|
1227
1281
|
),
|
|
1228
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1229
|
-
/* @__PURE__ */
|
|
1230
|
-
/* @__PURE__ */
|
|
1282
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
1283
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1284
|
+
/* @__PURE__ */ jsx8(
|
|
1231
1285
|
NeverLine,
|
|
1232
1286
|
{
|
|
1233
1287
|
width: dividerWidth,
|
|
@@ -1239,24 +1293,24 @@ function LearnMore() {
|
|
|
1239
1293
|
}
|
|
1240
1294
|
)
|
|
1241
1295
|
] }, item)),
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */
|
|
1296
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1297
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1244
1298
|
] }),
|
|
1245
|
-
/* @__PURE__ */
|
|
1246
|
-
/* @__PURE__ */
|
|
1247
|
-
/* @__PURE__ */
|
|
1299
|
+
/* @__PURE__ */ jsx8(Box10, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1300
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1301
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.accent, children: link.url })
|
|
1248
1302
|
] }, link.label)) }),
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1253
|
-
/* @__PURE__ */
|
|
1303
|
+
/* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1304
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1305
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
|
|
1306
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "esc" }),
|
|
1307
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "] back" })
|
|
1254
1308
|
] }),
|
|
1255
|
-
/* @__PURE__ */
|
|
1256
|
-
/* @__PURE__ */
|
|
1257
|
-
/* @__PURE__ */
|
|
1258
|
-
/* @__PURE__ */
|
|
1259
|
-
/* @__PURE__ */
|
|
1309
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1310
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
|
|
1311
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "enter" }),
|
|
1312
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "]" }),
|
|
1313
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1260
1314
|
] })
|
|
1261
1315
|
] })
|
|
1262
1316
|
]
|
|
@@ -1265,10 +1319,10 @@ function LearnMore() {
|
|
|
1265
1319
|
}
|
|
1266
1320
|
|
|
1267
1321
|
// src/ui/Sidebar.tsx
|
|
1268
|
-
import { Box as
|
|
1322
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1269
1323
|
|
|
1270
1324
|
// src/ui/Steps.tsx
|
|
1271
|
-
import { Box as
|
|
1325
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1272
1326
|
import Spinner from "ink-spinner";
|
|
1273
1327
|
|
|
1274
1328
|
// src/core/persistence.ts
|
|
@@ -1297,12 +1351,12 @@ async function clearWorkflowState(workflowId) {
|
|
|
1297
1351
|
}
|
|
1298
1352
|
|
|
1299
1353
|
// src/ui/Steps.tsx
|
|
1300
|
-
import { jsx as
|
|
1354
|
+
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1301
1355
|
function Steps() {
|
|
1302
1356
|
const { steps } = useWizard();
|
|
1303
1357
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1304
|
-
return /* @__PURE__ */
|
|
1305
|
-
s.status === "running" ? /* @__PURE__ */
|
|
1358
|
+
return /* @__PURE__ */ jsx9(Box11, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx9(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status[s.status], children: [
|
|
1359
|
+
s.status === "running" ? /* @__PURE__ */ jsx9(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1306
1360
|
" ",
|
|
1307
1361
|
s.title
|
|
1308
1362
|
] }) }, s.id)) });
|
|
@@ -1311,27 +1365,27 @@ function CurrentStep() {
|
|
|
1311
1365
|
const { steps } = useWizard();
|
|
1312
1366
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1313
1367
|
if (!currentStep) return null;
|
|
1314
|
-
return /* @__PURE__ */
|
|
1315
|
-
/* @__PURE__ */
|
|
1368
|
+
return /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status.running, children: [
|
|
1369
|
+
/* @__PURE__ */ jsx9(Spinner, { type: "dots" }),
|
|
1316
1370
|
" ",
|
|
1317
1371
|
` ${currentStep.title}`
|
|
1318
1372
|
] });
|
|
1319
1373
|
}
|
|
1320
1374
|
|
|
1321
1375
|
// src/ui/Progress.tsx
|
|
1322
|
-
import { Box as
|
|
1323
|
-
import { jsx as
|
|
1376
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1377
|
+
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1324
1378
|
function Progress() {
|
|
1325
1379
|
const { steps, currentStepIndex } = useWizard();
|
|
1326
1380
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1327
1381
|
if (visibleSteps.length === 0) return null;
|
|
1328
1382
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1329
1383
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1330
|
-
return /* @__PURE__ */
|
|
1331
|
-
/* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1334
|
-
/* @__PURE__ */
|
|
1384
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1385
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "STEP" }),
|
|
1386
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: activeStepNumber }),
|
|
1387
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: "/" }),
|
|
1388
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: visibleSteps.length })
|
|
1335
1389
|
] });
|
|
1336
1390
|
}
|
|
1337
1391
|
|
|
@@ -1342,10 +1396,10 @@ var sidebarCommands = [
|
|
|
1342
1396
|
];
|
|
1343
1397
|
|
|
1344
1398
|
// src/ui/Sidebar.tsx
|
|
1345
|
-
import { jsx as
|
|
1399
|
+
import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1346
1400
|
function Sidebar() {
|
|
1347
|
-
return /* @__PURE__ */
|
|
1348
|
-
|
|
1401
|
+
return /* @__PURE__ */ jsxs12(
|
|
1402
|
+
Box13,
|
|
1349
1403
|
{
|
|
1350
1404
|
backgroundColor: "#14171E",
|
|
1351
1405
|
width: 30,
|
|
@@ -1354,16 +1408,16 @@ function Sidebar() {
|
|
|
1354
1408
|
flexDirection: "column",
|
|
1355
1409
|
justifyContent: "space-between",
|
|
1356
1410
|
children: [
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
/* @__PURE__ */
|
|
1411
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1412
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1413
|
+
/* @__PURE__ */ jsx11(Steps, {})
|
|
1360
1414
|
] }),
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__PURE__ */
|
|
1363
|
-
/* @__PURE__ */
|
|
1364
|
-
return /* @__PURE__ */
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
/* @__PURE__ */
|
|
1415
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1416
|
+
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1417
|
+
/* @__PURE__ */ jsx11(Box13, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1418
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
|
|
1419
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1420
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: c.description })
|
|
1367
1421
|
] });
|
|
1368
1422
|
}) })
|
|
1369
1423
|
] })
|
|
@@ -1373,12 +1427,12 @@ function Sidebar() {
|
|
|
1373
1427
|
}
|
|
1374
1428
|
|
|
1375
1429
|
// src/ui/Ribbon.tsx
|
|
1376
|
-
import { Box as
|
|
1377
|
-
import { jsx as
|
|
1430
|
+
import { Box as Box14, Text as Text14 } from "ink";
|
|
1431
|
+
import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1378
1432
|
function Ribbon() {
|
|
1379
1433
|
const firstCommand = sidebarCommands[0];
|
|
1380
|
-
return /* @__PURE__ */
|
|
1381
|
-
|
|
1434
|
+
return /* @__PURE__ */ jsxs13(
|
|
1435
|
+
Box14,
|
|
1382
1436
|
{
|
|
1383
1437
|
backgroundColor: "#14171E",
|
|
1384
1438
|
flexDirection: "row",
|
|
@@ -1386,11 +1440,11 @@ function Ribbon() {
|
|
|
1386
1440
|
paddingX: 2,
|
|
1387
1441
|
paddingY: 1,
|
|
1388
1442
|
children: [
|
|
1389
|
-
/* @__PURE__ */
|
|
1390
|
-
/* @__PURE__ */
|
|
1391
|
-
/* @__PURE__ */
|
|
1392
|
-
/* @__PURE__ */
|
|
1393
|
-
/* @__PURE__ */
|
|
1443
|
+
/* @__PURE__ */ jsx12(Progress, {}),
|
|
1444
|
+
/* @__PURE__ */ jsx12(CurrentStep, {}),
|
|
1445
|
+
/* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: 1, children: [
|
|
1446
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1447
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: firstCommand.description })
|
|
1394
1448
|
] })
|
|
1395
1449
|
]
|
|
1396
1450
|
}
|
|
@@ -1401,8 +1455,8 @@ function Ribbon() {
|
|
|
1401
1455
|
import { useState as useState6 } from "react";
|
|
1402
1456
|
|
|
1403
1457
|
// src/ui/Logs.tsx
|
|
1404
|
-
import { Box as
|
|
1405
|
-
import { jsx as
|
|
1458
|
+
import { Box as Box15, Text as Text15, useInput as useInput6 } from "ink";
|
|
1459
|
+
import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1406
1460
|
var KIND_COLOR = {
|
|
1407
1461
|
tool: COLORS.primary,
|
|
1408
1462
|
prompt: COLORS.badge
|
|
@@ -1433,14 +1487,14 @@ function formatTimestamp(ms) {
|
|
|
1433
1487
|
function Logs() {
|
|
1434
1488
|
const logs = useWizard((s) => s.logs);
|
|
1435
1489
|
const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
|
|
1436
|
-
|
|
1490
|
+
useInput6((_input, key) => {
|
|
1437
1491
|
if (key.upArrow) scroll.scrollBy(-1);
|
|
1438
1492
|
else if (key.downArrow) scroll.scrollBy(1);
|
|
1439
1493
|
});
|
|
1440
1494
|
const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
1441
|
-
return /* @__PURE__ */
|
|
1442
|
-
logs.length === 0 && /* @__PURE__ */
|
|
1443
|
-
/* @__PURE__ */
|
|
1495
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1496
|
+
logs.length === 0 && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "No logs yet." }),
|
|
1497
|
+
/* @__PURE__ */ jsx13(ScrollView, { scroll, children: visible.map((entry) => {
|
|
1444
1498
|
const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
|
|
1445
1499
|
const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
|
|
1446
1500
|
const rawPreview = rawInputText(entry.input);
|
|
@@ -1450,14 +1504,14 @@ function Logs() {
|
|
|
1450
1504
|
const name = truncate2(entry.name, budget);
|
|
1451
1505
|
budget -= name.length;
|
|
1452
1506
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1453
|
-
return /* @__PURE__ */
|
|
1454
|
-
/* @__PURE__ */
|
|
1455
|
-
/* @__PURE__ */
|
|
1456
|
-
preview && /* @__PURE__ */
|
|
1457
|
-
durationText && /* @__PURE__ */
|
|
1507
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1508
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: timestamp }),
|
|
1509
|
+
/* @__PURE__ */ jsx13(Text15, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1510
|
+
preview && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1511
|
+
durationText && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: durationText })
|
|
1458
1512
|
] }, entry.id);
|
|
1459
1513
|
}) }),
|
|
1460
|
-
/* @__PURE__ */
|
|
1514
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1461
1515
|
] });
|
|
1462
1516
|
}
|
|
1463
1517
|
|
|
@@ -1649,7 +1703,7 @@ function track(event, payload) {
|
|
|
1649
1703
|
}
|
|
1650
1704
|
|
|
1651
1705
|
// src/ui/App.tsx
|
|
1652
|
-
import { jsx as
|
|
1706
|
+
import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1653
1707
|
function App() {
|
|
1654
1708
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1655
1709
|
const { exit } = useApp();
|
|
@@ -1657,7 +1711,7 @@ function App() {
|
|
|
1657
1711
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1658
1712
|
const finished = phase === "done" || phase === "error";
|
|
1659
1713
|
const currentStep = steps[currentStepIndex];
|
|
1660
|
-
|
|
1714
|
+
useInput7(
|
|
1661
1715
|
(_input, key) => {
|
|
1662
1716
|
if (key.return) {
|
|
1663
1717
|
exit();
|
|
@@ -1665,7 +1719,7 @@ function App() {
|
|
|
1665
1719
|
},
|
|
1666
1720
|
{ isActive: finished }
|
|
1667
1721
|
);
|
|
1668
|
-
|
|
1722
|
+
useInput7((_input, key) => {
|
|
1669
1723
|
if (phase === "idle" || phase === "authenticating") return;
|
|
1670
1724
|
if (key.tab) {
|
|
1671
1725
|
setShowLogs(!showLogs);
|
|
@@ -1676,8 +1730,8 @@ function App() {
|
|
|
1676
1730
|
});
|
|
1677
1731
|
}
|
|
1678
1732
|
});
|
|
1679
|
-
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1680
|
-
|
|
1733
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
|
|
1734
|
+
useInput7((_input, key) => {
|
|
1681
1735
|
if (escOwnedElsewhere) return;
|
|
1682
1736
|
if (key.escape) {
|
|
1683
1737
|
track("AI Wizard Interaction", {
|
|
@@ -1696,8 +1750,8 @@ function App() {
|
|
|
1696
1750
|
/* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
|
|
1697
1751
|
the whole screen, and the scrolling throws off its cursor arithmetic —
|
|
1698
1752
|
flicker and leftover rows. */
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
|
|
1753
|
+
/* @__PURE__ */ jsxs15(
|
|
1754
|
+
Box16,
|
|
1701
1755
|
{
|
|
1702
1756
|
backgroundColor: COLORS.bg.main,
|
|
1703
1757
|
flexDirection: "row",
|
|
@@ -1705,16 +1759,16 @@ function App() {
|
|
|
1705
1759
|
height: scrollsPastViewport ? void 0 : rows,
|
|
1706
1760
|
overflow: scrollsPastViewport ? "visible" : "hidden",
|
|
1707
1761
|
children: [
|
|
1708
|
-
mainWindowVisible && /* @__PURE__ */
|
|
1709
|
-
|
|
1762
|
+
mainWindowVisible && /* @__PURE__ */ jsxs15(
|
|
1763
|
+
Box16,
|
|
1710
1764
|
{
|
|
1711
1765
|
flexDirection,
|
|
1712
1766
|
width: "100%",
|
|
1713
1767
|
maxHeight: rows,
|
|
1714
1768
|
justifyContent: "space-between",
|
|
1715
1769
|
children: [
|
|
1716
|
-
showLogs ? /* @__PURE__ */
|
|
1717
|
-
|
|
1770
|
+
showLogs ? /* @__PURE__ */ jsx14(Logs, {}) : /* @__PURE__ */ jsxs15(
|
|
1771
|
+
Box16,
|
|
1718
1772
|
{
|
|
1719
1773
|
flexDirection: "column",
|
|
1720
1774
|
paddingX: 4,
|
|
@@ -1722,26 +1776,26 @@ function App() {
|
|
|
1722
1776
|
width: showSidebar ? 70 : "100%",
|
|
1723
1777
|
flexGrow: 1,
|
|
1724
1778
|
children: [
|
|
1725
|
-
phase === "authenticating" && /* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
/* @__PURE__ */
|
|
1779
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
1780
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1781
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1728
1782
|
] }),
|
|
1729
|
-
/* @__PURE__ */
|
|
1730
|
-
/* @__PURE__ */
|
|
1731
|
-
/* @__PURE__ */
|
|
1732
|
-
phase === "running" && showSidebar && /* @__PURE__ */
|
|
1733
|
-
phase === "error" && error && /* @__PURE__ */
|
|
1783
|
+
/* @__PURE__ */ jsx14(CliOutput, {}),
|
|
1784
|
+
/* @__PURE__ */ jsx14(Notices, {}),
|
|
1785
|
+
/* @__PURE__ */ jsx14(PromptInput, {}),
|
|
1786
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx14(CurrentStep, {}) }),
|
|
1787
|
+
phase === "error" && error && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: COLORS.status.error, children: [
|
|
1734
1788
|
"\u2716 ",
|
|
1735
1789
|
error
|
|
1736
1790
|
] }) })
|
|
1737
1791
|
]
|
|
1738
1792
|
}
|
|
1739
1793
|
),
|
|
1740
|
-
showSidebar ? /* @__PURE__ */
|
|
1794
|
+
showSidebar ? /* @__PURE__ */ jsx14(Sidebar, {}) : /* @__PURE__ */ jsx14(Ribbon, {})
|
|
1741
1795
|
]
|
|
1742
1796
|
}
|
|
1743
1797
|
),
|
|
1744
|
-
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */
|
|
1798
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx14(LearnMore, {}) : /* @__PURE__ */ jsx14(Welcome, {}))
|
|
1745
1799
|
]
|
|
1746
1800
|
}
|
|
1747
1801
|
)
|
|
@@ -1821,7 +1875,7 @@ async function ensureConsent() {
|
|
|
1821
1875
|
if (config.aiConsent) return;
|
|
1822
1876
|
const store = useWizard.getState();
|
|
1823
1877
|
const answer = await store.requestUserInput({
|
|
1824
|
-
prompt: "Wizard will make AI-authored changes to this repository.",
|
|
1878
|
+
prompt: "Wizard will make AI-authored changes to this repository, and will propose shell commands to set it up. You approve each command before it runs.",
|
|
1825
1879
|
promptType: "enterToContinue",
|
|
1826
1880
|
options: []
|
|
1827
1881
|
});
|
|
@@ -2103,7 +2157,7 @@ async function ensureApplication() {
|
|
|
2103
2157
|
}
|
|
2104
2158
|
|
|
2105
2159
|
// src/workflows/default.ts
|
|
2106
|
-
import { z as
|
|
2160
|
+
import { z as z28 } from "zod";
|
|
2107
2161
|
|
|
2108
2162
|
// src/actions/listIndices.ts
|
|
2109
2163
|
import { z as z5 } from "zod";
|
|
@@ -2363,22 +2417,48 @@ function writeFileTool(ctx) {
|
|
|
2363
2417
|
|
|
2364
2418
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2365
2419
|
import { tool as tool6 } from "ai";
|
|
2366
|
-
import
|
|
2420
|
+
import z13 from "zod";
|
|
2367
2421
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2368
2422
|
import { dirname as dirname5 } from "node:path";
|
|
2369
2423
|
|
|
2370
2424
|
// src/lib/algoliaApiKey.ts
|
|
2371
|
-
import { z as
|
|
2425
|
+
import { z as z12 } from "zod";
|
|
2372
2426
|
|
|
2373
2427
|
// src/lib/keychain.ts
|
|
2374
|
-
import { getPassword, setPassword } from "cross-keychain";
|
|
2428
|
+
import { deletePassword, getPassword, setPassword } from "cross-keychain";
|
|
2429
|
+
import { z as z11 } from "zod";
|
|
2375
2430
|
var SERVICE = "algolia-wizard";
|
|
2376
|
-
|
|
2431
|
+
var ACCOUNT = "api-keys";
|
|
2432
|
+
var storedKeysSchema = z11.record(z11.string(), z11.string());
|
|
2433
|
+
function entryId(kind, index, appId) {
|
|
2377
2434
|
return `${kind}:${appId}:${index}`;
|
|
2378
2435
|
}
|
|
2436
|
+
async function loadKeys() {
|
|
2437
|
+
const raw = await getPassword(SERVICE, ACCOUNT);
|
|
2438
|
+
if (!raw) return {};
|
|
2439
|
+
let payload;
|
|
2440
|
+
try {
|
|
2441
|
+
payload = JSON.parse(raw);
|
|
2442
|
+
} catch {
|
|
2443
|
+
payload = null;
|
|
2444
|
+
}
|
|
2445
|
+
const keys = storedKeysSchema.safeParse(payload);
|
|
2446
|
+
if (!keys.success) {
|
|
2447
|
+
logger.warn("the stored API keys are unreadable; treating them as empty");
|
|
2448
|
+
return {};
|
|
2449
|
+
}
|
|
2450
|
+
return keys.data;
|
|
2451
|
+
}
|
|
2452
|
+
var queue = Promise.resolve();
|
|
2453
|
+
function serialized(op) {
|
|
2454
|
+
const next = queue.then(op);
|
|
2455
|
+
queue = next.catch(() => {
|
|
2456
|
+
});
|
|
2457
|
+
return next;
|
|
2458
|
+
}
|
|
2379
2459
|
async function readStoredKey(kind, index, appId) {
|
|
2380
2460
|
try {
|
|
2381
|
-
return await
|
|
2461
|
+
return (await loadKeys())[entryId(kind, index, appId)] ?? null;
|
|
2382
2462
|
} catch (err) {
|
|
2383
2463
|
logger.warn(
|
|
2384
2464
|
{ err: err.message, kind, index, appId },
|
|
@@ -2387,19 +2467,40 @@ async function readStoredKey(kind, index, appId) {
|
|
|
2387
2467
|
return null;
|
|
2388
2468
|
}
|
|
2389
2469
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2470
|
+
function storeKey(kind, index, appId, value) {
|
|
2471
|
+
return serialized(async () => {
|
|
2472
|
+
const id = entryId(kind, index, appId);
|
|
2473
|
+
try {
|
|
2474
|
+
const keys = await loadKeys();
|
|
2475
|
+
await setPassword(
|
|
2476
|
+
SERVICE,
|
|
2477
|
+
ACCOUNT,
|
|
2478
|
+
JSON.stringify({ ...keys, [id]: value })
|
|
2479
|
+
);
|
|
2480
|
+
if ((await loadKeys())[id] !== value) {
|
|
2481
|
+
throw new Error("the keychain did not store the value");
|
|
2482
|
+
}
|
|
2483
|
+
} catch (err) {
|
|
2484
|
+
logger.warn(
|
|
2485
|
+
{ err: err.message, kind, index, appId },
|
|
2486
|
+
"could not store the API key in the keychain; the next run will create another"
|
|
2487
|
+
);
|
|
2396
2488
|
}
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
function deleteStoredKeys() {
|
|
2492
|
+
return serialized(async () => {
|
|
2493
|
+
try {
|
|
2494
|
+
await deletePassword(SERVICE, ACCOUNT);
|
|
2495
|
+
} catch (err) {
|
|
2496
|
+
const message = err.message;
|
|
2497
|
+
if (/not found/i.test(message)) return;
|
|
2498
|
+
logger.warn(
|
|
2499
|
+
{ err: message },
|
|
2500
|
+
"could not delete the API keys from the keychain"
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
});
|
|
2403
2504
|
}
|
|
2404
2505
|
|
|
2405
2506
|
// src/lib/algoliaApiKey.ts
|
|
@@ -2410,27 +2511,24 @@ var WRITE_ACLS = [
|
|
|
2410
2511
|
"editSettings",
|
|
2411
2512
|
"listIndexes"
|
|
2412
2513
|
];
|
|
2413
|
-
var createdKeySchema =
|
|
2414
|
-
key:
|
|
2415
|
-
value:
|
|
2514
|
+
var createdKeySchema = z12.object({
|
|
2515
|
+
key: z12.string().min(1).optional(),
|
|
2516
|
+
value: z12.string().min(1).optional()
|
|
2416
2517
|
}).transform((o) => o.key ?? o.value);
|
|
2417
2518
|
async function createKey(index, acls, description) {
|
|
2418
2519
|
logger.info({ index, acls }, "creating an API key");
|
|
2419
|
-
const stdout = await runAlgoliaCli(
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
],
|
|
2432
|
-
{ withoutAdminKey: true }
|
|
2433
|
-
);
|
|
2520
|
+
const stdout = await runAlgoliaCli([
|
|
2521
|
+
"apikeys",
|
|
2522
|
+
"create",
|
|
2523
|
+
"--acl",
|
|
2524
|
+
acls.join(","),
|
|
2525
|
+
"--indices",
|
|
2526
|
+
index,
|
|
2527
|
+
"--description",
|
|
2528
|
+
description,
|
|
2529
|
+
"-o",
|
|
2530
|
+
"json"
|
|
2531
|
+
]);
|
|
2434
2532
|
let payload;
|
|
2435
2533
|
try {
|
|
2436
2534
|
payload = JSON.parse(stdout);
|
|
@@ -2441,9 +2539,18 @@ async function createKey(index, acls, description) {
|
|
|
2441
2539
|
if (!created) throw new Error("apikeys create returned no key value");
|
|
2442
2540
|
return created;
|
|
2443
2541
|
}
|
|
2542
|
+
async function keyExists(key) {
|
|
2543
|
+
try {
|
|
2544
|
+
await runAlgoliaCli(["apikeys", "get", key, "-o", "json"], { redact: key });
|
|
2545
|
+
return true;
|
|
2546
|
+
} catch (err) {
|
|
2547
|
+
return !/does not exist/i.test(err.message);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2444
2550
|
var resolved = /* @__PURE__ */ new Map();
|
|
2445
|
-
function forgetResolvedKeys() {
|
|
2551
|
+
async function forgetResolvedKeys() {
|
|
2446
2552
|
resolved.clear();
|
|
2553
|
+
await deleteStoredKeys();
|
|
2447
2554
|
}
|
|
2448
2555
|
function resolveKey(kind, index, appId, acls, description) {
|
|
2449
2556
|
const cacheKey = `${kind}:${appId}:${index}`;
|
|
@@ -2461,8 +2568,14 @@ function resolveKey(kind, index, appId, acls, description) {
|
|
|
2461
2568
|
async function provisionKey(kind, index, appId, acls, description) {
|
|
2462
2569
|
const stored = await readStoredKey(kind, index, appId);
|
|
2463
2570
|
if (stored) {
|
|
2464
|
-
|
|
2465
|
-
|
|
2571
|
+
if (await keyExists(stored)) {
|
|
2572
|
+
logger.info({ kind, index, appId }, "reusing the stored API key");
|
|
2573
|
+
return { key: stored, source: "keychain" };
|
|
2574
|
+
}
|
|
2575
|
+
logger.info(
|
|
2576
|
+
{ kind, index, appId },
|
|
2577
|
+
"the stored API key no longer exists; creating another"
|
|
2578
|
+
);
|
|
2466
2579
|
}
|
|
2467
2580
|
const key = await createKey(index, acls, description);
|
|
2468
2581
|
await storeKey(kind, index, appId, key);
|
|
@@ -2511,8 +2624,8 @@ function upsertEnv(content, name, value) {
|
|
|
2511
2624
|
function writeCredentialsTool(ctx) {
|
|
2512
2625
|
return tool6({
|
|
2513
2626
|
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) and the target index name (${INDEX_NAME_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file (e.g. ".env"). Any name the file already defines is left untouched.`,
|
|
2514
|
-
inputSchema:
|
|
2515
|
-
filePath:
|
|
2627
|
+
inputSchema: z13.object({
|
|
2628
|
+
filePath: z13.string().describe(
|
|
2516
2629
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
2517
2630
|
)
|
|
2518
2631
|
}),
|
|
@@ -2572,15 +2685,23 @@ function writeCredentialsTool(ctx) {
|
|
|
2572
2685
|
|
|
2573
2686
|
// src/lib/tools/searchFiles.ts
|
|
2574
2687
|
import { tool as tool7 } from "ai";
|
|
2575
|
-
import
|
|
2688
|
+
import z14 from "zod";
|
|
2576
2689
|
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2577
2690
|
import { join as join7 } from "node:path";
|
|
2578
2691
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2692
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2693
|
+
"node_modules",
|
|
2694
|
+
"dist",
|
|
2695
|
+
"build",
|
|
2696
|
+
"vendor",
|
|
2697
|
+
"venv",
|
|
2698
|
+
"__pycache__",
|
|
2699
|
+
"target"
|
|
2700
|
+
]);
|
|
2579
2701
|
async function walkFiles(dir) {
|
|
2580
|
-
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2581
2702
|
const out = [];
|
|
2582
2703
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2583
|
-
if (e.name.startsWith(".") ||
|
|
2704
|
+
if (e.name.startsWith(".") || SKIP_DIRS.has(e.name)) continue;
|
|
2584
2705
|
const full = join7(dir, e.name);
|
|
2585
2706
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2586
2707
|
else if (e.isFile()) out.push(full);
|
|
@@ -2590,9 +2711,9 @@ async function walkFiles(dir) {
|
|
|
2590
2711
|
function searchFilesTool(ctx) {
|
|
2591
2712
|
return tool7({
|
|
2592
2713
|
description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
|
|
2593
|
-
inputSchema:
|
|
2594
|
-
query:
|
|
2595
|
-
path:
|
|
2714
|
+
inputSchema: z14.object({
|
|
2715
|
+
query: z14.string().describe("JavaScript RegExp pattern to search for"),
|
|
2716
|
+
path: z14.string().optional().describe("Directory to search in (default: cwd)")
|
|
2596
2717
|
}),
|
|
2597
2718
|
execute: async ({ query, path = "." }) => {
|
|
2598
2719
|
logger.info({ query, path }, "called searchFiles tool");
|
|
@@ -2634,92 +2755,188 @@ function searchFilesTool(ctx) {
|
|
|
2634
2755
|
});
|
|
2635
2756
|
}
|
|
2636
2757
|
|
|
2637
|
-
// src/lib/tools/
|
|
2758
|
+
// src/lib/tools/runShell.ts
|
|
2638
2759
|
import { tool as tool8 } from "ai";
|
|
2639
|
-
import
|
|
2760
|
+
import z15 from "zod";
|
|
2761
|
+
import { relative as relative2 } from "node:path";
|
|
2640
2762
|
|
|
2641
|
-
// src/lib/tools/utils/
|
|
2763
|
+
// src/lib/tools/utils/runShell.ts
|
|
2642
2764
|
import { spawn as spawn2 } from "node:child_process";
|
|
2643
|
-
|
|
2765
|
+
|
|
2766
|
+
// src/lib/tools/context.ts
|
|
2767
|
+
var DEFAULT_TOOL_LIMITS = {
|
|
2768
|
+
list: 10,
|
|
2769
|
+
search: 10,
|
|
2770
|
+
read: 20,
|
|
2771
|
+
match: 100,
|
|
2772
|
+
shell: 30
|
|
2773
|
+
};
|
|
2774
|
+
var DEFAULT_SHELL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2775
|
+
async function refuseByDefault() {
|
|
2776
|
+
return "reject";
|
|
2777
|
+
}
|
|
2778
|
+
function createShellContext(overrides = {}) {
|
|
2779
|
+
return {
|
|
2780
|
+
env: async () => ({}),
|
|
2781
|
+
timeoutMs: DEFAULT_SHELL_TIMEOUT_MS,
|
|
2782
|
+
executions: [],
|
|
2783
|
+
approve: refuseByDefault,
|
|
2784
|
+
...overrides
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2787
|
+
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd(), shell2 = createShellContext()) {
|
|
2788
|
+
return {
|
|
2789
|
+
root: cwd,
|
|
2790
|
+
cwd,
|
|
2791
|
+
limits: { ...limits },
|
|
2792
|
+
counts: { list: 0, search: 0, read: 0, shell: 0 },
|
|
2793
|
+
shell: shell2
|
|
2794
|
+
};
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
// src/lib/tools/utils/runShell.ts
|
|
2798
|
+
var SIGKILL_DELAY_MS = 5e3;
|
|
2799
|
+
var HEAD_CHARS = 4e3;
|
|
2800
|
+
var TAIL_CHARS = 8e3;
|
|
2801
|
+
function truncateOutput(output) {
|
|
2802
|
+
if (output.length <= HEAD_CHARS + TAIL_CHARS) return output;
|
|
2803
|
+
const omitted = output.length - HEAD_CHARS - TAIL_CHARS;
|
|
2804
|
+
return [
|
|
2805
|
+
output.slice(0, HEAD_CHARS),
|
|
2806
|
+
`
|
|
2807
|
+
\u2026 [${omitted} characters omitted] \u2026
|
|
2808
|
+
`,
|
|
2809
|
+
output.slice(-TAIL_CHARS)
|
|
2810
|
+
].join("");
|
|
2811
|
+
}
|
|
2812
|
+
function runShell(command, opts) {
|
|
2813
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
|
|
2814
|
+
const startedAt = Date.now();
|
|
2644
2815
|
return new Promise((resolve4) => {
|
|
2645
2816
|
let output = "";
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2817
|
+
let timedOut = false;
|
|
2818
|
+
let settled = false;
|
|
2819
|
+
const child = spawn2(command, {
|
|
2820
|
+
shell: true,
|
|
2821
|
+
cwd: opts.cwd,
|
|
2822
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2823
|
+
env: { ...process.env, ...opts.env }
|
|
2649
2824
|
});
|
|
2825
|
+
const finish = (exitCode) => {
|
|
2826
|
+
if (settled) return;
|
|
2827
|
+
settled = true;
|
|
2828
|
+
clearTimeout(timer);
|
|
2829
|
+
clearTimeout(killTimer);
|
|
2830
|
+
resolve4({
|
|
2831
|
+
exitCode,
|
|
2832
|
+
output: truncateOutput(output.trim()),
|
|
2833
|
+
timedOut,
|
|
2834
|
+
durationMs: Date.now() - startedAt
|
|
2835
|
+
});
|
|
2836
|
+
};
|
|
2837
|
+
let killTimer;
|
|
2838
|
+
const timer = setTimeout(() => {
|
|
2839
|
+
timedOut = true;
|
|
2840
|
+
output += `
|
|
2841
|
+
[timed out after ${timeoutMs}ms]`;
|
|
2842
|
+
child.kill("SIGTERM");
|
|
2843
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), SIGKILL_DELAY_MS);
|
|
2844
|
+
}, timeoutMs);
|
|
2650
2845
|
child.stdout?.on("data", (d) => output += d);
|
|
2651
2846
|
child.stderr?.on("data", (d) => output += d);
|
|
2652
|
-
child.on(
|
|
2653
|
-
|
|
2654
|
-
(
|
|
2655
|
-
);
|
|
2656
|
-
child.on("close", (code) =>
|
|
2847
|
+
child.on("error", (err) => {
|
|
2848
|
+
output += `Failed to run ${command}: ${err.message}`;
|
|
2849
|
+
finish(1);
|
|
2850
|
+
});
|
|
2851
|
+
child.on("close", (code) => finish(code ?? 1));
|
|
2657
2852
|
});
|
|
2658
2853
|
}
|
|
2659
2854
|
|
|
2660
|
-
// src/lib/tools/
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2676
|
-
}
|
|
2677
|
-
function packageManagerFromLockfile(cwd) {
|
|
2678
|
-
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2679
|
-
}
|
|
2680
|
-
async function detectPackageManager(cwd) {
|
|
2681
|
-
try {
|
|
2682
|
-
const pkg = await readPackageJson(cwd);
|
|
2683
|
-
if (pkg.packageManager) return packageManagerFrom(pkg);
|
|
2684
|
-
} catch {
|
|
2685
|
-
}
|
|
2686
|
-
return packageManagerFromLockfile(cwd) ?? "npm";
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
// src/lib/tools/repoVerification.ts
|
|
2690
|
-
var VERIFICATION_SCRIPT_CANDIDATES = ["lint", "typecheck", "check"];
|
|
2691
|
-
async function runRepoVerificationCheck() {
|
|
2692
|
-
let pkg;
|
|
2693
|
-
try {
|
|
2694
|
-
pkg = await readPackageJson();
|
|
2695
|
-
} catch (err) {
|
|
2696
|
-
const limitation = `Could not read package.json to detect verification conventions: ${err.message}`;
|
|
2697
|
-
return { ok: false, checks: [], limitation };
|
|
2698
|
-
}
|
|
2699
|
-
const scripts = pkg.scripts ?? {};
|
|
2700
|
-
const present = VERIFICATION_SCRIPT_CANDIDATES.filter((s) => s in scripts);
|
|
2701
|
-
if (present.length === 0) {
|
|
2702
|
-
const limitation = `No verification script found in package.json (looked for: ${VERIFICATION_SCRIPT_CANDIDATES.join(", ")}).`;
|
|
2703
|
-
return { ok: false, checks: [], limitation };
|
|
2704
|
-
}
|
|
2705
|
-
const pm = await detectPackageManager(process.cwd());
|
|
2706
|
-
const checks = [];
|
|
2707
|
-
for (const script of present) {
|
|
2708
|
-
const command = `${pm} run ${script}`;
|
|
2709
|
-
const { code, output } = await runCommand(pm, ["run", script]);
|
|
2710
|
-
checks.push({ command, exitCode: code, ok: code === 0, output: output.trim() });
|
|
2711
|
-
}
|
|
2712
|
-
return { ok: checks.every((c) => c.ok), checks };
|
|
2855
|
+
// src/lib/tools/runShell.ts
|
|
2856
|
+
function storeApproval(root) {
|
|
2857
|
+
return async (req) => {
|
|
2858
|
+
const rel = relative2(root, req.cwd);
|
|
2859
|
+
const answer = await useWizard.getState().requestUserInput({
|
|
2860
|
+
prompt: "Run this command?",
|
|
2861
|
+
promptType: "commandApproval",
|
|
2862
|
+
options: [],
|
|
2863
|
+
command: {
|
|
2864
|
+
...req,
|
|
2865
|
+
cwd: rel === "" || rel.startsWith("..") ? req.cwd : rel
|
|
2866
|
+
}
|
|
2867
|
+
});
|
|
2868
|
+
return answer === "approve" ? "approve" : "reject";
|
|
2869
|
+
};
|
|
2713
2870
|
}
|
|
2714
|
-
|
|
2715
|
-
// src/lib/tools/verifyImplementation.ts
|
|
2716
|
-
function verifyImplementationTool() {
|
|
2871
|
+
function runShellTool(ctx) {
|
|
2717
2872
|
return tool8({
|
|
2718
|
-
description: "Run the
|
|
2719
|
-
inputSchema:
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2873
|
+
description: "Run a shell command in the project. Use this for anything the project needs done in its own ecosystem: installing dependencies, running a script you wrote, running the project's lint/typecheck/test commands. The user sees and approves every command before it runs, so write a clear `explanation`. If the user rejects a command, do not retry it \u2014 propose a different approach.",
|
|
2874
|
+
inputSchema: z15.object({
|
|
2875
|
+
command: z15.string().describe(
|
|
2876
|
+
"The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
|
|
2877
|
+
),
|
|
2878
|
+
cwd: z15.string().optional().describe(
|
|
2879
|
+
"Directory to run in, relative to the project root. Defaults to the project root."
|
|
2880
|
+
),
|
|
2881
|
+
explanation: z15.string().describe(
|
|
2882
|
+
"One short line telling the user what this command does and why, including any side effect (e.g. writes records to Algolia). This is what they approve against."
|
|
2883
|
+
)
|
|
2884
|
+
}),
|
|
2885
|
+
execute: async ({ command, cwd, explanation }) => {
|
|
2886
|
+
if (++ctx.counts.shell > ctx.limits.shell) {
|
|
2887
|
+
return `Refused: command limit (${ctx.limits.shell}) reached. Stop running commands and report what you have.`;
|
|
2888
|
+
}
|
|
2889
|
+
const resolved2 = resolveInRoot(ctx, cwd ?? ".");
|
|
2890
|
+
if (!resolved2.ok) return resolved2.error;
|
|
2891
|
+
logger.info({ command, cwd: resolved2.target }, "called runShell tool");
|
|
2892
|
+
const decision = await ctx.shell.approve({
|
|
2893
|
+
command,
|
|
2894
|
+
cwd: resolved2.target,
|
|
2895
|
+
explanation
|
|
2896
|
+
});
|
|
2897
|
+
if (decision === "reject") {
|
|
2898
|
+
ctx.shell.executions.push({
|
|
2899
|
+
command,
|
|
2900
|
+
cwd: resolved2.target,
|
|
2901
|
+
approved: false
|
|
2902
|
+
});
|
|
2903
|
+
logger.info({ command }, "runShell: user rejected the command");
|
|
2904
|
+
return "The user rejected this command. Do not retry it. Propose a different command, or report the limitation via reportStatus.";
|
|
2905
|
+
}
|
|
2906
|
+
useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
|
|
2907
|
+
const env = await ctx.shell.env().catch((err) => {
|
|
2908
|
+
logger.warn({ err, command }, "runShell: could not resolve command env");
|
|
2909
|
+
return {};
|
|
2910
|
+
});
|
|
2911
|
+
const run2 = await (ctx.shell.run ?? runShell)(command, {
|
|
2912
|
+
cwd: resolved2.target,
|
|
2913
|
+
env,
|
|
2914
|
+
timeoutMs: ctx.shell.timeoutMs
|
|
2915
|
+
});
|
|
2916
|
+
logger.info(
|
|
2917
|
+
{
|
|
2918
|
+
command,
|
|
2919
|
+
exitCode: run2.exitCode,
|
|
2920
|
+
timedOut: run2.timedOut,
|
|
2921
|
+
durationMs: run2.durationMs
|
|
2922
|
+
},
|
|
2923
|
+
"runShell finished"
|
|
2924
|
+
);
|
|
2925
|
+
await markInteraction();
|
|
2926
|
+
ctx.shell.executions.push({
|
|
2927
|
+
command,
|
|
2928
|
+
cwd: resolved2.target,
|
|
2929
|
+
approved: true,
|
|
2930
|
+
exitCode: run2.exitCode,
|
|
2931
|
+
output: run2.output,
|
|
2932
|
+
timedOut: run2.timedOut,
|
|
2933
|
+
durationMs: run2.durationMs
|
|
2934
|
+
});
|
|
2935
|
+
return {
|
|
2936
|
+
exitCode: run2.exitCode,
|
|
2937
|
+
timedOut: run2.timedOut,
|
|
2938
|
+
output: run2.output
|
|
2939
|
+
};
|
|
2723
2940
|
}
|
|
2724
2941
|
});
|
|
2725
2942
|
}
|
|
@@ -2730,7 +2947,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
2730
2947
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2731
2948
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2732
2949
|
import { dirname as dirname6 } from "node:path";
|
|
2733
|
-
import
|
|
2950
|
+
import z16 from "zod";
|
|
2734
2951
|
var DATA_DIR = ".algolia-wizard/data";
|
|
2735
2952
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
2736
2953
|
var MAX_RECORDS = 100;
|
|
@@ -2742,17 +2959,17 @@ var anthropic = createAnthropic({
|
|
|
2742
2959
|
function generateRecordTool(ctx) {
|
|
2743
2960
|
return tool9({
|
|
2744
2961
|
description: "Generate realistic sample records for an entity and write them to a JSON file in the worktree. Provide the entity name and its attributes; this tool asks a model to invent varied, realistic values, each with a unique objectID, and returns the file path to read them from at runtime. Do not invent the record values or objectIDs yourself, and do not inline the returned records into the script \u2014 call this tool and read the file it writes.",
|
|
2745
|
-
inputSchema:
|
|
2746
|
-
entityName:
|
|
2747
|
-
attributes:
|
|
2748
|
-
count:
|
|
2749
|
-
hint:
|
|
2962
|
+
inputSchema: z16.object({
|
|
2963
|
+
entityName: z16.string().describe("Name of the entity to generate records for."),
|
|
2964
|
+
attributes: z16.array(z16.string()).describe("Attribute names each record must contain."),
|
|
2965
|
+
count: z16.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
|
|
2966
|
+
hint: z16.string().optional().describe("Optional context to steer realistic values.")
|
|
2750
2967
|
}),
|
|
2751
2968
|
execute: async ({ entityName, attributes, count, hint }) => {
|
|
2752
2969
|
logger.info({ entityName, count }, "called generateRecord tool");
|
|
2753
2970
|
try {
|
|
2754
|
-
const value =
|
|
2755
|
-
const recordSchema =
|
|
2971
|
+
const value = z16.union([z16.string(), z16.number(), z16.boolean(), z16.null()]);
|
|
2972
|
+
const recordSchema = z16.object(
|
|
2756
2973
|
Object.fromEntries(attributes.map((attr) => [attr, value]))
|
|
2757
2974
|
);
|
|
2758
2975
|
const generateBatch = async (batchCount) => {
|
|
@@ -2762,8 +2979,8 @@ function generateRecordTool(ctx) {
|
|
|
2762
2979
|
const { output } = await generateText({
|
|
2763
2980
|
model: anthropic(RECORD_MODEL),
|
|
2764
2981
|
output: Output.object({
|
|
2765
|
-
schema:
|
|
2766
|
-
records:
|
|
2982
|
+
schema: z16.object({
|
|
2983
|
+
records: z16.array(recordSchema).length(batchCount)
|
|
2767
2984
|
})
|
|
2768
2985
|
}),
|
|
2769
2986
|
prompt: [
|
|
@@ -2810,7 +3027,7 @@ function generateRecordTool(ctx) {
|
|
|
2810
3027
|
return {
|
|
2811
3028
|
filePath: relPath,
|
|
2812
3029
|
count: records.length,
|
|
2813
|
-
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime
|
|
3030
|
+
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime using your language's standard JSON support \u2014 do not inline the records as literals.`
|
|
2814
3031
|
};
|
|
2815
3032
|
} catch (err) {
|
|
2816
3033
|
return `Error generating records: ${err.message}`;
|
|
@@ -2821,12 +3038,12 @@ function generateRecordTool(ctx) {
|
|
|
2821
3038
|
|
|
2822
3039
|
// src/lib/tools/notifyUser.ts
|
|
2823
3040
|
import { tool as tool10 } from "ai";
|
|
2824
|
-
import
|
|
3041
|
+
import z17 from "zod";
|
|
2825
3042
|
function notifyUserTool() {
|
|
2826
3043
|
return tool10({
|
|
2827
3044
|
description: `Give the user a brief, high-level update on what you are currently doing or about to do next. This is for the big picture (e.g. "Reading through your data models", "Writing the search UI") \u2014 not granular detail like individual tool calls, which are already logged separately. Call it when you start a new phase of work or your focus shifts, just not on every step, enough to keep the user engaged. Don't say things like "starting", just describe what you are doing. Don't mention tool calls themselves, just general direction of the work.`,
|
|
2828
|
-
inputSchema:
|
|
2829
|
-
message:
|
|
3045
|
+
inputSchema: z17.object({
|
|
3046
|
+
message: z17.string().describe(
|
|
2830
3047
|
"Short, plain-language description of what you are doing now."
|
|
2831
3048
|
)
|
|
2832
3049
|
}),
|
|
@@ -2838,22 +3055,6 @@ function notifyUserTool() {
|
|
|
2838
3055
|
});
|
|
2839
3056
|
}
|
|
2840
3057
|
|
|
2841
|
-
// src/lib/tools/context.ts
|
|
2842
|
-
var DEFAULT_TOOL_LIMITS = {
|
|
2843
|
-
list: 10,
|
|
2844
|
-
search: 10,
|
|
2845
|
-
read: 20,
|
|
2846
|
-
match: 100
|
|
2847
|
-
};
|
|
2848
|
-
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd()) {
|
|
2849
|
-
return {
|
|
2850
|
-
root: cwd,
|
|
2851
|
-
cwd,
|
|
2852
|
-
limits,
|
|
2853
|
-
counts: { list: 0, search: 0, read: 0 }
|
|
2854
|
-
};
|
|
2855
|
-
}
|
|
2856
|
-
|
|
2857
3058
|
// src/lib/tools/index.ts
|
|
2858
3059
|
function withLogging(name, def) {
|
|
2859
3060
|
const execute = def.execute;
|
|
@@ -2885,10 +3086,7 @@ function createTools(ctx, { output, tools }) {
|
|
|
2885
3086
|
writeCredentialsTool(ctx)
|
|
2886
3087
|
),
|
|
2887
3088
|
searchFiles: withLogging("searchFiles", searchFilesTool(ctx)),
|
|
2888
|
-
|
|
2889
|
-
"verifyImplementation",
|
|
2890
|
-
verifyImplementationTool()
|
|
2891
|
-
),
|
|
3089
|
+
runShell: withLogging("runShell", runShellTool(ctx)),
|
|
2892
3090
|
generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
|
|
2893
3091
|
notifyUser: withLogging("notifyUser", notifyUserTool())
|
|
2894
3092
|
};
|
|
@@ -2923,7 +3121,7 @@ async function runAgent(req) {
|
|
|
2923
3121
|
baseURL: PROXY_BASE_URL,
|
|
2924
3122
|
fetch: proxyFetch
|
|
2925
3123
|
});
|
|
2926
|
-
const toolContext = createToolContext();
|
|
3124
|
+
const toolContext = req.toolContext ?? createToolContext();
|
|
2927
3125
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
2928
3126
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
2929
3127
|
const instructions = [
|
|
@@ -2988,7 +3186,11 @@ async function runAgent(req) {
|
|
|
2988
3186
|
"runAgent finished"
|
|
2989
3187
|
);
|
|
2990
3188
|
logger.info(
|
|
2991
|
-
{
|
|
3189
|
+
{
|
|
3190
|
+
counts: toolContext.counts,
|
|
3191
|
+
limits: toolContext.limits,
|
|
3192
|
+
commandsRun: toolContext.shell.executions.length
|
|
3193
|
+
},
|
|
2992
3194
|
"tool usage"
|
|
2993
3195
|
);
|
|
2994
3196
|
const toolResults = await stream.toolResults;
|
|
@@ -3006,16 +3208,16 @@ async function runAgent(req) {
|
|
|
3006
3208
|
}
|
|
3007
3209
|
|
|
3008
3210
|
// src/actions/detectLanguage.ts
|
|
3009
|
-
import
|
|
3010
|
-
var detectLanguageSchema =
|
|
3011
|
-
languages:
|
|
3012
|
-
frameworks:
|
|
3211
|
+
import z20 from "zod";
|
|
3212
|
+
var detectLanguageSchema = z20.object({
|
|
3213
|
+
languages: z20.array(z20.object({ name: z20.string(), version: z20.string() })),
|
|
3214
|
+
frameworks: z20.array(z20.object({ name: z20.string(), version: z20.string() }))
|
|
3013
3215
|
});
|
|
3014
3216
|
var detectLanguage = () => runAgent({
|
|
3015
3217
|
instructions: [
|
|
3016
3218
|
"Analyze the codebase and determine the programming languages and frameworks used",
|
|
3017
|
-
"If a superset language is found, exclude the subset language.
|
|
3018
|
-
"If a meta-framework is used, exclude the framework. Next
|
|
3219
|
+
"If a superset language is found, exclude the subset language (e.g. TypeScript over JavaScript).",
|
|
3220
|
+
"If a meta-framework is used, exclude the framework it builds on (e.g. Next.js over React, Rails over Rack).",
|
|
3019
3221
|
"Return the exact version",
|
|
3020
3222
|
"Exclude things like CSS frameworks, build tools, or testing frameworks",
|
|
3021
3223
|
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
@@ -3027,31 +3229,31 @@ var detectLanguage = () => runAgent({
|
|
|
3027
3229
|
});
|
|
3028
3230
|
|
|
3029
3231
|
// src/actions/analyzeCodebase.ts
|
|
3030
|
-
import
|
|
3232
|
+
import z21 from "zod";
|
|
3031
3233
|
var READONLY_TOOLS = [
|
|
3032
3234
|
"listFiles",
|
|
3033
3235
|
"changeDirectory",
|
|
3034
3236
|
"readFile",
|
|
3035
3237
|
"searchFiles"
|
|
3036
3238
|
];
|
|
3037
|
-
var ingestionAnalysisSchema =
|
|
3038
|
-
ingestionAnalysis:
|
|
3039
|
-
|
|
3040
|
-
name:
|
|
3041
|
-
paths:
|
|
3239
|
+
var ingestionAnalysisSchema = z21.object({
|
|
3240
|
+
ingestionAnalysis: z21.array(
|
|
3241
|
+
z21.object({
|
|
3242
|
+
name: z21.string(),
|
|
3243
|
+
paths: z21.array(z21.string()),
|
|
3042
3244
|
// indexable fields the agent found for this entity
|
|
3043
|
-
attributes:
|
|
3245
|
+
attributes: z21.array(z21.string())
|
|
3044
3246
|
})
|
|
3045
3247
|
)
|
|
3046
3248
|
});
|
|
3047
|
-
var searchImplementationAnalysisSchema =
|
|
3048
|
-
searchImplementationAnalysis:
|
|
3249
|
+
var searchImplementationAnalysisSchema = z21.object({
|
|
3250
|
+
searchImplementationAnalysis: z21.string()
|
|
3049
3251
|
});
|
|
3050
|
-
var verificationSchema =
|
|
3051
|
-
verification:
|
|
3252
|
+
var verificationSchema = z21.object({
|
|
3253
|
+
verification: z21.array(z21.string())
|
|
3052
3254
|
});
|
|
3053
3255
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
3054
|
-
var analyzeCodebaseSchema =
|
|
3256
|
+
var analyzeCodebaseSchema = z21.object({
|
|
3055
3257
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3056
3258
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
3057
3259
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -3113,7 +3315,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3113
3315
|
// package.json
|
|
3114
3316
|
var package_default = {
|
|
3115
3317
|
name: "@algolia/wizard",
|
|
3116
|
-
version: "0.9.0-rc.
|
|
3318
|
+
version: "0.9.0-rc.93.108",
|
|
3117
3319
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3118
3320
|
type: "module",
|
|
3119
3321
|
engines: {
|
|
@@ -3232,8 +3434,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
3232
3434
|
}
|
|
3233
3435
|
|
|
3234
3436
|
// src/actions/confirmLanguage.ts
|
|
3235
|
-
import
|
|
3236
|
-
var confirmLanguageSchema =
|
|
3437
|
+
import z23 from "zod";
|
|
3438
|
+
var confirmLanguageSchema = z23.object({
|
|
3237
3439
|
languages: detectLanguageSchema.shape.languages
|
|
3238
3440
|
});
|
|
3239
3441
|
async function confirmLanguage(ctx) {
|
|
@@ -3254,17 +3456,19 @@ async function confirmLanguage(ctx) {
|
|
|
3254
3456
|
}
|
|
3255
3457
|
|
|
3256
3458
|
// src/actions/confirmFramework.ts
|
|
3257
|
-
import
|
|
3258
|
-
var confirmFrameworkSchema =
|
|
3459
|
+
import z24 from "zod";
|
|
3460
|
+
var confirmFrameworkSchema = z24.object({
|
|
3259
3461
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
3260
3462
|
});
|
|
3261
3463
|
var CURATED_FRAMEWORKS = [
|
|
3262
3464
|
"Next.js",
|
|
3263
3465
|
"React",
|
|
3264
3466
|
"Vue",
|
|
3265
|
-
"
|
|
3266
|
-
"
|
|
3267
|
-
"
|
|
3467
|
+
"Vanilla JS",
|
|
3468
|
+
"Django",
|
|
3469
|
+
"Laravel",
|
|
3470
|
+
"Rails",
|
|
3471
|
+
"Symfony"
|
|
3268
3472
|
];
|
|
3269
3473
|
var OTHER_OPTION = "Other";
|
|
3270
3474
|
var normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -3277,12 +3481,15 @@ var FRAMEWORK_ALIASES = {
|
|
|
3277
3481
|
vuejs: "vue",
|
|
3278
3482
|
angular: "angular",
|
|
3279
3483
|
angularjs: "angular",
|
|
3280
|
-
svelte: "svelte",
|
|
3281
|
-
sveltekit: "svelte",
|
|
3282
3484
|
vanillajs: "vanillajs",
|
|
3283
3485
|
vanilla: "vanillajs",
|
|
3284
3486
|
javascript: "vanillajs",
|
|
3285
|
-
js: "vanillajs"
|
|
3487
|
+
js: "vanillajs",
|
|
3488
|
+
django: "django",
|
|
3489
|
+
laravel: "laravel",
|
|
3490
|
+
rails: "rails",
|
|
3491
|
+
rubyonrails: "rails",
|
|
3492
|
+
symfony: "symfony"
|
|
3286
3493
|
};
|
|
3287
3494
|
var isSameFramework = (a, b) => {
|
|
3288
3495
|
const x = FRAMEWORK_ALIASES[normalize(a)] ?? normalize(a);
|
|
@@ -3383,8 +3590,8 @@ async function promptUser(ctx, params) {
|
|
|
3383
3590
|
}
|
|
3384
3591
|
|
|
3385
3592
|
// src/actions/confirmEntities.ts
|
|
3386
|
-
import
|
|
3387
|
-
var confirmEntitiesSchema =
|
|
3593
|
+
import z25 from "zod";
|
|
3594
|
+
var confirmEntitiesSchema = z25.object({
|
|
3388
3595
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
3389
3596
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3390
3597
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -3454,15 +3661,15 @@ async function confirmEntities(ctx) {
|
|
|
3454
3661
|
}
|
|
3455
3662
|
|
|
3456
3663
|
// src/actions/review.ts
|
|
3457
|
-
import { z as
|
|
3458
|
-
var reviewSchema =
|
|
3664
|
+
import { z as z26 } from "zod";
|
|
3665
|
+
var reviewSchema = z26.object({
|
|
3459
3666
|
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
3460
3667
|
// not one entry per workflow step — a step's raw output can be a long,
|
|
3461
3668
|
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
3462
3669
|
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
3463
|
-
summaryPoints:
|
|
3464
|
-
reviewPrompt:
|
|
3465
|
-
nextSteps:
|
|
3670
|
+
summaryPoints: z26.array(z26.string()),
|
|
3671
|
+
reviewPrompt: z26.string(),
|
|
3672
|
+
nextSteps: z26.array(z26.string())
|
|
3466
3673
|
});
|
|
3467
3674
|
function formatCompletedSteps(steps) {
|
|
3468
3675
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -3513,19 +3720,12 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3513
3720
|
};
|
|
3514
3721
|
|
|
3515
3722
|
// src/actions/implement.ts
|
|
3516
|
-
import
|
|
3723
|
+
import z27 from "zod";
|
|
3517
3724
|
|
|
3518
3725
|
// src/lib/worktree.ts
|
|
3519
|
-
import { execFile
|
|
3520
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3521
|
-
import {
|
|
3522
|
-
basename as basename2,
|
|
3523
|
-
dirname as dirname7,
|
|
3524
|
-
isAbsolute as isAbsolute2,
|
|
3525
|
-
join as join9,
|
|
3526
|
-
relative as relative2,
|
|
3527
|
-
resolve as resolve3
|
|
3528
|
-
} from "node:path";
|
|
3726
|
+
import { execFile } from "node:child_process";
|
|
3727
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile6, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3728
|
+
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join8, resolve as resolve3 } from "node:path";
|
|
3529
3729
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
3530
3730
|
var MAX_WIZARD_WORKTREES = 3;
|
|
3531
3731
|
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
@@ -3556,7 +3756,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3556
3756
|
return out.trim().length > 0;
|
|
3557
3757
|
}
|
|
3558
3758
|
async function pruneOldWorktrees(repoRoot) {
|
|
3559
|
-
const dir =
|
|
3759
|
+
const dir = join8(stateDir(repoRoot), "worktrees");
|
|
3560
3760
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3561
3761
|
for (const slug of stale) {
|
|
3562
3762
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3567,7 +3767,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3567
3767
|
"worktree",
|
|
3568
3768
|
"remove",
|
|
3569
3769
|
"--force",
|
|
3570
|
-
|
|
3770
|
+
join8(dir, slug)
|
|
3571
3771
|
]);
|
|
3572
3772
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3573
3773
|
} catch (err) {
|
|
@@ -3581,113 +3781,13 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3581
3781
|
async function createWorktree(repoRoot) {
|
|
3582
3782
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3583
3783
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3584
|
-
const path =
|
|
3784
|
+
const path = join8(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3585
3785
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3586
3786
|
await pruneOldWorktrees(repoRoot);
|
|
3587
3787
|
await mkdir6(dirname7(path), { recursive: true });
|
|
3588
3788
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3589
3789
|
return { path, branch };
|
|
3590
3790
|
}
|
|
3591
|
-
async function installWorktreeDeps(worktreePath) {
|
|
3592
|
-
try {
|
|
3593
|
-
await readPackageJson(worktreePath);
|
|
3594
|
-
} catch {
|
|
3595
|
-
return { ok: true, output: "no package.json; skipped install" };
|
|
3596
|
-
}
|
|
3597
|
-
const pm = await detectPackageManager(worktreePath);
|
|
3598
|
-
return new Promise((resolve4) => {
|
|
3599
|
-
let output = "";
|
|
3600
|
-
const child = spawn3(pm, ["install"], {
|
|
3601
|
-
cwd: worktreePath,
|
|
3602
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3603
|
-
});
|
|
3604
|
-
child.stdout?.on("data", (d) => output += d);
|
|
3605
|
-
child.stderr?.on("data", (d) => output += d);
|
|
3606
|
-
child.on(
|
|
3607
|
-
"error",
|
|
3608
|
-
(err) => resolve4({
|
|
3609
|
-
ok: false,
|
|
3610
|
-
output: `Failed to run ${pm} install: ${err.message}`
|
|
3611
|
-
})
|
|
3612
|
-
);
|
|
3613
|
-
child.on(
|
|
3614
|
-
"close",
|
|
3615
|
-
(code) => resolve4({ ok: code === 0, output: output.trim() })
|
|
3616
|
-
);
|
|
3617
|
-
});
|
|
3618
|
-
}
|
|
3619
|
-
var INGEST_RUNTIMES = ["node", "python", "python3", "bun"];
|
|
3620
|
-
function validateIngestEntrypoint(worktreePath, entrypoint) {
|
|
3621
|
-
if (!entrypoint || entrypoint.startsWith("-")) {
|
|
3622
|
-
return {
|
|
3623
|
-
ok: false,
|
|
3624
|
-
reason: `entrypoint "${entrypoint}" is not a plain file path`
|
|
3625
|
-
};
|
|
3626
|
-
}
|
|
3627
|
-
const target = resolve3(worktreePath, entrypoint);
|
|
3628
|
-
const rel = relative2(worktreePath, target);
|
|
3629
|
-
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
3630
|
-
return {
|
|
3631
|
-
ok: false,
|
|
3632
|
-
reason: `entrypoint "${entrypoint}" resolves outside the worktree`
|
|
3633
|
-
};
|
|
3634
|
-
}
|
|
3635
|
-
return { ok: true, target };
|
|
3636
|
-
}
|
|
3637
|
-
async function runIngestScript(worktreePath, runtime, entrypoint, env = {}) {
|
|
3638
|
-
if (!INGEST_RUNTIMES.includes(runtime)) {
|
|
3639
|
-
return {
|
|
3640
|
-
ran: false,
|
|
3641
|
-
ok: false,
|
|
3642
|
-
output: "",
|
|
3643
|
-
reason: `runtime "${runtime}" is not an allowed interpreter (${INGEST_RUNTIMES.join(", ")})`
|
|
3644
|
-
};
|
|
3645
|
-
}
|
|
3646
|
-
const validated = validateIngestEntrypoint(worktreePath, entrypoint);
|
|
3647
|
-
if (!validated.ok) {
|
|
3648
|
-
return { ran: false, ok: false, output: "", reason: validated.reason };
|
|
3649
|
-
}
|
|
3650
|
-
try {
|
|
3651
|
-
if (!(await stat2(validated.target)).isFile()) {
|
|
3652
|
-
return {
|
|
3653
|
-
ran: false,
|
|
3654
|
-
ok: false,
|
|
3655
|
-
output: "",
|
|
3656
|
-
reason: `entrypoint "${entrypoint}" is not a file`
|
|
3657
|
-
};
|
|
3658
|
-
}
|
|
3659
|
-
} catch {
|
|
3660
|
-
return {
|
|
3661
|
-
ran: false,
|
|
3662
|
-
ok: false,
|
|
3663
|
-
output: "",
|
|
3664
|
-
reason: `entrypoint "${entrypoint}" does not exist`
|
|
3665
|
-
};
|
|
3666
|
-
}
|
|
3667
|
-
return new Promise((resolveRun) => {
|
|
3668
|
-
let output = "";
|
|
3669
|
-
const child = spawn3(runtime, [entrypoint], {
|
|
3670
|
-
cwd: worktreePath,
|
|
3671
|
-
shell: false,
|
|
3672
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3673
|
-
env: { ...process.env, ...env }
|
|
3674
|
-
});
|
|
3675
|
-
child.stdout?.on("data", (d) => output += d);
|
|
3676
|
-
child.stderr?.on("data", (d) => output += d);
|
|
3677
|
-
child.on(
|
|
3678
|
-
"error",
|
|
3679
|
-
(err) => resolveRun({
|
|
3680
|
-
ran: true,
|
|
3681
|
-
ok: false,
|
|
3682
|
-
output: `Failed to run ${runtime} ${entrypoint}: ${err.message}`
|
|
3683
|
-
})
|
|
3684
|
-
);
|
|
3685
|
-
child.on(
|
|
3686
|
-
"close",
|
|
3687
|
-
(code) => resolveRun({ ran: true, ok: code === 0, output: output.trim() })
|
|
3688
|
-
);
|
|
3689
|
-
});
|
|
3690
|
-
}
|
|
3691
3791
|
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
3692
3792
|
const trimmed = sourcePath.trim();
|
|
3693
3793
|
if (!trimmed) {
|
|
@@ -3701,8 +3801,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3701
3801
|
} catch {
|
|
3702
3802
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3703
3803
|
}
|
|
3704
|
-
const relPath =
|
|
3705
|
-
const dest =
|
|
3804
|
+
const relPath = join8(ingestDir, basename2(source));
|
|
3805
|
+
const dest = join8(worktreePath, relPath);
|
|
3706
3806
|
try {
|
|
3707
3807
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3708
3808
|
await copyFile(source, dest);
|
|
@@ -3720,7 +3820,7 @@ function hasEnvVar(content, name) {
|
|
|
3720
3820
|
async function readEnvVar(worktreePath, name) {
|
|
3721
3821
|
let content;
|
|
3722
3822
|
try {
|
|
3723
|
-
content = await
|
|
3823
|
+
content = await readFile6(join8(worktreePath, ".env"), "utf8");
|
|
3724
3824
|
} catch (err) {
|
|
3725
3825
|
if (err.code !== "ENOENT") throw err;
|
|
3726
3826
|
return void 0;
|
|
@@ -3735,10 +3835,10 @@ async function readEnvVar(worktreePath, name) {
|
|
|
3735
3835
|
return value;
|
|
3736
3836
|
}
|
|
3737
3837
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3738
|
-
const target =
|
|
3838
|
+
const target = join8(worktreePath, ".env");
|
|
3739
3839
|
let existing = "";
|
|
3740
3840
|
try {
|
|
3741
|
-
existing = await
|
|
3841
|
+
existing = await readFile6(target, "utf8");
|
|
3742
3842
|
} catch (err) {
|
|
3743
3843
|
if (err.code !== "ENOENT") throw err;
|
|
3744
3844
|
}
|
|
@@ -3807,15 +3907,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3807
3907
|
}
|
|
3808
3908
|
|
|
3809
3909
|
// src/lib/algoliaDocs.ts
|
|
3810
|
-
import { readFileSync, readdirSync, existsSync
|
|
3811
|
-
import { dirname as dirname8, join as
|
|
3910
|
+
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
3911
|
+
import { dirname as dirname8, join as join9 } from "node:path";
|
|
3812
3912
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3813
|
-
var DOCS_SUBPATH =
|
|
3913
|
+
var DOCS_SUBPATH = join9("docs", "algolia-sdk");
|
|
3814
3914
|
function findDocsDir() {
|
|
3815
3915
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3816
3916
|
for (; ; ) {
|
|
3817
|
-
const candidate =
|
|
3818
|
-
if (
|
|
3917
|
+
const candidate = join9(dir, DOCS_SUBPATH);
|
|
3918
|
+
if (existsSync(candidate)) return candidate;
|
|
3819
3919
|
const parent = dirname8(dir);
|
|
3820
3920
|
if (parent === dir) return void 0;
|
|
3821
3921
|
dir = parent;
|
|
@@ -3837,7 +3937,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3837
3937
|
);
|
|
3838
3938
|
return "";
|
|
3839
3939
|
}
|
|
3840
|
-
return readFileSync(
|
|
3940
|
+
return readFileSync(join9(docsDir, files[0]), "utf8").trim();
|
|
3841
3941
|
}
|
|
3842
3942
|
function getNamedDoc(name, language) {
|
|
3843
3943
|
const docsDir = findDocsDir();
|
|
@@ -3845,14 +3945,15 @@ function getNamedDoc(name, language) {
|
|
|
3845
3945
|
logger.warn("docs/algolia-sdk not found");
|
|
3846
3946
|
return "";
|
|
3847
3947
|
}
|
|
3848
|
-
const file =
|
|
3849
|
-
if (!
|
|
3948
|
+
const file = join9(docsDir, `${name}-${language}.md`);
|
|
3949
|
+
if (!existsSync(file)) {
|
|
3850
3950
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3851
3951
|
return "";
|
|
3852
3952
|
}
|
|
3853
3953
|
return readFileSync(file, "utf8").trim();
|
|
3854
3954
|
}
|
|
3855
3955
|
function getFrameworkSpecificDoc(frameworks) {
|
|
3956
|
+
if (frameworks.length === 0) return "";
|
|
3856
3957
|
const fw = frameworks.map((f) => f.toLowerCase());
|
|
3857
3958
|
if (fw.includes("vue") || fw.includes("nuxt")) {
|
|
3858
3959
|
return loadAlgoliaDoc("vue");
|
|
@@ -3860,9 +3961,6 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
3860
3961
|
if (fw.includes("react") || fw.includes("next.js")) {
|
|
3861
3962
|
return loadAlgoliaDoc("react");
|
|
3862
3963
|
}
|
|
3863
|
-
if (fw.includes("angular")) {
|
|
3864
|
-
return loadAlgoliaDoc("angular");
|
|
3865
|
-
}
|
|
3866
3964
|
return loadAlgoliaDoc("js");
|
|
3867
3965
|
}
|
|
3868
3966
|
|
|
@@ -3872,62 +3970,63 @@ function shellQuote(value) {
|
|
|
3872
3970
|
}
|
|
3873
3971
|
|
|
3874
3972
|
// src/actions/implement.ts
|
|
3875
|
-
var implementSchema =
|
|
3876
|
-
filesChanged:
|
|
3877
|
-
summary:
|
|
3878
|
-
worktreePath:
|
|
3879
|
-
ingestCommand:
|
|
3880
|
-
ingestScriptRan:
|
|
3881
|
-
ingestRecordCount:
|
|
3882
|
-
ingestDurationMs:
|
|
3883
|
-
ingestionSource:
|
|
3884
|
-
searchEnvVars:
|
|
3885
|
-
|
|
3886
|
-
name:
|
|
3887
|
-
value:
|
|
3973
|
+
var implementSchema = z27.object({
|
|
3974
|
+
filesChanged: z27.array(z27.string()),
|
|
3975
|
+
summary: z27.string(),
|
|
3976
|
+
worktreePath: z27.string().optional(),
|
|
3977
|
+
ingestCommand: z27.string().optional(),
|
|
3978
|
+
ingestScriptRan: z27.boolean().optional(),
|
|
3979
|
+
ingestRecordCount: z27.number().optional(),
|
|
3980
|
+
ingestDurationMs: z27.number().optional(),
|
|
3981
|
+
ingestionSource: z27.enum(["local", "fileUpload", "generated"]),
|
|
3982
|
+
searchEnvVars: z27.array(
|
|
3983
|
+
z27.object({
|
|
3984
|
+
name: z27.string(),
|
|
3985
|
+
value: z27.string()
|
|
3888
3986
|
})
|
|
3889
3987
|
).optional()
|
|
3890
3988
|
});
|
|
3891
|
-
var implementationOutputSchema =
|
|
3892
|
-
summary:
|
|
3893
|
-
|
|
3894
|
-
// free-form command string. `runtime` is allowlisted and `entrypoint` is
|
|
3895
|
-
// validated worktree-relative, so the agent cannot inject extra commands.
|
|
3896
|
-
runtime: z26.enum(INGEST_RUNTIMES).optional(),
|
|
3897
|
-
entrypoint: z26.string().optional()
|
|
3989
|
+
var implementationOutputSchema = z27.object({
|
|
3990
|
+
summary: z27.string(),
|
|
3991
|
+
ingestCommand: z27.string().optional()
|
|
3898
3992
|
});
|
|
3899
|
-
var verificationOutputSchema =
|
|
3900
|
-
summary:
|
|
3901
|
-
sufficient:
|
|
3902
|
-
additionalInstructions:
|
|
3993
|
+
var verificationOutputSchema = z27.object({
|
|
3994
|
+
summary: z27.string(),
|
|
3995
|
+
sufficient: z27.boolean(),
|
|
3996
|
+
additionalInstructions: z27.string().optional()
|
|
3903
3997
|
});
|
|
3904
3998
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3905
3999
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
3906
4000
|
var INGEST_DIR = ".algolia-wizard";
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
if (names.some((n) => n.includes("react") || n.includes("next")))
|
|
3911
|
-
return "React";
|
|
3912
|
-
if (names.some((n) => n.includes("angular"))) return "Angular";
|
|
3913
|
-
return "JavaScript";
|
|
3914
|
-
}
|
|
3915
|
-
function frameworksForDoc(framework) {
|
|
3916
|
-
switch (framework) {
|
|
3917
|
-
case "React":
|
|
3918
|
-
return ["react"];
|
|
3919
|
-
case "Vue":
|
|
3920
|
-
return ["vue"];
|
|
3921
|
-
case "Angular":
|
|
3922
|
-
return ["angular"];
|
|
3923
|
-
case "JavaScript":
|
|
3924
|
-
return [];
|
|
3925
|
-
}
|
|
4001
|
+
var JS_LANGUAGES = ["javascript", "typescript", "jsx", "tsx", "node"];
|
|
4002
|
+
function lower(entries) {
|
|
4003
|
+
return entries.map((entry) => entry.name.toLowerCase());
|
|
3926
4004
|
}
|
|
3927
|
-
function
|
|
3928
|
-
|
|
3929
|
-
(
|
|
4005
|
+
function isJsProject(language) {
|
|
4006
|
+
return lower(language.languages).some(
|
|
4007
|
+
(name) => JS_LANGUAGES.some((js) => name.includes(js))
|
|
3930
4008
|
);
|
|
4009
|
+
}
|
|
4010
|
+
var UI_FRAMEWORKS = [
|
|
4011
|
+
{ match: ["vue", "nuxt"], target: "Vue", doc: "vue" },
|
|
4012
|
+
{ match: ["react", "next"], target: "React", doc: "react" },
|
|
4013
|
+
{ match: ["angular"], target: "Angular" }
|
|
4014
|
+
];
|
|
4015
|
+
function matchUiFramework(language) {
|
|
4016
|
+
const names = lower(language.frameworks);
|
|
4017
|
+
return UI_FRAMEWORKS.find(
|
|
4018
|
+
(ui) => ui.match.some((needle) => names.some((name) => name.includes(needle)))
|
|
4019
|
+
);
|
|
4020
|
+
}
|
|
4021
|
+
function searchUiTarget(language) {
|
|
4022
|
+
return matchUiFramework(language)?.target ?? language.frameworks[0]?.name ?? (isJsProject(language) ? "JavaScript" : "this project");
|
|
4023
|
+
}
|
|
4024
|
+
function frameworksForDoc(language) {
|
|
4025
|
+
if (!isJsProject(language)) return [];
|
|
4026
|
+
return [matchUiFramework(language)?.doc ?? "js"];
|
|
4027
|
+
}
|
|
4028
|
+
function publicEnvPrefix(language) {
|
|
4029
|
+
const frameworkNames = lower(language.frameworks);
|
|
3931
4030
|
if (frameworkNames.some((name) => name.includes("next"))) {
|
|
3932
4031
|
return "NEXT_PUBLIC_";
|
|
3933
4032
|
}
|
|
@@ -3940,7 +4039,7 @@ function publicEnvPrefix(language) {
|
|
|
3940
4039
|
if (frameworkNames.some((name) => name.includes("vite"))) {
|
|
3941
4040
|
return "VITE_";
|
|
3942
4041
|
}
|
|
3943
|
-
return "PUBLIC_";
|
|
4042
|
+
return isJsProject(language) ? "PUBLIC_" : "";
|
|
3944
4043
|
}
|
|
3945
4044
|
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
3946
4045
|
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
@@ -3979,7 +4078,10 @@ function baseInstructions(input) {
|
|
|
3979
4078
|
// index-scoped keys then reject with a 403.
|
|
3980
4079
|
`Target Algolia index, to be used exactly as written \u2014 never renamed, re-cased, prefixed, or suffixed: "${input.targetIndex}"`,
|
|
3981
4080
|
`Project languages and frameworks: ${JSON.stringify(input.language)}`,
|
|
3982
|
-
"Make minimal, idiomatic changes; do not touch unrelated code."
|
|
4081
|
+
"Make minimal, idiomatic changes; do not touch unrelated code.",
|
|
4082
|
+
`Commands run through a shell on ${process.platform}. Write commands that work there.`,
|
|
4083
|
+
"Use the project's own tooling for every command \u2014 its package manager, task runner, and test/lint commands. Do not assume a JavaScript toolchain.",
|
|
4084
|
+
"runShell needs the developer to approve each command, so give every call a clear `explanation` naming what it does and any side effect. If a command is rejected, do not retry it \u2014 take a different approach or report the limitation."
|
|
3983
4085
|
];
|
|
3984
4086
|
}
|
|
3985
4087
|
function sourceSpecificInstructions(input) {
|
|
@@ -3999,12 +4101,21 @@ function sourceSpecificInstructions(input) {
|
|
|
3999
4101
|
generated: [
|
|
4000
4102
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
4001
4103
|
"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 in the worktree, returning the file path. Do not write records or objectIDs yourself.",
|
|
4002
|
-
"In the script, read and parse each returned file path at runtime
|
|
4104
|
+
"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.",
|
|
4003
4105
|
"Add a prominent TODO where the developer swaps the generated records (and the JSON file under `.algolia-wizard/data/`) for their real record source."
|
|
4004
4106
|
]
|
|
4005
4107
|
};
|
|
4006
4108
|
return byLine[input.ingestionSource];
|
|
4007
4109
|
}
|
|
4110
|
+
function algoliaClientDoc(input) {
|
|
4111
|
+
const doc = getNamedDoc("save-records", "js");
|
|
4112
|
+
if (!doc) return [];
|
|
4113
|
+
if (isJsProject(input.language)) return [doc];
|
|
4114
|
+
return [
|
|
4115
|
+
"The reference below is written in JavaScript. Use it for the method names, arguments, and record shape, then translate to this project's language and its official Algolia client:",
|
|
4116
|
+
doc
|
|
4117
|
+
];
|
|
4118
|
+
}
|
|
4008
4119
|
function ingestionInstructions(input) {
|
|
4009
4120
|
return [
|
|
4010
4121
|
...input.confirmed && input.confirmed.length ? [
|
|
@@ -4012,25 +4123,30 @@ function ingestionInstructions(input) {
|
|
|
4012
4123
|
`Ingest only these confirmed entities (name, source paths, attributes): ${JSON.stringify(input.confirmed)}.`,
|
|
4013
4124
|
`Ingesting writes to Algolia, so the script needs a write API key and App ID \u2014 read them from the ${API_KEY_VAR} and ${APP_ID_VAR} environment variables rather than hardcoding them. The wizard sets these when it runs the script.`,
|
|
4014
4125
|
`Read the index name from the ${INDEX_NAME_VAR} environment variable, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or entity name \u2014 the write key only works for that exact index. Exit with an error if ${INDEX_NAME_VAR} is unset.`,
|
|
4015
|
-
"
|
|
4126
|
+
"Write the script in the project's primary language, using Algolia's official client for that language. Do not use the raw HTTP API.",
|
|
4016
4127
|
"After a successful ingest, the script must print exactly one line to stdout in the form `ALGOLIA_WIZARD_RECORD_COUNT=<n>`, where <n> is the total number of records pushed to Algolia. Print it last, on its own line, with no surrounding text.",
|
|
4017
|
-
|
|
4018
|
-
'
|
|
4128
|
+
...algoliaClientDoc(input),
|
|
4129
|
+
"Install the Algolia client with the project's own package manager via runShell, declaring it in whatever manifest the project uses (e.g. package.json, requirements.txt, Gemfile, go.mod, composer.json) so the dependency is not just installed ad hoc.",
|
|
4130
|
+
'Then run the script yourself via runShell, and report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
|
|
4019
4131
|
"The summary should be extremely concise.",
|
|
4020
|
-
`Return how to run the script as two fields, not a command string: "runtime" (one of ${INGEST_RUNTIMES.join(", ")}) and "entrypoint" (the script path relative to the worktree root, e.g. "${input.ingestDir}/ingest.mjs"). The wizard runs \`<runtime> <entrypoint>\` directly, so the entrypoint must be a plain path with no flags or arguments. Write a script one of those interpreters can run as-is.`,
|
|
4021
4132
|
...sourceSpecificInstructions(input)
|
|
4022
4133
|
] : []
|
|
4023
4134
|
];
|
|
4024
4135
|
}
|
|
4025
4136
|
function searchInstructions(input) {
|
|
4026
|
-
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.
|
|
4137
|
+
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.language));
|
|
4027
4138
|
return [
|
|
4028
4139
|
"Implement an in-app Algolia search experience.",
|
|
4029
|
-
`Build the search UI for ${input.
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4140
|
+
`Build the search UI for ${input.searchUiTarget}.`,
|
|
4141
|
+
...doc ? [
|
|
4142
|
+
"Follow the Algolia SDK reference below for client setup and search UI wiring; prefer it over prior knowledge:",
|
|
4143
|
+
doc
|
|
4144
|
+
] : [
|
|
4145
|
+
"No bundled Algolia SDK reference exists for this stack, so rely on the project's own conventions and Algolia's official client for its language. Do not invent APIs \u2014 keep to the documented search endpoint and its parameters."
|
|
4146
|
+
],
|
|
4147
|
+
`Add the search UI at ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working search input and results list against the target index.`,
|
|
4033
4148
|
`Read the index name from the ${searchIndexVar(input.language)} env var, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or component name.`,
|
|
4149
|
+
"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.",
|
|
4034
4150
|
// The key is provisioned only after verification passes, so the agent never
|
|
4035
4151
|
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
4036
4152
|
// decide whether a key already exists, and an agent-invented value there
|
|
@@ -4039,9 +4155,8 @@ function searchInstructions(input) {
|
|
|
4039
4155
|
// Not the agent's to rename: the wizard writes these exact names into
|
|
4040
4156
|
// ".env" right after this step, so a renamed prefix would leave the code
|
|
4041
4157
|
// reading a var the wizard never wrote.
|
|
4042
|
-
`Use exactly these
|
|
4043
|
-
"
|
|
4044
|
-
'Add any Algolia/InstantSearch packages you import to package.json "dependencies" with a valid version range; the wizard installs them in the worktree after you finish.',
|
|
4158
|
+
`Use exactly these env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4159
|
+
"Install any Algolia packages you import with the project's own package manager via runShell, and declare them in the project's dependency manifest.",
|
|
4045
4160
|
"The summary should be extremely concise; do not mention env var setup or manual testing steps \u2014 the wizard writes the resolved credentials to .env and reports that separately."
|
|
4046
4161
|
];
|
|
4047
4162
|
}
|
|
@@ -4049,11 +4164,12 @@ function verificationInstructions(input) {
|
|
|
4049
4164
|
return [
|
|
4050
4165
|
"Verify the Algolia implementation changes in the current worktree.",
|
|
4051
4166
|
`Verification tools found in the codebase: ${JSON.stringify(input.findings.verification ?? [])}.`,
|
|
4052
|
-
"
|
|
4053
|
-
"
|
|
4054
|
-
"
|
|
4167
|
+
"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.",
|
|
4168
|
+
"This worktree starts with no installed dependencies. If a check fails because packages or modules are missing, install the dependencies via runShell and re-run it rather than changing the code.",
|
|
4169
|
+
"For issues caused by the implementation, make minimal fixes with writeFile and re-run the checks.",
|
|
4170
|
+
"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.",
|
|
4055
4171
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
4056
|
-
`Do not modify "${input.ingestDir}/" unless
|
|
4172
|
+
`Do not modify "${input.ingestDir}/" unless a check reports an actionable issue in its files.`,
|
|
4057
4173
|
"Always call reportStatus with status=success once verification has run, even when sufficient=false.",
|
|
4058
4174
|
"Set sufficient=true only when the implementation is complete and checks pass (or fail for a clearly unrelated reason).",
|
|
4059
4175
|
"Set sufficient=false when the implementation is incomplete or has implementation-caused failures; include concrete additionalInstructions for the next pass."
|
|
@@ -4074,14 +4190,15 @@ var IMPLEMENT_CONFIG = {
|
|
|
4074
4190
|
}
|
|
4075
4191
|
};
|
|
4076
4192
|
var useCaseToolMap = {
|
|
4077
|
-
ingestion: [
|
|
4078
|
-
search: [...FS_READ_TOOLS, "writeFile", "notifyUser"],
|
|
4079
|
-
verification: [
|
|
4193
|
+
ingestion: [
|
|
4080
4194
|
...FS_READ_TOOLS,
|
|
4081
4195
|
"writeFile",
|
|
4082
|
-
"
|
|
4196
|
+
"writeCredentials",
|
|
4197
|
+
"runShell",
|
|
4083
4198
|
"notifyUser"
|
|
4084
|
-
]
|
|
4199
|
+
],
|
|
4200
|
+
search: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"],
|
|
4201
|
+
verification: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"]
|
|
4085
4202
|
};
|
|
4086
4203
|
function toolsForUseCase(useCase, ingestionSource) {
|
|
4087
4204
|
const tools = useCaseToolMap[useCase];
|
|
@@ -4104,15 +4221,30 @@ function formatSummary(useCase, summary) {
|
|
|
4104
4221
|
const label = useCase === "ingestion" ? "Ingestion" : useCase === "search" ? "Search" : "Verification";
|
|
4105
4222
|
return `${label}: ${summary}`;
|
|
4106
4223
|
}
|
|
4107
|
-
function buildIngestCommand(worktree, runtime, entrypoint) {
|
|
4108
|
-
return `cd ${shellQuote(worktree)} && ${runtime} ${shellQuote(entrypoint)}`;
|
|
4109
|
-
}
|
|
4110
4224
|
function parseIngestRecordCount(output) {
|
|
4111
4225
|
const match = output.match(/ALGOLIA_WIZARD_RECORD_COUNT=(\d+)/);
|
|
4112
4226
|
if (!match) return void 0;
|
|
4113
4227
|
const count = Number(match[1]);
|
|
4114
4228
|
return Number.isFinite(count) ? count : void 0;
|
|
4115
4229
|
}
|
|
4230
|
+
function ingestOutcome(executions, ingestCommand) {
|
|
4231
|
+
const newestFirst = [...executions].reverse();
|
|
4232
|
+
const withCount = newestFirst.filter(
|
|
4233
|
+
(e) => parseIngestRecordCount(e.output ?? "") != null
|
|
4234
|
+
);
|
|
4235
|
+
const succeeded = newestFirst.filter((e) => e.approved && e.exitCode === 0);
|
|
4236
|
+
return {
|
|
4237
|
+
run: succeeded.find((e) => e.command === ingestCommand) ?? succeeded.find((e) => withCount.includes(e)),
|
|
4238
|
+
recordCount: parseIngestRecordCount(withCount[0]?.output ?? "")
|
|
4239
|
+
};
|
|
4240
|
+
}
|
|
4241
|
+
function makeToolContext(worktree, env = async () => ({})) {
|
|
4242
|
+
return createToolContext(
|
|
4243
|
+
DEFAULT_TOOL_LIMITS,
|
|
4244
|
+
worktree,
|
|
4245
|
+
createShellContext({ env, approve: storeApproval(worktree) })
|
|
4246
|
+
);
|
|
4247
|
+
}
|
|
4116
4248
|
function verificationRetryInstructions(verification) {
|
|
4117
4249
|
return [
|
|
4118
4250
|
`Implementation insufficient. Address these findings before reporting completion: ${verification.additionalInstructions ?? verification.summary}`
|
|
@@ -4191,9 +4323,13 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4191
4323
|
const confirmed2 = normalized.confirmedEntities;
|
|
4192
4324
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
4193
4325
|
let appId;
|
|
4326
|
+
let ingestAppId;
|
|
4194
4327
|
if (useCases.includes("search")) {
|
|
4195
4328
|
appId = (await requireApplication()).id;
|
|
4196
4329
|
}
|
|
4330
|
+
if (useCases.includes("ingestion")) {
|
|
4331
|
+
ingestAppId = appId ?? (await requireApplication()).id;
|
|
4332
|
+
}
|
|
4197
4333
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
4198
4334
|
try {
|
|
4199
4335
|
process.chdir(worktree);
|
|
@@ -4230,7 +4366,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4230
4366
|
ingestDir: INGEST_DIR,
|
|
4231
4367
|
ingestionSource,
|
|
4232
4368
|
uploadFilePath,
|
|
4233
|
-
|
|
4369
|
+
searchUiTarget: searchUiTarget(language)
|
|
4234
4370
|
};
|
|
4235
4371
|
const summaries = [];
|
|
4236
4372
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
@@ -4253,41 +4389,31 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4253
4389
|
}
|
|
4254
4390
|
let finalSearchEnvVars = input.searchEnvVars;
|
|
4255
4391
|
let agentRuns = 0;
|
|
4256
|
-
let
|
|
4257
|
-
let ingestEntrypoint;
|
|
4392
|
+
let ingestCommand;
|
|
4258
4393
|
let ingestScriptRan = false;
|
|
4259
4394
|
let ingestRecordCount;
|
|
4260
4395
|
let ingestDurationMs;
|
|
4261
|
-
let installFailed = false;
|
|
4262
4396
|
let ingestOutcomeMessage;
|
|
4397
|
+
const ingestKeyAppId = ingestAppId;
|
|
4398
|
+
const ingestionTools = ingestKeyAppId ? makeToolContext(worktree, async () => ({
|
|
4399
|
+
[APP_ID_VAR]: ingestKeyAppId,
|
|
4400
|
+
[API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
|
|
4401
|
+
[INDEX_NAME_VAR]: targetIndex
|
|
4402
|
+
})) : void 0;
|
|
4403
|
+
const searchTools = makeToolContext(worktree);
|
|
4263
4404
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4264
4405
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4265
4406
|
agentRuns += 1;
|
|
4266
|
-
|
|
4407
|
+
return runAgent({
|
|
4267
4408
|
instructions: buildAgentInstructions(
|
|
4268
4409
|
currentUseCase,
|
|
4269
4410
|
input,
|
|
4270
4411
|
extraInstructions
|
|
4271
4412
|
),
|
|
4272
4413
|
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4273
|
-
outputSchema: implementationOutputSchema
|
|
4414
|
+
outputSchema: implementationOutputSchema,
|
|
4415
|
+
toolContext: currentUseCase === "ingestion" ? ingestionTools ?? searchTools : searchTools
|
|
4274
4416
|
});
|
|
4275
|
-
ctx.notify({
|
|
4276
|
-
messages: [`Installing dependencies for ${currentUseCase}\u2026`]
|
|
4277
|
-
});
|
|
4278
|
-
const installLogId = ctx.logStart("installWorktreeDeps", {
|
|
4279
|
-
useCase: currentUseCase
|
|
4280
|
-
});
|
|
4281
|
-
const install = await installWorktreeDeps(worktree);
|
|
4282
|
-
ctx.logEnd(installLogId, install.ok ? "success" : "error");
|
|
4283
|
-
if (!install.ok) {
|
|
4284
|
-
installFailed = true;
|
|
4285
|
-
logger.warn(
|
|
4286
|
-
{ useCase: currentUseCase, output: install.output },
|
|
4287
|
-
"implement: dependency install in worktree failed; generated commands may not run until deps are installed"
|
|
4288
|
-
);
|
|
4289
|
-
}
|
|
4290
|
-
return result;
|
|
4291
4417
|
}
|
|
4292
4418
|
async function runVerificationUseCase() {
|
|
4293
4419
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
@@ -4295,111 +4421,55 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4295
4421
|
return runAgent({
|
|
4296
4422
|
instructions: buildAgentInstructions("verification", input),
|
|
4297
4423
|
tools: toolsForUseCase("verification"),
|
|
4298
|
-
outputSchema: verificationOutputSchema
|
|
4424
|
+
outputSchema: verificationOutputSchema,
|
|
4425
|
+
toolContext: searchTools
|
|
4299
4426
|
});
|
|
4300
4427
|
}
|
|
4301
4428
|
if (useCases.includes("ingestion")) {
|
|
4302
|
-
const
|
|
4303
|
-
summaries.push(formatSummary("ingestion", summary));
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
entrypoint: ingestEntrypoint
|
|
4429
|
+
const result = await runImplementationUseCase("ingestion");
|
|
4430
|
+
summaries.push(formatSummary("ingestion", result.summary));
|
|
4431
|
+
ingestCommand = result.ingestCommand;
|
|
4432
|
+
const executions = (ingestionTools ?? searchTools).shell.executions;
|
|
4433
|
+
const { run: ingestRun, recordCount } = ingestOutcome(
|
|
4434
|
+
executions,
|
|
4435
|
+
ingestCommand
|
|
4436
|
+
);
|
|
4437
|
+
ingestScriptRan = ingestRun != null;
|
|
4438
|
+
ingestRecordCount = recordCount;
|
|
4439
|
+
ingestDurationMs = ingestRun?.durationMs;
|
|
4440
|
+
if (ingestScriptRan) {
|
|
4441
|
+
ingestOutcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4442
|
+
if (ingestRecordCount != null) {
|
|
4443
|
+
track("AI Wizard Ingest Successful", {
|
|
4444
|
+
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4445
|
+
record_count: ingestRecordCount,
|
|
4446
|
+
duration_ms: ingestDurationMs ?? 0
|
|
4321
4447
|
});
|
|
4322
|
-
const startedAt = Date.now();
|
|
4323
|
-
const run2 = await runIngestScript(
|
|
4324
|
-
worktree,
|
|
4325
|
-
ingestRuntime,
|
|
4326
|
-
ingestEntrypoint,
|
|
4327
|
-
{
|
|
4328
|
-
[APP_ID_VAR]: ingestApp.id,
|
|
4329
|
-
[API_KEY_VAR]: writeKey,
|
|
4330
|
-
[INDEX_NAME_VAR]: targetIndex
|
|
4331
|
-
}
|
|
4332
|
-
);
|
|
4333
|
-
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
4334
|
-
ingestScriptRan = run2.ran && run2.ok;
|
|
4335
|
-
if (ingestScriptRan) {
|
|
4336
|
-
ingestDurationMs = Date.now() - startedAt;
|
|
4337
|
-
ingestRecordCount = parseIngestRecordCount(run2.output);
|
|
4338
|
-
if (ingestRecordCount != null) {
|
|
4339
|
-
track("AI Wizard Ingest Successful", {
|
|
4340
|
-
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4341
|
-
record_count: ingestRecordCount,
|
|
4342
|
-
duration_ms: ingestDurationMs
|
|
4343
|
-
});
|
|
4344
|
-
}
|
|
4345
|
-
}
|
|
4346
|
-
let summaryLine;
|
|
4347
|
-
let outcomeMessage;
|
|
4348
|
-
if (!run2.ran) {
|
|
4349
|
-
summaryLine = `\u26A0\uFE0F Skipped running the ingestion script: ${run2.reason}`;
|
|
4350
|
-
outcomeMessage = `\u26A0\uFE0F The ingestion script did not run: ${run2.reason}`;
|
|
4351
|
-
logger.warn(
|
|
4352
|
-
{
|
|
4353
|
-
runtime: ingestRuntime,
|
|
4354
|
-
entrypoint: ingestEntrypoint,
|
|
4355
|
-
reason: run2.reason
|
|
4356
|
-
},
|
|
4357
|
-
"implement: refused to auto-run ingestion script"
|
|
4358
|
-
);
|
|
4359
|
-
track("Error", {
|
|
4360
|
-
step: "Push Data",
|
|
4361
|
-
error: `ingestion script skipped: ${run2.reason}`,
|
|
4362
|
-
product_area: "AI Wizard"
|
|
4363
|
-
});
|
|
4364
|
-
} else if (run2.ok) {
|
|
4365
|
-
const status = "Ingestion run: succeeded.";
|
|
4366
|
-
summaryLine = run2.output ? `${status}
|
|
4367
|
-
${run2.output}` : status;
|
|
4368
|
-
outcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4369
|
-
} else {
|
|
4370
|
-
const status = "\u26A0\uFE0F Ingestion run failed:";
|
|
4371
|
-
summaryLine = run2.output ? `${status}
|
|
4372
|
-
${run2.output}` : status;
|
|
4373
|
-
outcomeMessage = `\u274C Ingestion failed.${run2.output ? ` ${run2.output}` : ""}`;
|
|
4374
|
-
logger.warn(
|
|
4375
|
-
{
|
|
4376
|
-
runtime: ingestRuntime,
|
|
4377
|
-
entrypoint: ingestEntrypoint,
|
|
4378
|
-
output: run2.output
|
|
4379
|
-
},
|
|
4380
|
-
"implement: ingestion script run failed"
|
|
4381
|
-
);
|
|
4382
|
-
track("Error", {
|
|
4383
|
-
step: "Push Data",
|
|
4384
|
-
error: run2.output || "ingestion script exited non-zero",
|
|
4385
|
-
product_area: "AI Wizard"
|
|
4386
|
-
});
|
|
4387
|
-
}
|
|
4388
|
-
summaries.push(summaryLine);
|
|
4389
|
-
ingestOutcomeMessage = outcomeMessage;
|
|
4390
4448
|
}
|
|
4449
|
+
} else {
|
|
4450
|
+
const rejected = executions.some((e) => !e.approved);
|
|
4451
|
+
const reason = rejected ? "you declined to run it" : "no successful run was recorded";
|
|
4452
|
+
ingestOutcomeMessage = `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.${ingestCommand ? " Run the command below when you are ready." : ""}`;
|
|
4453
|
+
summaries.push(`\u26A0\uFE0F The ingestion script did not run: ${reason}.`);
|
|
4454
|
+
logger.warn(
|
|
4455
|
+
{ ingestCommand, rejected, commandsRun: executions.length },
|
|
4456
|
+
"implement: ingestion script did not complete successfully"
|
|
4457
|
+
);
|
|
4458
|
+
track("Error", {
|
|
4459
|
+
step: "Push Data",
|
|
4460
|
+
error: `ingestion did not run: ${reason}`,
|
|
4461
|
+
product_area: "AI Wizard"
|
|
4462
|
+
});
|
|
4391
4463
|
}
|
|
4392
4464
|
const commandMessages = [`Open the worktree: cd ${shellQuote(worktree)}`];
|
|
4393
|
-
if (
|
|
4394
|
-
commandMessages.push(
|
|
4395
|
-
`Ingestion command: ${buildIngestCommand(worktree, ingestRuntime, ingestEntrypoint)}`
|
|
4396
|
-
);
|
|
4465
|
+
if (ingestCommand) {
|
|
4466
|
+
commandMessages.push(`Ingestion command: ${ingestCommand}`);
|
|
4397
4467
|
}
|
|
4398
4468
|
await ctx.requestUserInput({
|
|
4399
4469
|
prompt: "",
|
|
4400
4470
|
promptType: "enterToContinue",
|
|
4401
4471
|
options: [],
|
|
4402
|
-
messages:
|
|
4472
|
+
messages: [ingestOutcomeMessage, ...commandMessages]
|
|
4403
4473
|
});
|
|
4404
4474
|
}
|
|
4405
4475
|
if (useCases.includes("search")) {
|
|
@@ -4516,22 +4586,13 @@ ${run2.output}` : status;
|
|
|
4516
4586
|
"implement: agent reported success but no files changed in the worktree"
|
|
4517
4587
|
);
|
|
4518
4588
|
}
|
|
4519
|
-
if (installFailed) {
|
|
4520
|
-
summaries.push(
|
|
4521
|
-
'\u26A0\uFE0F Dependency install in the worktree failed. Run your package manager install in the worktree before the command below, or it will fail with "Cannot find module".'
|
|
4522
|
-
);
|
|
4523
|
-
}
|
|
4524
4589
|
return {
|
|
4525
4590
|
ingestionSource,
|
|
4526
4591
|
filesChanged,
|
|
4527
4592
|
summary: summaries.join("\n\n"),
|
|
4528
4593
|
worktreePath: worktree,
|
|
4529
|
-
...useCases.includes("ingestion") &&
|
|
4530
|
-
ingestCommand
|
|
4531
|
-
worktree,
|
|
4532
|
-
ingestRuntime,
|
|
4533
|
-
ingestEntrypoint
|
|
4534
|
-
),
|
|
4594
|
+
...useCases.includes("ingestion") && ingestCommand ? {
|
|
4595
|
+
ingestCommand,
|
|
4535
4596
|
ingestScriptRan,
|
|
4536
4597
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4537
4598
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
@@ -4579,8 +4640,8 @@ var defaultWorkflow = {
|
|
|
4579
4640
|
defineStep({
|
|
4580
4641
|
id: "select-index",
|
|
4581
4642
|
title: "Set up index",
|
|
4582
|
-
outputSchema:
|
|
4583
|
-
selection:
|
|
4643
|
+
outputSchema: z28.object({
|
|
4644
|
+
selection: z28.string()
|
|
4584
4645
|
}),
|
|
4585
4646
|
run: (ctx) => selectIndexStep(ctx)
|
|
4586
4647
|
}),
|
|
@@ -4822,7 +4883,10 @@ Options:
|
|
|
4822
4883
|
--no-telemetry Send no telemetry or analytics for this run.
|
|
4823
4884
|
--reset-on-run Wipe this project's wizard state (run state, AI consent,
|
|
4824
4885
|
worktrees) before starting, so the run behaves like a
|
|
4825
|
-
first-ever run.
|
|
4886
|
+
first-ever run. Also drops every API key the wizard has
|
|
4887
|
+
stored in your keychain, for this project and any other, so
|
|
4888
|
+
later runs create new ones. Your Algolia login is not
|
|
4889
|
+
touched.
|
|
4826
4890
|
-h, --help Print this message.`;
|
|
4827
4891
|
function parseCliArgs(argv) {
|
|
4828
4892
|
const positionals = [];
|
|
@@ -4859,11 +4923,11 @@ function parseCliArgs(argv) {
|
|
|
4859
4923
|
|
|
4860
4924
|
// src/lib/resetState.ts
|
|
4861
4925
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4862
|
-
import { join as
|
|
4926
|
+
import { join as join10 } from "node:path";
|
|
4863
4927
|
var KEEP = ["wizard.log"];
|
|
4864
4928
|
async function resetProjectState() {
|
|
4865
4929
|
const dir = stateDir();
|
|
4866
|
-
forgetResolvedKeys();
|
|
4930
|
+
await forgetResolvedKeys();
|
|
4867
4931
|
let entries;
|
|
4868
4932
|
try {
|
|
4869
4933
|
entries = await readdir4(dir);
|
|
@@ -4873,14 +4937,14 @@ async function resetProjectState() {
|
|
|
4873
4937
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4874
4938
|
await Promise.all(
|
|
4875
4939
|
targets.map(
|
|
4876
|
-
(name) => rm2(
|
|
4940
|
+
(name) => rm2(join10(dir, name), { recursive: true, force: true })
|
|
4877
4941
|
)
|
|
4878
4942
|
);
|
|
4879
4943
|
return { dir, removed: targets };
|
|
4880
4944
|
}
|
|
4881
4945
|
|
|
4882
4946
|
// src/main.tsx
|
|
4883
|
-
import { jsx as
|
|
4947
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4884
4948
|
async function startup() {
|
|
4885
4949
|
setProjectRoot(process.cwd());
|
|
4886
4950
|
let args;
|
|
@@ -4930,7 +4994,7 @@ ${formatStepList(workflow)}`);
|
|
|
4930
4994
|
}
|
|
4931
4995
|
async function run(workflow) {
|
|
4932
4996
|
const store = useWizard.getState();
|
|
4933
|
-
const instance = render(/* @__PURE__ */
|
|
4997
|
+
const instance = render(/* @__PURE__ */ jsx15(App, {}), { incrementalRendering: true });
|
|
4934
4998
|
await store.waitForStart();
|
|
4935
4999
|
let user = await getUser();
|
|
4936
5000
|
if (!user) {
|