@algolia/wizard 0.9.0-rc.88.103 → 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";
|
|
@@ -622,10 +622,13 @@ function Notices() {
|
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
// src/ui/PromptInput.tsx
|
|
625
|
-
import { Box as
|
|
625
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
626
626
|
import TextInput from "ink-text-input";
|
|
627
627
|
import { useState as useState5 } from "react";
|
|
628
628
|
|
|
629
|
+
// src/ui/CommandApproval.tsx
|
|
630
|
+
import { Box as Box5, Text as Text5, useInput } from "ink";
|
|
631
|
+
|
|
629
632
|
// src/ui/NextAction.tsx
|
|
630
633
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
631
634
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -649,14 +652,60 @@ function NextAction({
|
|
|
649
652
|
] });
|
|
650
653
|
}
|
|
651
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
|
+
|
|
652
701
|
// src/ui/SelectPrompt.tsx
|
|
653
|
-
import { Box as
|
|
702
|
+
import { Box as Box7, Text as Text7, useInput as useInput2, useWindowSize as useWindowSize5 } from "ink";
|
|
654
703
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
655
704
|
|
|
656
705
|
// src/ui/ScrollView.tsx
|
|
657
|
-
import { Box as
|
|
706
|
+
import { Box as Box6, Text as Text6, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
|
|
658
707
|
import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
659
|
-
import { jsxs as
|
|
708
|
+
import { jsxs as jsxs5 } from "react/jsx-runtime";
|
|
660
709
|
var INDICATOR_ROWS = 2;
|
|
661
710
|
function fittedWidth(node, columns) {
|
|
662
711
|
let left = 0;
|
|
@@ -726,14 +775,14 @@ function useScrollWindow({
|
|
|
726
775
|
};
|
|
727
776
|
}
|
|
728
777
|
function ScrollView({ scroll, children }) {
|
|
729
|
-
return /* @__PURE__ */
|
|
730
|
-
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: [
|
|
731
780
|
"\u2191 ",
|
|
732
781
|
scroll.hiddenAbove,
|
|
733
782
|
" more"
|
|
734
783
|
] }),
|
|
735
784
|
children,
|
|
736
|
-
scroll.hiddenBelow > 0 && /* @__PURE__ */
|
|
785
|
+
scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
737
786
|
"\u2193 ",
|
|
738
787
|
scroll.hiddenBelow,
|
|
739
788
|
" more"
|
|
@@ -742,7 +791,7 @@ function ScrollView({ scroll, children }) {
|
|
|
742
791
|
}
|
|
743
792
|
|
|
744
793
|
// src/ui/SelectPrompt.tsx
|
|
745
|
-
import { jsx as
|
|
794
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
746
795
|
var CANCEL = "cancel";
|
|
747
796
|
var ARROW_WIDTH = 4;
|
|
748
797
|
var COLUMN_GAP = 2;
|
|
@@ -804,7 +853,7 @@ function SelectPrompt({
|
|
|
804
853
|
revealIndex(index);
|
|
805
854
|
}, [index, revealIndex]);
|
|
806
855
|
const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
807
|
-
|
|
856
|
+
useInput2((input, key) => {
|
|
808
857
|
if (rows.length === 0) return;
|
|
809
858
|
if (key.upArrow || input === "k") {
|
|
810
859
|
setIndex((i) => (i - 1 + rows.length) % rows.length);
|
|
@@ -827,56 +876,56 @@ function SelectPrompt({
|
|
|
827
876
|
}
|
|
828
877
|
}
|
|
829
878
|
});
|
|
830
|
-
return /* @__PURE__ */
|
|
831
|
-
/* @__PURE__ */
|
|
832
|
-
error && /* @__PURE__ */
|
|
833
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
834
|
-
table && /* @__PURE__ */
|
|
835
|
-
/* @__PURE__ */
|
|
836
|
-
question && /* @__PURE__ */
|
|
837
|
-
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 })
|
|
838
887
|
] })
|
|
839
888
|
] }),
|
|
840
|
-
/* @__PURE__ */
|
|
889
|
+
/* @__PURE__ */ jsx5(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
|
|
841
890
|
const i = scroll.offset + visibleIndex;
|
|
842
891
|
const highlighted = i === index;
|
|
843
892
|
const isCancel = i === cancelIndex;
|
|
844
893
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
845
894
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
846
895
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
847
|
-
const label = /* @__PURE__ */
|
|
896
|
+
const label = /* @__PURE__ */ jsxs6(Text7, { color: labelColor, wrap: "truncate", children: [
|
|
848
897
|
highlighted ? "\u276F " : " ",
|
|
849
898
|
bullet,
|
|
850
899
|
option
|
|
851
900
|
] });
|
|
852
901
|
const isText = sec?.kind === "text";
|
|
853
|
-
return /* @__PURE__ */
|
|
854
|
-
|
|
902
|
+
return /* @__PURE__ */ jsxs6(
|
|
903
|
+
Box7,
|
|
855
904
|
{
|
|
856
905
|
width: isText ? "100%" : barWidth,
|
|
857
906
|
paddingX: 1,
|
|
858
907
|
paddingY: 1,
|
|
859
908
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
860
909
|
children: [
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
isText && textWidth > 0 && /* @__PURE__ */
|
|
863
|
-
|
|
910
|
+
/* @__PURE__ */ jsx5(Box7, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
911
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx5(Box7, { width: textWidth, children: /* @__PURE__ */ jsx5(
|
|
912
|
+
Text7,
|
|
864
913
|
{
|
|
865
914
|
wrap: "truncate",
|
|
866
915
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
867
916
|
children: sec.value
|
|
868
917
|
}
|
|
869
918
|
) }),
|
|
870
|
-
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 }) })
|
|
871
920
|
]
|
|
872
921
|
},
|
|
873
922
|
`row-${i}`
|
|
874
923
|
);
|
|
875
924
|
}) }),
|
|
876
|
-
/* @__PURE__ */
|
|
925
|
+
/* @__PURE__ */ jsx5(Box7, { flexShrink: 0, children: /* @__PURE__ */ jsx5(Text7, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
877
926
|
i > 0 ? " " : "",
|
|
878
|
-
/* @__PURE__ */
|
|
879
|
-
/* @__PURE__ */
|
|
927
|
+
/* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: key }),
|
|
928
|
+
/* @__PURE__ */ jsxs6(Text7, { color: COLORS.dim, children: [
|
|
880
929
|
" ",
|
|
881
930
|
label
|
|
882
931
|
] })
|
|
@@ -885,23 +934,23 @@ function SelectPrompt({
|
|
|
885
934
|
}
|
|
886
935
|
|
|
887
936
|
// src/ui/PromptInput.tsx
|
|
888
|
-
import { jsx as
|
|
937
|
+
import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
889
938
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
890
939
|
function EnterToContinuePrompt({
|
|
891
940
|
question,
|
|
892
941
|
messages,
|
|
893
942
|
onDecide
|
|
894
943
|
}) {
|
|
895
|
-
|
|
944
|
+
useInput3((_input, key) => {
|
|
896
945
|
if (key.return) onDecide(true);
|
|
897
946
|
else if (key.escape) onDecide(false);
|
|
898
947
|
});
|
|
899
|
-
return /* @__PURE__ */
|
|
900
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
901
|
-
question && /* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
903
|
-
/* @__PURE__ */
|
|
904
|
-
/* @__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" })
|
|
905
954
|
] })
|
|
906
955
|
] });
|
|
907
956
|
}
|
|
@@ -909,11 +958,11 @@ function PromptInput() {
|
|
|
909
958
|
const { phase, inputReq, submitInput } = useWizard();
|
|
910
959
|
const [draft, setDraft] = useState5("");
|
|
911
960
|
if (phase === "done" || phase === "error") {
|
|
912
|
-
return /* @__PURE__ */
|
|
961
|
+
return /* @__PURE__ */ jsx6(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text8, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
913
962
|
}
|
|
914
963
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
915
964
|
if (inputReq.promptType === "multipleChoice") {
|
|
916
|
-
return /* @__PURE__ */
|
|
965
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
917
966
|
SelectPrompt,
|
|
918
967
|
{
|
|
919
968
|
question: inputReq.prompt,
|
|
@@ -930,7 +979,7 @@ function PromptInput() {
|
|
|
930
979
|
) });
|
|
931
980
|
}
|
|
932
981
|
if (inputReq.promptType === "multiSelect") {
|
|
933
|
-
return /* @__PURE__ */
|
|
982
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
934
983
|
SelectPrompt,
|
|
935
984
|
{
|
|
936
985
|
multi: true,
|
|
@@ -945,7 +994,7 @@ function PromptInput() {
|
|
|
945
994
|
) });
|
|
946
995
|
}
|
|
947
996
|
if (inputReq.promptType === "notice") {
|
|
948
|
-
return /* @__PURE__ */
|
|
997
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
949
998
|
SelectPrompt,
|
|
950
999
|
{
|
|
951
1000
|
question: inputReq.prompt,
|
|
@@ -956,7 +1005,7 @@ function PromptInput() {
|
|
|
956
1005
|
) });
|
|
957
1006
|
}
|
|
958
1007
|
if (inputReq.promptType === "enterToContinue") {
|
|
959
|
-
return /* @__PURE__ */
|
|
1008
|
+
return /* @__PURE__ */ jsx6(
|
|
960
1009
|
EnterToContinuePrompt,
|
|
961
1010
|
{
|
|
962
1011
|
question: inputReq.prompt,
|
|
@@ -965,9 +1014,12 @@ function PromptInput() {
|
|
|
965
1014
|
}
|
|
966
1015
|
);
|
|
967
1016
|
}
|
|
1017
|
+
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1018
|
+
return /* @__PURE__ */ jsx6(CommandApproval, { command: inputReq.command, onDecide: submitInput });
|
|
1019
|
+
}
|
|
968
1020
|
if (inputReq.promptType === "acceptReject") {
|
|
969
1021
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
970
|
-
return /* @__PURE__ */
|
|
1022
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
971
1023
|
SelectPrompt,
|
|
972
1024
|
{
|
|
973
1025
|
question: inputReq.prompt,
|
|
@@ -978,15 +1030,15 @@ function PromptInput() {
|
|
|
978
1030
|
}
|
|
979
1031
|
) });
|
|
980
1032
|
}
|
|
981
|
-
return /* @__PURE__ */
|
|
982
|
-
inputReq.error && /* @__PURE__ */
|
|
983
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
985
|
-
/* @__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: [
|
|
986
1038
|
inputReq.prompt,
|
|
987
1039
|
" "
|
|
988
1040
|
] }),
|
|
989
|
-
/* @__PURE__ */
|
|
1041
|
+
/* @__PURE__ */ jsx6(
|
|
990
1042
|
TextInput,
|
|
991
1043
|
{
|
|
992
1044
|
value: draft,
|
|
@@ -1004,7 +1056,7 @@ function PromptInput() {
|
|
|
1004
1056
|
// src/ui/Welcome.tsx
|
|
1005
1057
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1006
1058
|
import { fileURLToPath } from "node:url";
|
|
1007
|
-
import { Box as
|
|
1059
|
+
import { Box as Box9, Spacer, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
1008
1060
|
|
|
1009
1061
|
// src/ui/copy/welcome.ts
|
|
1010
1062
|
var sidebarItems = [
|
|
@@ -1017,12 +1069,12 @@ var sidebarItems = [
|
|
|
1017
1069
|
description: "push 100 records to Algolia in seconds"
|
|
1018
1070
|
},
|
|
1019
1071
|
{
|
|
1020
|
-
title: "detect your
|
|
1021
|
-
description: "
|
|
1072
|
+
title: "detect your stack",
|
|
1073
|
+
description: "whatever language and framework you already use"
|
|
1022
1074
|
},
|
|
1023
1075
|
{
|
|
1024
1076
|
title: "scaffold a search UI",
|
|
1025
|
-
description: "a
|
|
1077
|
+
description: "a search box and results, wired into your app"
|
|
1026
1078
|
},
|
|
1027
1079
|
{
|
|
1028
1080
|
title: "ship it",
|
|
@@ -1032,20 +1084,20 @@ var sidebarItems = [
|
|
|
1032
1084
|
|
|
1033
1085
|
// src/ui/Welcome.tsx
|
|
1034
1086
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
1035
|
-
import { jsx as
|
|
1087
|
+
import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1036
1088
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
1037
1089
|
function SidebarItem({
|
|
1038
1090
|
title,
|
|
1039
1091
|
description
|
|
1040
1092
|
}) {
|
|
1041
|
-
return /* @__PURE__ */
|
|
1042
|
-
/* @__PURE__ */
|
|
1043
|
-
/* @__PURE__ */
|
|
1044
|
-
/* @__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 })
|
|
1045
1097
|
] }),
|
|
1046
|
-
/* @__PURE__ */
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1098
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 2, children: [
|
|
1099
|
+
/* @__PURE__ */ jsx7(Spacer, {}),
|
|
1100
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: description })
|
|
1049
1101
|
] })
|
|
1050
1102
|
] });
|
|
1051
1103
|
}
|
|
@@ -1053,7 +1105,7 @@ function Welcome() {
|
|
|
1053
1105
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
1054
1106
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
1055
1107
|
const { rows } = useWindowSize6();
|
|
1056
|
-
|
|
1108
|
+
useInput4((input, key) => {
|
|
1057
1109
|
if (key.return) confirmStart();
|
|
1058
1110
|
else if (input === "i") openLearnMore();
|
|
1059
1111
|
});
|
|
@@ -1071,16 +1123,16 @@ function Welcome() {
|
|
|
1071
1123
|
if (rows < 30) {
|
|
1072
1124
|
layout = scales["small"];
|
|
1073
1125
|
}
|
|
1074
|
-
return /* @__PURE__ */
|
|
1075
|
-
/* @__PURE__ */
|
|
1076
|
-
|
|
1126
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
1127
|
+
/* @__PURE__ */ jsx7(
|
|
1128
|
+
Box9,
|
|
1077
1129
|
{
|
|
1078
1130
|
paddingY: layout.main.padding.y,
|
|
1079
1131
|
paddingX: layout.main.padding.x,
|
|
1080
1132
|
flexDirection: "column",
|
|
1081
1133
|
justifyContent: "center",
|
|
1082
|
-
children: /* @__PURE__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
1134
|
+
children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 2, children: [
|
|
1135
|
+
/* @__PURE__ */ jsx7(InkPictureProvider, { children: /* @__PURE__ */ jsx7(
|
|
1084
1136
|
Image,
|
|
1085
1137
|
{
|
|
1086
1138
|
src: IMAGE_PATH,
|
|
@@ -1091,16 +1143,16 @@ function Welcome() {
|
|
|
1091
1143
|
protocol: "halfBlock"
|
|
1092
1144
|
}
|
|
1093
1145
|
) }),
|
|
1094
|
-
/* @__PURE__ */
|
|
1095
|
-
/* @__PURE__ */
|
|
1096
|
-
/* @__PURE__ */
|
|
1097
|
-
/* @__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" })
|
|
1098
1150
|
] })
|
|
1099
1151
|
] })
|
|
1100
1152
|
}
|
|
1101
1153
|
),
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
|
|
1154
|
+
/* @__PURE__ */ jsxs8(
|
|
1155
|
+
Box9,
|
|
1104
1156
|
{
|
|
1105
1157
|
backgroundColor: COLORS.bg.sidebar,
|
|
1106
1158
|
width: 40,
|
|
@@ -1110,8 +1162,8 @@ function Welcome() {
|
|
|
1110
1162
|
flexDirection: "column",
|
|
1111
1163
|
justifyContent: "center",
|
|
1112
1164
|
children: [
|
|
1113
|
-
/* @__PURE__ */
|
|
1114
|
-
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))
|
|
1115
1167
|
]
|
|
1116
1168
|
}
|
|
1117
1169
|
)
|
|
@@ -1120,7 +1172,7 @@ function Welcome() {
|
|
|
1120
1172
|
|
|
1121
1173
|
// src/ui/LearnMore.tsx
|
|
1122
1174
|
import { Fragment as Fragment2 } from "react";
|
|
1123
|
-
import { Box as
|
|
1175
|
+
import { Box as Box10, Text as Text10, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1124
1176
|
|
|
1125
1177
|
// src/ui/copy/learn-more.ts
|
|
1126
1178
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -1128,12 +1180,17 @@ var accessItems = [
|
|
|
1128
1180
|
{
|
|
1129
1181
|
tag: "READ",
|
|
1130
1182
|
title: "Project files",
|
|
1131
|
-
description: "reads
|
|
1183
|
+
description: "reads manifests, configs & source to detect your stack. Read-only; nothing is uploaded."
|
|
1132
1184
|
},
|
|
1133
1185
|
{
|
|
1134
1186
|
tag: "WRITE",
|
|
1135
1187
|
title: "Code changes",
|
|
1136
|
-
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."
|
|
1137
1194
|
},
|
|
1138
1195
|
{
|
|
1139
1196
|
tag: "NET",
|
|
@@ -1143,13 +1200,13 @@ var accessItems = [
|
|
|
1143
1200
|
{
|
|
1144
1201
|
tag: "KEY",
|
|
1145
1202
|
title: "Credentials",
|
|
1146
|
-
description: "
|
|
1203
|
+
description: "writes your Algolia app id and a search-only key (safe to expose) to .env in the worktree."
|
|
1147
1204
|
}
|
|
1148
1205
|
];
|
|
1149
1206
|
var neverItems = [
|
|
1150
1207
|
"Send your source code to a model or third party",
|
|
1151
1208
|
"Commit or push to git",
|
|
1152
|
-
"
|
|
1209
|
+
"Run a command you haven't approved"
|
|
1153
1210
|
];
|
|
1154
1211
|
var policyLinks = [
|
|
1155
1212
|
{ label: "Terms", url: "https://www.algolia.com/policies/terms" },
|
|
@@ -1157,10 +1214,11 @@ var policyLinks = [
|
|
|
1157
1214
|
];
|
|
1158
1215
|
|
|
1159
1216
|
// src/ui/LearnMore.tsx
|
|
1160
|
-
import { jsx as
|
|
1217
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1161
1218
|
var TAG_COLORS = {
|
|
1162
1219
|
READ: COLORS.success,
|
|
1163
1220
|
WRITE: COLORS.badge,
|
|
1221
|
+
EXEC: COLORS.danger,
|
|
1164
1222
|
NET: COLORS.accent,
|
|
1165
1223
|
KEY: COLORS.muted
|
|
1166
1224
|
};
|
|
@@ -1173,12 +1231,12 @@ function NeverLine({
|
|
|
1173
1231
|
}) {
|
|
1174
1232
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
1175
1233
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
1176
|
-
return /* @__PURE__ */
|
|
1177
|
-
/* @__PURE__ */
|
|
1234
|
+
return /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1235
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" }),
|
|
1178
1236
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
1179
|
-
segments.map((s, i) => /* @__PURE__ */
|
|
1237
|
+
segments.map((s, i) => /* @__PURE__ */ jsx8(Text10, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
1180
1238
|
" ".repeat(rightPad),
|
|
1181
|
-
/* @__PURE__ */
|
|
1239
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" })
|
|
1182
1240
|
] });
|
|
1183
1241
|
}
|
|
1184
1242
|
function LearnMore() {
|
|
@@ -1186,12 +1244,12 @@ function LearnMore() {
|
|
|
1186
1244
|
const backToHome = useWizard((s) => s.backToHome);
|
|
1187
1245
|
const { columns } = useWindowSize7();
|
|
1188
1246
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
1189
|
-
|
|
1247
|
+
useInput5((_input, key) => {
|
|
1190
1248
|
if (key.escape) backToHome();
|
|
1191
1249
|
else if (key.return) confirmStart();
|
|
1192
1250
|
});
|
|
1193
|
-
return /* @__PURE__ */
|
|
1194
|
-
|
|
1251
|
+
return /* @__PURE__ */ jsxs9(
|
|
1252
|
+
Box10,
|
|
1195
1253
|
{
|
|
1196
1254
|
flexDirection: "column",
|
|
1197
1255
|
paddingX: PADDING_X,
|
|
@@ -1199,31 +1257,31 @@ function LearnMore() {
|
|
|
1199
1257
|
width: "100%",
|
|
1200
1258
|
gap: 1,
|
|
1201
1259
|
children: [
|
|
1202
|
-
/* @__PURE__ */
|
|
1203
|
-
/* @__PURE__ */
|
|
1204
|
-
/* @__PURE__ */
|
|
1205
|
-
/* @__PURE__ */
|
|
1206
|
-
/* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1208
|
-
/* @__PURE__ */
|
|
1209
|
-
/* @__PURE__ */
|
|
1210
|
-
/* @__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}` })
|
|
1211
1269
|
] }) })
|
|
1212
1270
|
] })
|
|
1213
1271
|
] }, item.tag)) }),
|
|
1214
|
-
/* @__PURE__ */
|
|
1215
|
-
/* @__PURE__ */
|
|
1216
|
-
/* @__PURE__ */
|
|
1217
|
-
/* @__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(
|
|
1218
1276
|
NeverLine,
|
|
1219
1277
|
{
|
|
1220
1278
|
width: dividerWidth,
|
|
1221
1279
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1222
1280
|
}
|
|
1223
1281
|
),
|
|
1224
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1225
|
-
/* @__PURE__ */
|
|
1226
|
-
/* @__PURE__ */
|
|
1282
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
1283
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1284
|
+
/* @__PURE__ */ jsx8(
|
|
1227
1285
|
NeverLine,
|
|
1228
1286
|
{
|
|
1229
1287
|
width: dividerWidth,
|
|
@@ -1235,24 +1293,24 @@ function LearnMore() {
|
|
|
1235
1293
|
}
|
|
1236
1294
|
)
|
|
1237
1295
|
] }, item)),
|
|
1238
|
-
/* @__PURE__ */
|
|
1239
|
-
/* @__PURE__ */
|
|
1296
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1297
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1240
1298
|
] }),
|
|
1241
|
-
/* @__PURE__ */
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__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 })
|
|
1244
1302
|
] }, link.label)) }),
|
|
1245
|
-
/* @__PURE__ */
|
|
1246
|
-
/* @__PURE__ */
|
|
1247
|
-
/* @__PURE__ */
|
|
1248
|
-
/* @__PURE__ */
|
|
1249
|
-
/* @__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" })
|
|
1250
1308
|
] }),
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1253
|
-
/* @__PURE__ */
|
|
1254
|
-
/* @__PURE__ */
|
|
1255
|
-
/* @__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" })
|
|
1256
1314
|
] })
|
|
1257
1315
|
] })
|
|
1258
1316
|
]
|
|
@@ -1261,10 +1319,10 @@ function LearnMore() {
|
|
|
1261
1319
|
}
|
|
1262
1320
|
|
|
1263
1321
|
// src/ui/Sidebar.tsx
|
|
1264
|
-
import { Box as
|
|
1322
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1265
1323
|
|
|
1266
1324
|
// src/ui/Steps.tsx
|
|
1267
|
-
import { Box as
|
|
1325
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1268
1326
|
import Spinner from "ink-spinner";
|
|
1269
1327
|
|
|
1270
1328
|
// src/core/persistence.ts
|
|
@@ -1293,12 +1351,12 @@ async function clearWorkflowState(workflowId) {
|
|
|
1293
1351
|
}
|
|
1294
1352
|
|
|
1295
1353
|
// src/ui/Steps.tsx
|
|
1296
|
-
import { jsx as
|
|
1354
|
+
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1297
1355
|
function Steps() {
|
|
1298
1356
|
const { steps } = useWizard();
|
|
1299
1357
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1300
|
-
return /* @__PURE__ */
|
|
1301
|
-
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],
|
|
1302
1360
|
" ",
|
|
1303
1361
|
s.title
|
|
1304
1362
|
] }) }, s.id)) });
|
|
@@ -1307,27 +1365,27 @@ function CurrentStep() {
|
|
|
1307
1365
|
const { steps } = useWizard();
|
|
1308
1366
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1309
1367
|
if (!currentStep) return null;
|
|
1310
|
-
return /* @__PURE__ */
|
|
1311
|
-
/* @__PURE__ */
|
|
1368
|
+
return /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status.running, children: [
|
|
1369
|
+
/* @__PURE__ */ jsx9(Spinner, { type: "dots" }),
|
|
1312
1370
|
" ",
|
|
1313
1371
|
` ${currentStep.title}`
|
|
1314
1372
|
] });
|
|
1315
1373
|
}
|
|
1316
1374
|
|
|
1317
1375
|
// src/ui/Progress.tsx
|
|
1318
|
-
import { Box as
|
|
1319
|
-
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";
|
|
1320
1378
|
function Progress() {
|
|
1321
1379
|
const { steps, currentStepIndex } = useWizard();
|
|
1322
1380
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1323
1381
|
if (visibleSteps.length === 0) return null;
|
|
1324
1382
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1325
1383
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1326
|
-
return /* @__PURE__ */
|
|
1327
|
-
/* @__PURE__ */
|
|
1328
|
-
/* @__PURE__ */
|
|
1329
|
-
/* @__PURE__ */
|
|
1330
|
-
/* @__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 })
|
|
1331
1389
|
] });
|
|
1332
1390
|
}
|
|
1333
1391
|
|
|
@@ -1338,10 +1396,10 @@ var sidebarCommands = [
|
|
|
1338
1396
|
];
|
|
1339
1397
|
|
|
1340
1398
|
// src/ui/Sidebar.tsx
|
|
1341
|
-
import { jsx as
|
|
1399
|
+
import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1342
1400
|
function Sidebar() {
|
|
1343
|
-
return /* @__PURE__ */
|
|
1344
|
-
|
|
1401
|
+
return /* @__PURE__ */ jsxs12(
|
|
1402
|
+
Box13,
|
|
1345
1403
|
{
|
|
1346
1404
|
backgroundColor: "#14171E",
|
|
1347
1405
|
width: 30,
|
|
@@ -1350,16 +1408,16 @@ function Sidebar() {
|
|
|
1350
1408
|
flexDirection: "column",
|
|
1351
1409
|
justifyContent: "space-between",
|
|
1352
1410
|
children: [
|
|
1353
|
-
/* @__PURE__ */
|
|
1354
|
-
/* @__PURE__ */
|
|
1355
|
-
/* @__PURE__ */
|
|
1411
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1412
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1413
|
+
/* @__PURE__ */ jsx11(Steps, {})
|
|
1356
1414
|
] }),
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
/* @__PURE__ */
|
|
1360
|
-
return /* @__PURE__ */
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__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 })
|
|
1363
1421
|
] });
|
|
1364
1422
|
}) })
|
|
1365
1423
|
] })
|
|
@@ -1369,12 +1427,12 @@ function Sidebar() {
|
|
|
1369
1427
|
}
|
|
1370
1428
|
|
|
1371
1429
|
// src/ui/Ribbon.tsx
|
|
1372
|
-
import { Box as
|
|
1373
|
-
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";
|
|
1374
1432
|
function Ribbon() {
|
|
1375
1433
|
const firstCommand = sidebarCommands[0];
|
|
1376
|
-
return /* @__PURE__ */
|
|
1377
|
-
|
|
1434
|
+
return /* @__PURE__ */ jsxs13(
|
|
1435
|
+
Box14,
|
|
1378
1436
|
{
|
|
1379
1437
|
backgroundColor: "#14171E",
|
|
1380
1438
|
flexDirection: "row",
|
|
@@ -1382,11 +1440,11 @@ function Ribbon() {
|
|
|
1382
1440
|
paddingX: 2,
|
|
1383
1441
|
paddingY: 1,
|
|
1384
1442
|
children: [
|
|
1385
|
-
/* @__PURE__ */
|
|
1386
|
-
/* @__PURE__ */
|
|
1387
|
-
/* @__PURE__ */
|
|
1388
|
-
/* @__PURE__ */
|
|
1389
|
-
/* @__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 })
|
|
1390
1448
|
] })
|
|
1391
1449
|
]
|
|
1392
1450
|
}
|
|
@@ -1397,8 +1455,8 @@ function Ribbon() {
|
|
|
1397
1455
|
import { useState as useState6 } from "react";
|
|
1398
1456
|
|
|
1399
1457
|
// src/ui/Logs.tsx
|
|
1400
|
-
import { Box as
|
|
1401
|
-
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";
|
|
1402
1460
|
var KIND_COLOR = {
|
|
1403
1461
|
tool: COLORS.primary,
|
|
1404
1462
|
prompt: COLORS.badge
|
|
@@ -1429,14 +1487,14 @@ function formatTimestamp(ms) {
|
|
|
1429
1487
|
function Logs() {
|
|
1430
1488
|
const logs = useWizard((s) => s.logs);
|
|
1431
1489
|
const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
|
|
1432
|
-
|
|
1490
|
+
useInput6((_input, key) => {
|
|
1433
1491
|
if (key.upArrow) scroll.scrollBy(-1);
|
|
1434
1492
|
else if (key.downArrow) scroll.scrollBy(1);
|
|
1435
1493
|
});
|
|
1436
1494
|
const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
1437
|
-
return /* @__PURE__ */
|
|
1438
|
-
logs.length === 0 && /* @__PURE__ */
|
|
1439
|
-
/* @__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) => {
|
|
1440
1498
|
const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
|
|
1441
1499
|
const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
|
|
1442
1500
|
const rawPreview = rawInputText(entry.input);
|
|
@@ -1446,14 +1504,14 @@ function Logs() {
|
|
|
1446
1504
|
const name = truncate2(entry.name, budget);
|
|
1447
1505
|
budget -= name.length;
|
|
1448
1506
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1449
|
-
return /* @__PURE__ */
|
|
1450
|
-
/* @__PURE__ */
|
|
1451
|
-
/* @__PURE__ */
|
|
1452
|
-
preview && /* @__PURE__ */
|
|
1453
|
-
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 })
|
|
1454
1512
|
] }, entry.id);
|
|
1455
1513
|
}) }),
|
|
1456
|
-
/* @__PURE__ */
|
|
1514
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1457
1515
|
] });
|
|
1458
1516
|
}
|
|
1459
1517
|
|
|
@@ -1645,7 +1703,7 @@ function track(event, payload) {
|
|
|
1645
1703
|
}
|
|
1646
1704
|
|
|
1647
1705
|
// src/ui/App.tsx
|
|
1648
|
-
import { jsx as
|
|
1706
|
+
import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1649
1707
|
function App() {
|
|
1650
1708
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1651
1709
|
const { exit } = useApp();
|
|
@@ -1653,7 +1711,7 @@ function App() {
|
|
|
1653
1711
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1654
1712
|
const finished = phase === "done" || phase === "error";
|
|
1655
1713
|
const currentStep = steps[currentStepIndex];
|
|
1656
|
-
|
|
1714
|
+
useInput7(
|
|
1657
1715
|
(_input, key) => {
|
|
1658
1716
|
if (key.return) {
|
|
1659
1717
|
exit();
|
|
@@ -1661,7 +1719,7 @@ function App() {
|
|
|
1661
1719
|
},
|
|
1662
1720
|
{ isActive: finished }
|
|
1663
1721
|
);
|
|
1664
|
-
|
|
1722
|
+
useInput7((_input, key) => {
|
|
1665
1723
|
if (phase === "idle" || phase === "authenticating") return;
|
|
1666
1724
|
if (key.tab) {
|
|
1667
1725
|
setShowLogs(!showLogs);
|
|
@@ -1672,8 +1730,8 @@ function App() {
|
|
|
1672
1730
|
});
|
|
1673
1731
|
}
|
|
1674
1732
|
});
|
|
1675
|
-
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1676
|
-
|
|
1733
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
|
|
1734
|
+
useInput7((_input, key) => {
|
|
1677
1735
|
if (escOwnedElsewhere) return;
|
|
1678
1736
|
if (key.escape) {
|
|
1679
1737
|
track("AI Wizard Interaction", {
|
|
@@ -1692,8 +1750,8 @@ function App() {
|
|
|
1692
1750
|
/* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
|
|
1693
1751
|
the whole screen, and the scrolling throws off its cursor arithmetic —
|
|
1694
1752
|
flicker and leftover rows. */
|
|
1695
|
-
/* @__PURE__ */
|
|
1696
|
-
|
|
1753
|
+
/* @__PURE__ */ jsxs15(
|
|
1754
|
+
Box16,
|
|
1697
1755
|
{
|
|
1698
1756
|
backgroundColor: COLORS.bg.main,
|
|
1699
1757
|
flexDirection: "row",
|
|
@@ -1701,16 +1759,16 @@ function App() {
|
|
|
1701
1759
|
height: scrollsPastViewport ? void 0 : rows,
|
|
1702
1760
|
overflow: scrollsPastViewport ? "visible" : "hidden",
|
|
1703
1761
|
children: [
|
|
1704
|
-
mainWindowVisible && /* @__PURE__ */
|
|
1705
|
-
|
|
1762
|
+
mainWindowVisible && /* @__PURE__ */ jsxs15(
|
|
1763
|
+
Box16,
|
|
1706
1764
|
{
|
|
1707
1765
|
flexDirection,
|
|
1708
1766
|
width: "100%",
|
|
1709
1767
|
maxHeight: rows,
|
|
1710
1768
|
justifyContent: "space-between",
|
|
1711
1769
|
children: [
|
|
1712
|
-
showLogs ? /* @__PURE__ */
|
|
1713
|
-
|
|
1770
|
+
showLogs ? /* @__PURE__ */ jsx14(Logs, {}) : /* @__PURE__ */ jsxs15(
|
|
1771
|
+
Box16,
|
|
1714
1772
|
{
|
|
1715
1773
|
flexDirection: "column",
|
|
1716
1774
|
paddingX: 4,
|
|
@@ -1718,26 +1776,26 @@ function App() {
|
|
|
1718
1776
|
width: showSidebar ? 70 : "100%",
|
|
1719
1777
|
flexGrow: 1,
|
|
1720
1778
|
children: [
|
|
1721
|
-
phase === "authenticating" && /* @__PURE__ */
|
|
1722
|
-
/* @__PURE__ */
|
|
1723
|
-
/* @__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." })
|
|
1724
1782
|
] }),
|
|
1725
|
-
/* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
/* @__PURE__ */
|
|
1728
|
-
phase === "running" && showSidebar && /* @__PURE__ */
|
|
1729
|
-
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: [
|
|
1730
1788
|
"\u2716 ",
|
|
1731
1789
|
error
|
|
1732
1790
|
] }) })
|
|
1733
1791
|
]
|
|
1734
1792
|
}
|
|
1735
1793
|
),
|
|
1736
|
-
showSidebar ? /* @__PURE__ */
|
|
1794
|
+
showSidebar ? /* @__PURE__ */ jsx14(Sidebar, {}) : /* @__PURE__ */ jsx14(Ribbon, {})
|
|
1737
1795
|
]
|
|
1738
1796
|
}
|
|
1739
1797
|
),
|
|
1740
|
-
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */
|
|
1798
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx14(LearnMore, {}) : /* @__PURE__ */ jsx14(Welcome, {}))
|
|
1741
1799
|
]
|
|
1742
1800
|
}
|
|
1743
1801
|
)
|
|
@@ -1817,7 +1875,7 @@ async function ensureConsent() {
|
|
|
1817
1875
|
if (config.aiConsent) return;
|
|
1818
1876
|
const store = useWizard.getState();
|
|
1819
1877
|
const answer = await store.requestUserInput({
|
|
1820
|
-
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.",
|
|
1821
1879
|
promptType: "enterToContinue",
|
|
1822
1880
|
options: []
|
|
1823
1881
|
});
|
|
@@ -2631,11 +2689,19 @@ import z14 from "zod";
|
|
|
2631
2689
|
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2632
2690
|
import { join as join7 } from "node:path";
|
|
2633
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
|
+
]);
|
|
2634
2701
|
async function walkFiles(dir) {
|
|
2635
|
-
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2636
2702
|
const out = [];
|
|
2637
2703
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2638
|
-
if (e.name.startsWith(".") ||
|
|
2704
|
+
if (e.name.startsWith(".") || SKIP_DIRS.has(e.name)) continue;
|
|
2639
2705
|
const full = join7(dir, e.name);
|
|
2640
2706
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2641
2707
|
else if (e.isFile()) out.push(full);
|
|
@@ -2689,92 +2755,188 @@ function searchFilesTool(ctx) {
|
|
|
2689
2755
|
});
|
|
2690
2756
|
}
|
|
2691
2757
|
|
|
2692
|
-
// src/lib/tools/
|
|
2758
|
+
// src/lib/tools/runShell.ts
|
|
2693
2759
|
import { tool as tool8 } from "ai";
|
|
2694
2760
|
import z15 from "zod";
|
|
2761
|
+
import { relative as relative2 } from "node:path";
|
|
2695
2762
|
|
|
2696
|
-
// src/lib/tools/utils/
|
|
2763
|
+
// src/lib/tools/utils/runShell.ts
|
|
2697
2764
|
import { spawn as spawn2 } from "node:child_process";
|
|
2698
|
-
|
|
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();
|
|
2699
2815
|
return new Promise((resolve4) => {
|
|
2700
2816
|
let output = "";
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
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 }
|
|
2704
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);
|
|
2705
2845
|
child.stdout?.on("data", (d) => output += d);
|
|
2706
2846
|
child.stderr?.on("data", (d) => output += d);
|
|
2707
|
-
child.on(
|
|
2708
|
-
|
|
2709
|
-
(
|
|
2710
|
-
);
|
|
2711
|
-
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));
|
|
2712
2852
|
});
|
|
2713
2853
|
}
|
|
2714
2854
|
|
|
2715
|
-
// src/lib/tools/
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2731
|
-
}
|
|
2732
|
-
function packageManagerFromLockfile(cwd) {
|
|
2733
|
-
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2734
|
-
}
|
|
2735
|
-
async function detectPackageManager(cwd) {
|
|
2736
|
-
try {
|
|
2737
|
-
const pkg = await readPackageJson(cwd);
|
|
2738
|
-
if (pkg.packageManager) return packageManagerFrom(pkg);
|
|
2739
|
-
} catch {
|
|
2740
|
-
}
|
|
2741
|
-
return packageManagerFromLockfile(cwd) ?? "npm";
|
|
2742
|
-
}
|
|
2743
|
-
|
|
2744
|
-
// src/lib/tools/repoVerification.ts
|
|
2745
|
-
var VERIFICATION_SCRIPT_CANDIDATES = ["lint", "typecheck", "check"];
|
|
2746
|
-
async function runRepoVerificationCheck() {
|
|
2747
|
-
let pkg;
|
|
2748
|
-
try {
|
|
2749
|
-
pkg = await readPackageJson();
|
|
2750
|
-
} catch (err) {
|
|
2751
|
-
const limitation = `Could not read package.json to detect verification conventions: ${err.message}`;
|
|
2752
|
-
return { ok: false, checks: [], limitation };
|
|
2753
|
-
}
|
|
2754
|
-
const scripts = pkg.scripts ?? {};
|
|
2755
|
-
const present = VERIFICATION_SCRIPT_CANDIDATES.filter((s) => s in scripts);
|
|
2756
|
-
if (present.length === 0) {
|
|
2757
|
-
const limitation = `No verification script found in package.json (looked for: ${VERIFICATION_SCRIPT_CANDIDATES.join(", ")}).`;
|
|
2758
|
-
return { ok: false, checks: [], limitation };
|
|
2759
|
-
}
|
|
2760
|
-
const pm = await detectPackageManager(process.cwd());
|
|
2761
|
-
const checks = [];
|
|
2762
|
-
for (const script of present) {
|
|
2763
|
-
const command = `${pm} run ${script}`;
|
|
2764
|
-
const { code, output } = await runCommand(pm, ["run", script]);
|
|
2765
|
-
checks.push({ command, exitCode: code, ok: code === 0, output: output.trim() });
|
|
2766
|
-
}
|
|
2767
|
-
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
|
+
};
|
|
2768
2870
|
}
|
|
2769
|
-
|
|
2770
|
-
// src/lib/tools/verifyImplementation.ts
|
|
2771
|
-
function verifyImplementationTool() {
|
|
2871
|
+
function runShellTool(ctx) {
|
|
2772
2872
|
return tool8({
|
|
2773
|
-
description: "Run the
|
|
2774
|
-
inputSchema: z15.object(
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
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
|
+
};
|
|
2778
2940
|
}
|
|
2779
2941
|
});
|
|
2780
2942
|
}
|
|
@@ -2865,7 +3027,7 @@ function generateRecordTool(ctx) {
|
|
|
2865
3027
|
return {
|
|
2866
3028
|
filePath: relPath,
|
|
2867
3029
|
count: records.length,
|
|
2868
|
-
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.`
|
|
2869
3031
|
};
|
|
2870
3032
|
} catch (err) {
|
|
2871
3033
|
return `Error generating records: ${err.message}`;
|
|
@@ -2893,22 +3055,6 @@ function notifyUserTool() {
|
|
|
2893
3055
|
});
|
|
2894
3056
|
}
|
|
2895
3057
|
|
|
2896
|
-
// src/lib/tools/context.ts
|
|
2897
|
-
var DEFAULT_TOOL_LIMITS = {
|
|
2898
|
-
list: 10,
|
|
2899
|
-
search: 10,
|
|
2900
|
-
read: 20,
|
|
2901
|
-
match: 100
|
|
2902
|
-
};
|
|
2903
|
-
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd()) {
|
|
2904
|
-
return {
|
|
2905
|
-
root: cwd,
|
|
2906
|
-
cwd,
|
|
2907
|
-
limits,
|
|
2908
|
-
counts: { list: 0, search: 0, read: 0 }
|
|
2909
|
-
};
|
|
2910
|
-
}
|
|
2911
|
-
|
|
2912
3058
|
// src/lib/tools/index.ts
|
|
2913
3059
|
function withLogging(name, def) {
|
|
2914
3060
|
const execute = def.execute;
|
|
@@ -2940,10 +3086,7 @@ function createTools(ctx, { output, tools }) {
|
|
|
2940
3086
|
writeCredentialsTool(ctx)
|
|
2941
3087
|
),
|
|
2942
3088
|
searchFiles: withLogging("searchFiles", searchFilesTool(ctx)),
|
|
2943
|
-
|
|
2944
|
-
"verifyImplementation",
|
|
2945
|
-
verifyImplementationTool()
|
|
2946
|
-
),
|
|
3089
|
+
runShell: withLogging("runShell", runShellTool(ctx)),
|
|
2947
3090
|
generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
|
|
2948
3091
|
notifyUser: withLogging("notifyUser", notifyUserTool())
|
|
2949
3092
|
};
|
|
@@ -2978,7 +3121,7 @@ async function runAgent(req) {
|
|
|
2978
3121
|
baseURL: PROXY_BASE_URL,
|
|
2979
3122
|
fetch: proxyFetch
|
|
2980
3123
|
});
|
|
2981
|
-
const toolContext = createToolContext();
|
|
3124
|
+
const toolContext = req.toolContext ?? createToolContext();
|
|
2982
3125
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
2983
3126
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
2984
3127
|
const instructions = [
|
|
@@ -3043,7 +3186,11 @@ async function runAgent(req) {
|
|
|
3043
3186
|
"runAgent finished"
|
|
3044
3187
|
);
|
|
3045
3188
|
logger.info(
|
|
3046
|
-
{
|
|
3189
|
+
{
|
|
3190
|
+
counts: toolContext.counts,
|
|
3191
|
+
limits: toolContext.limits,
|
|
3192
|
+
commandsRun: toolContext.shell.executions.length
|
|
3193
|
+
},
|
|
3047
3194
|
"tool usage"
|
|
3048
3195
|
);
|
|
3049
3196
|
const toolResults = await stream.toolResults;
|
|
@@ -3069,8 +3216,8 @@ var detectLanguageSchema = z20.object({
|
|
|
3069
3216
|
var detectLanguage = () => runAgent({
|
|
3070
3217
|
instructions: [
|
|
3071
3218
|
"Analyze the codebase and determine the programming languages and frameworks used",
|
|
3072
|
-
"If a superset language is found, exclude the subset language.
|
|
3073
|
-
"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).",
|
|
3074
3221
|
"Return the exact version",
|
|
3075
3222
|
"Exclude things like CSS frameworks, build tools, or testing frameworks",
|
|
3076
3223
|
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
@@ -3168,7 +3315,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3168
3315
|
// package.json
|
|
3169
3316
|
var package_default = {
|
|
3170
3317
|
name: "@algolia/wizard",
|
|
3171
|
-
version: "0.9.0-rc.
|
|
3318
|
+
version: "0.9.0-rc.93.108",
|
|
3172
3319
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3173
3320
|
type: "module",
|
|
3174
3321
|
engines: {
|
|
@@ -3317,9 +3464,11 @@ var CURATED_FRAMEWORKS = [
|
|
|
3317
3464
|
"Next.js",
|
|
3318
3465
|
"React",
|
|
3319
3466
|
"Vue",
|
|
3320
|
-
"
|
|
3321
|
-
"
|
|
3322
|
-
"
|
|
3467
|
+
"Vanilla JS",
|
|
3468
|
+
"Django",
|
|
3469
|
+
"Laravel",
|
|
3470
|
+
"Rails",
|
|
3471
|
+
"Symfony"
|
|
3323
3472
|
];
|
|
3324
3473
|
var OTHER_OPTION = "Other";
|
|
3325
3474
|
var normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -3332,12 +3481,15 @@ var FRAMEWORK_ALIASES = {
|
|
|
3332
3481
|
vuejs: "vue",
|
|
3333
3482
|
angular: "angular",
|
|
3334
3483
|
angularjs: "angular",
|
|
3335
|
-
svelte: "svelte",
|
|
3336
|
-
sveltekit: "svelte",
|
|
3337
3484
|
vanillajs: "vanillajs",
|
|
3338
3485
|
vanilla: "vanillajs",
|
|
3339
3486
|
javascript: "vanillajs",
|
|
3340
|
-
js: "vanillajs"
|
|
3487
|
+
js: "vanillajs",
|
|
3488
|
+
django: "django",
|
|
3489
|
+
laravel: "laravel",
|
|
3490
|
+
rails: "rails",
|
|
3491
|
+
rubyonrails: "rails",
|
|
3492
|
+
symfony: "symfony"
|
|
3341
3493
|
};
|
|
3342
3494
|
var isSameFramework = (a, b) => {
|
|
3343
3495
|
const x = FRAMEWORK_ALIASES[normalize(a)] ?? normalize(a);
|
|
@@ -3571,16 +3723,9 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3571
3723
|
import z27 from "zod";
|
|
3572
3724
|
|
|
3573
3725
|
// src/lib/worktree.ts
|
|
3574
|
-
import { execFile
|
|
3575
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3576
|
-
import {
|
|
3577
|
-
basename as basename2,
|
|
3578
|
-
dirname as dirname7,
|
|
3579
|
-
isAbsolute as isAbsolute2,
|
|
3580
|
-
join as join9,
|
|
3581
|
-
relative as relative2,
|
|
3582
|
-
resolve as resolve3
|
|
3583
|
-
} 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";
|
|
3584
3729
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
3585
3730
|
var MAX_WIZARD_WORKTREES = 3;
|
|
3586
3731
|
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
@@ -3611,7 +3756,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3611
3756
|
return out.trim().length > 0;
|
|
3612
3757
|
}
|
|
3613
3758
|
async function pruneOldWorktrees(repoRoot) {
|
|
3614
|
-
const dir =
|
|
3759
|
+
const dir = join8(stateDir(repoRoot), "worktrees");
|
|
3615
3760
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3616
3761
|
for (const slug of stale) {
|
|
3617
3762
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3622,7 +3767,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3622
3767
|
"worktree",
|
|
3623
3768
|
"remove",
|
|
3624
3769
|
"--force",
|
|
3625
|
-
|
|
3770
|
+
join8(dir, slug)
|
|
3626
3771
|
]);
|
|
3627
3772
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3628
3773
|
} catch (err) {
|
|
@@ -3636,113 +3781,13 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3636
3781
|
async function createWorktree(repoRoot) {
|
|
3637
3782
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3638
3783
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3639
|
-
const path =
|
|
3784
|
+
const path = join8(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3640
3785
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3641
3786
|
await pruneOldWorktrees(repoRoot);
|
|
3642
3787
|
await mkdir6(dirname7(path), { recursive: true });
|
|
3643
3788
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3644
3789
|
return { path, branch };
|
|
3645
3790
|
}
|
|
3646
|
-
async function installWorktreeDeps(worktreePath) {
|
|
3647
|
-
try {
|
|
3648
|
-
await readPackageJson(worktreePath);
|
|
3649
|
-
} catch {
|
|
3650
|
-
return { ok: true, output: "no package.json; skipped install" };
|
|
3651
|
-
}
|
|
3652
|
-
const pm = await detectPackageManager(worktreePath);
|
|
3653
|
-
return new Promise((resolve4) => {
|
|
3654
|
-
let output = "";
|
|
3655
|
-
const child = spawn3(pm, ["install"], {
|
|
3656
|
-
cwd: worktreePath,
|
|
3657
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3658
|
-
});
|
|
3659
|
-
child.stdout?.on("data", (d) => output += d);
|
|
3660
|
-
child.stderr?.on("data", (d) => output += d);
|
|
3661
|
-
child.on(
|
|
3662
|
-
"error",
|
|
3663
|
-
(err) => resolve4({
|
|
3664
|
-
ok: false,
|
|
3665
|
-
output: `Failed to run ${pm} install: ${err.message}`
|
|
3666
|
-
})
|
|
3667
|
-
);
|
|
3668
|
-
child.on(
|
|
3669
|
-
"close",
|
|
3670
|
-
(code) => resolve4({ ok: code === 0, output: output.trim() })
|
|
3671
|
-
);
|
|
3672
|
-
});
|
|
3673
|
-
}
|
|
3674
|
-
var INGEST_RUNTIMES = ["node", "python", "python3", "bun"];
|
|
3675
|
-
function validateIngestEntrypoint(worktreePath, entrypoint) {
|
|
3676
|
-
if (!entrypoint || entrypoint.startsWith("-")) {
|
|
3677
|
-
return {
|
|
3678
|
-
ok: false,
|
|
3679
|
-
reason: `entrypoint "${entrypoint}" is not a plain file path`
|
|
3680
|
-
};
|
|
3681
|
-
}
|
|
3682
|
-
const target = resolve3(worktreePath, entrypoint);
|
|
3683
|
-
const rel = relative2(worktreePath, target);
|
|
3684
|
-
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
3685
|
-
return {
|
|
3686
|
-
ok: false,
|
|
3687
|
-
reason: `entrypoint "${entrypoint}" resolves outside the worktree`
|
|
3688
|
-
};
|
|
3689
|
-
}
|
|
3690
|
-
return { ok: true, target };
|
|
3691
|
-
}
|
|
3692
|
-
async function runIngestScript(worktreePath, runtime, entrypoint, env = {}) {
|
|
3693
|
-
if (!INGEST_RUNTIMES.includes(runtime)) {
|
|
3694
|
-
return {
|
|
3695
|
-
ran: false,
|
|
3696
|
-
ok: false,
|
|
3697
|
-
output: "",
|
|
3698
|
-
reason: `runtime "${runtime}" is not an allowed interpreter (${INGEST_RUNTIMES.join(", ")})`
|
|
3699
|
-
};
|
|
3700
|
-
}
|
|
3701
|
-
const validated = validateIngestEntrypoint(worktreePath, entrypoint);
|
|
3702
|
-
if (!validated.ok) {
|
|
3703
|
-
return { ran: false, ok: false, output: "", reason: validated.reason };
|
|
3704
|
-
}
|
|
3705
|
-
try {
|
|
3706
|
-
if (!(await stat2(validated.target)).isFile()) {
|
|
3707
|
-
return {
|
|
3708
|
-
ran: false,
|
|
3709
|
-
ok: false,
|
|
3710
|
-
output: "",
|
|
3711
|
-
reason: `entrypoint "${entrypoint}" is not a file`
|
|
3712
|
-
};
|
|
3713
|
-
}
|
|
3714
|
-
} catch {
|
|
3715
|
-
return {
|
|
3716
|
-
ran: false,
|
|
3717
|
-
ok: false,
|
|
3718
|
-
output: "",
|
|
3719
|
-
reason: `entrypoint "${entrypoint}" does not exist`
|
|
3720
|
-
};
|
|
3721
|
-
}
|
|
3722
|
-
return new Promise((resolveRun) => {
|
|
3723
|
-
let output = "";
|
|
3724
|
-
const child = spawn3(runtime, [entrypoint], {
|
|
3725
|
-
cwd: worktreePath,
|
|
3726
|
-
shell: false,
|
|
3727
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3728
|
-
env: { ...process.env, ...env }
|
|
3729
|
-
});
|
|
3730
|
-
child.stdout?.on("data", (d) => output += d);
|
|
3731
|
-
child.stderr?.on("data", (d) => output += d);
|
|
3732
|
-
child.on(
|
|
3733
|
-
"error",
|
|
3734
|
-
(err) => resolveRun({
|
|
3735
|
-
ran: true,
|
|
3736
|
-
ok: false,
|
|
3737
|
-
output: `Failed to run ${runtime} ${entrypoint}: ${err.message}`
|
|
3738
|
-
})
|
|
3739
|
-
);
|
|
3740
|
-
child.on(
|
|
3741
|
-
"close",
|
|
3742
|
-
(code) => resolveRun({ ran: true, ok: code === 0, output: output.trim() })
|
|
3743
|
-
);
|
|
3744
|
-
});
|
|
3745
|
-
}
|
|
3746
3791
|
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
3747
3792
|
const trimmed = sourcePath.trim();
|
|
3748
3793
|
if (!trimmed) {
|
|
@@ -3756,8 +3801,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3756
3801
|
} catch {
|
|
3757
3802
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3758
3803
|
}
|
|
3759
|
-
const relPath =
|
|
3760
|
-
const dest =
|
|
3804
|
+
const relPath = join8(ingestDir, basename2(source));
|
|
3805
|
+
const dest = join8(worktreePath, relPath);
|
|
3761
3806
|
try {
|
|
3762
3807
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3763
3808
|
await copyFile(source, dest);
|
|
@@ -3775,7 +3820,7 @@ function hasEnvVar(content, name) {
|
|
|
3775
3820
|
async function readEnvVar(worktreePath, name) {
|
|
3776
3821
|
let content;
|
|
3777
3822
|
try {
|
|
3778
|
-
content = await
|
|
3823
|
+
content = await readFile6(join8(worktreePath, ".env"), "utf8");
|
|
3779
3824
|
} catch (err) {
|
|
3780
3825
|
if (err.code !== "ENOENT") throw err;
|
|
3781
3826
|
return void 0;
|
|
@@ -3790,10 +3835,10 @@ async function readEnvVar(worktreePath, name) {
|
|
|
3790
3835
|
return value;
|
|
3791
3836
|
}
|
|
3792
3837
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3793
|
-
const target =
|
|
3838
|
+
const target = join8(worktreePath, ".env");
|
|
3794
3839
|
let existing = "";
|
|
3795
3840
|
try {
|
|
3796
|
-
existing = await
|
|
3841
|
+
existing = await readFile6(target, "utf8");
|
|
3797
3842
|
} catch (err) {
|
|
3798
3843
|
if (err.code !== "ENOENT") throw err;
|
|
3799
3844
|
}
|
|
@@ -3862,15 +3907,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3862
3907
|
}
|
|
3863
3908
|
|
|
3864
3909
|
// src/lib/algoliaDocs.ts
|
|
3865
|
-
import { readFileSync, readdirSync, existsSync
|
|
3866
|
-
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";
|
|
3867
3912
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3868
|
-
var DOCS_SUBPATH =
|
|
3913
|
+
var DOCS_SUBPATH = join9("docs", "algolia-sdk");
|
|
3869
3914
|
function findDocsDir() {
|
|
3870
3915
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3871
3916
|
for (; ; ) {
|
|
3872
|
-
const candidate =
|
|
3873
|
-
if (
|
|
3917
|
+
const candidate = join9(dir, DOCS_SUBPATH);
|
|
3918
|
+
if (existsSync(candidate)) return candidate;
|
|
3874
3919
|
const parent = dirname8(dir);
|
|
3875
3920
|
if (parent === dir) return void 0;
|
|
3876
3921
|
dir = parent;
|
|
@@ -3892,7 +3937,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3892
3937
|
);
|
|
3893
3938
|
return "";
|
|
3894
3939
|
}
|
|
3895
|
-
return readFileSync(
|
|
3940
|
+
return readFileSync(join9(docsDir, files[0]), "utf8").trim();
|
|
3896
3941
|
}
|
|
3897
3942
|
function getNamedDoc(name, language) {
|
|
3898
3943
|
const docsDir = findDocsDir();
|
|
@@ -3900,14 +3945,15 @@ function getNamedDoc(name, language) {
|
|
|
3900
3945
|
logger.warn("docs/algolia-sdk not found");
|
|
3901
3946
|
return "";
|
|
3902
3947
|
}
|
|
3903
|
-
const file =
|
|
3904
|
-
if (!
|
|
3948
|
+
const file = join9(docsDir, `${name}-${language}.md`);
|
|
3949
|
+
if (!existsSync(file)) {
|
|
3905
3950
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3906
3951
|
return "";
|
|
3907
3952
|
}
|
|
3908
3953
|
return readFileSync(file, "utf8").trim();
|
|
3909
3954
|
}
|
|
3910
3955
|
function getFrameworkSpecificDoc(frameworks) {
|
|
3956
|
+
if (frameworks.length === 0) return "";
|
|
3911
3957
|
const fw = frameworks.map((f) => f.toLowerCase());
|
|
3912
3958
|
if (fw.includes("vue") || fw.includes("nuxt")) {
|
|
3913
3959
|
return loadAlgoliaDoc("vue");
|
|
@@ -3915,9 +3961,6 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
3915
3961
|
if (fw.includes("react") || fw.includes("next.js")) {
|
|
3916
3962
|
return loadAlgoliaDoc("react");
|
|
3917
3963
|
}
|
|
3918
|
-
if (fw.includes("angular")) {
|
|
3919
|
-
return loadAlgoliaDoc("angular");
|
|
3920
|
-
}
|
|
3921
3964
|
return loadAlgoliaDoc("js");
|
|
3922
3965
|
}
|
|
3923
3966
|
|
|
@@ -3945,11 +3988,7 @@ var implementSchema = z27.object({
|
|
|
3945
3988
|
});
|
|
3946
3989
|
var implementationOutputSchema = z27.object({
|
|
3947
3990
|
summary: z27.string(),
|
|
3948
|
-
|
|
3949
|
-
// free-form command string. `runtime` is allowlisted and `entrypoint` is
|
|
3950
|
-
// validated worktree-relative, so the agent cannot inject extra commands.
|
|
3951
|
-
runtime: z27.enum(INGEST_RUNTIMES).optional(),
|
|
3952
|
-
entrypoint: z27.string().optional()
|
|
3991
|
+
ingestCommand: z27.string().optional()
|
|
3953
3992
|
});
|
|
3954
3993
|
var verificationOutputSchema = z27.object({
|
|
3955
3994
|
summary: z27.string(),
|
|
@@ -3959,30 +3998,35 @@ var verificationOutputSchema = z27.object({
|
|
|
3959
3998
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3960
3999
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
3961
4000
|
var INGEST_DIR = ".algolia-wizard";
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
if (names.some((n) => n.includes("react") || n.includes("next")))
|
|
3966
|
-
return "React";
|
|
3967
|
-
if (names.some((n) => n.includes("angular"))) return "Angular";
|
|
3968
|
-
return "JavaScript";
|
|
3969
|
-
}
|
|
3970
|
-
function frameworksForDoc(framework) {
|
|
3971
|
-
switch (framework) {
|
|
3972
|
-
case "React":
|
|
3973
|
-
return ["react"];
|
|
3974
|
-
case "Vue":
|
|
3975
|
-
return ["vue"];
|
|
3976
|
-
case "Angular":
|
|
3977
|
-
return ["angular"];
|
|
3978
|
-
case "JavaScript":
|
|
3979
|
-
return [];
|
|
3980
|
-
}
|
|
4001
|
+
var JS_LANGUAGES = ["javascript", "typescript", "jsx", "tsx", "node"];
|
|
4002
|
+
function lower(entries) {
|
|
4003
|
+
return entries.map((entry) => entry.name.toLowerCase());
|
|
3981
4004
|
}
|
|
3982
|
-
function
|
|
3983
|
-
|
|
3984
|
-
(
|
|
4005
|
+
function isJsProject(language) {
|
|
4006
|
+
return lower(language.languages).some(
|
|
4007
|
+
(name) => JS_LANGUAGES.some((js) => name.includes(js))
|
|
3985
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);
|
|
3986
4030
|
if (frameworkNames.some((name) => name.includes("next"))) {
|
|
3987
4031
|
return "NEXT_PUBLIC_";
|
|
3988
4032
|
}
|
|
@@ -3995,7 +4039,7 @@ function publicEnvPrefix(language) {
|
|
|
3995
4039
|
if (frameworkNames.some((name) => name.includes("vite"))) {
|
|
3996
4040
|
return "VITE_";
|
|
3997
4041
|
}
|
|
3998
|
-
return "PUBLIC_";
|
|
4042
|
+
return isJsProject(language) ? "PUBLIC_" : "";
|
|
3999
4043
|
}
|
|
4000
4044
|
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
4001
4045
|
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
@@ -4034,7 +4078,10 @@ function baseInstructions(input) {
|
|
|
4034
4078
|
// index-scoped keys then reject with a 403.
|
|
4035
4079
|
`Target Algolia index, to be used exactly as written \u2014 never renamed, re-cased, prefixed, or suffixed: "${input.targetIndex}"`,
|
|
4036
4080
|
`Project languages and frameworks: ${JSON.stringify(input.language)}`,
|
|
4037
|
-
"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."
|
|
4038
4085
|
];
|
|
4039
4086
|
}
|
|
4040
4087
|
function sourceSpecificInstructions(input) {
|
|
@@ -4054,12 +4101,21 @@ function sourceSpecificInstructions(input) {
|
|
|
4054
4101
|
generated: [
|
|
4055
4102
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
4056
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.",
|
|
4057
|
-
"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.",
|
|
4058
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."
|
|
4059
4106
|
]
|
|
4060
4107
|
};
|
|
4061
4108
|
return byLine[input.ingestionSource];
|
|
4062
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
|
+
}
|
|
4063
4119
|
function ingestionInstructions(input) {
|
|
4064
4120
|
return [
|
|
4065
4121
|
...input.confirmed && input.confirmed.length ? [
|
|
@@ -4067,25 +4123,30 @@ function ingestionInstructions(input) {
|
|
|
4067
4123
|
`Ingest only these confirmed entities (name, source paths, attributes): ${JSON.stringify(input.confirmed)}.`,
|
|
4068
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.`,
|
|
4069
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.`,
|
|
4070
|
-
"
|
|
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.",
|
|
4071
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.",
|
|
4072
|
-
|
|
4073
|
-
'
|
|
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.',
|
|
4074
4131
|
"The summary should be extremely concise.",
|
|
4075
|
-
`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.`,
|
|
4076
4132
|
...sourceSpecificInstructions(input)
|
|
4077
4133
|
] : []
|
|
4078
4134
|
];
|
|
4079
4135
|
}
|
|
4080
4136
|
function searchInstructions(input) {
|
|
4081
|
-
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.
|
|
4137
|
+
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.language));
|
|
4082
4138
|
return [
|
|
4083
4139
|
"Implement an in-app Algolia search experience.",
|
|
4084
|
-
`Build the search UI for ${input.
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
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.`,
|
|
4088
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.",
|
|
4089
4150
|
// The key is provisioned only after verification passes, so the agent never
|
|
4090
4151
|
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
4091
4152
|
// decide whether a key already exists, and an agent-invented value there
|
|
@@ -4094,9 +4155,8 @@ function searchInstructions(input) {
|
|
|
4094
4155
|
// Not the agent's to rename: the wizard writes these exact names into
|
|
4095
4156
|
// ".env" right after this step, so a renamed prefix would leave the code
|
|
4096
4157
|
// reading a var the wizard never wrote.
|
|
4097
|
-
`Use exactly these
|
|
4098
|
-
"
|
|
4099
|
-
'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.",
|
|
4100
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."
|
|
4101
4161
|
];
|
|
4102
4162
|
}
|
|
@@ -4104,11 +4164,12 @@ function verificationInstructions(input) {
|
|
|
4104
4164
|
return [
|
|
4105
4165
|
"Verify the Algolia implementation changes in the current worktree.",
|
|
4106
4166
|
`Verification tools found in the codebase: ${JSON.stringify(input.findings.verification ?? [])}.`,
|
|
4107
|
-
"
|
|
4108
|
-
"
|
|
4109
|
-
"
|
|
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.",
|
|
4110
4171
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
4111
|
-
`Do not modify "${input.ingestDir}/" unless
|
|
4172
|
+
`Do not modify "${input.ingestDir}/" unless a check reports an actionable issue in its files.`,
|
|
4112
4173
|
"Always call reportStatus with status=success once verification has run, even when sufficient=false.",
|
|
4113
4174
|
"Set sufficient=true only when the implementation is complete and checks pass (or fail for a clearly unrelated reason).",
|
|
4114
4175
|
"Set sufficient=false when the implementation is incomplete or has implementation-caused failures; include concrete additionalInstructions for the next pass."
|
|
@@ -4129,14 +4190,15 @@ var IMPLEMENT_CONFIG = {
|
|
|
4129
4190
|
}
|
|
4130
4191
|
};
|
|
4131
4192
|
var useCaseToolMap = {
|
|
4132
|
-
ingestion: [
|
|
4133
|
-
search: [...FS_READ_TOOLS, "writeFile", "notifyUser"],
|
|
4134
|
-
verification: [
|
|
4193
|
+
ingestion: [
|
|
4135
4194
|
...FS_READ_TOOLS,
|
|
4136
4195
|
"writeFile",
|
|
4137
|
-
"
|
|
4196
|
+
"writeCredentials",
|
|
4197
|
+
"runShell",
|
|
4138
4198
|
"notifyUser"
|
|
4139
|
-
]
|
|
4199
|
+
],
|
|
4200
|
+
search: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"],
|
|
4201
|
+
verification: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"]
|
|
4140
4202
|
};
|
|
4141
4203
|
function toolsForUseCase(useCase, ingestionSource) {
|
|
4142
4204
|
const tools = useCaseToolMap[useCase];
|
|
@@ -4159,15 +4221,30 @@ function formatSummary(useCase, summary) {
|
|
|
4159
4221
|
const label = useCase === "ingestion" ? "Ingestion" : useCase === "search" ? "Search" : "Verification";
|
|
4160
4222
|
return `${label}: ${summary}`;
|
|
4161
4223
|
}
|
|
4162
|
-
function buildIngestCommand(worktree, runtime, entrypoint) {
|
|
4163
|
-
return `cd ${shellQuote(worktree)} && ${runtime} ${shellQuote(entrypoint)}`;
|
|
4164
|
-
}
|
|
4165
4224
|
function parseIngestRecordCount(output) {
|
|
4166
4225
|
const match = output.match(/ALGOLIA_WIZARD_RECORD_COUNT=(\d+)/);
|
|
4167
4226
|
if (!match) return void 0;
|
|
4168
4227
|
const count = Number(match[1]);
|
|
4169
4228
|
return Number.isFinite(count) ? count : void 0;
|
|
4170
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
|
+
}
|
|
4171
4248
|
function verificationRetryInstructions(verification) {
|
|
4172
4249
|
return [
|
|
4173
4250
|
`Implementation insufficient. Address these findings before reporting completion: ${verification.additionalInstructions ?? verification.summary}`
|
|
@@ -4246,9 +4323,13 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4246
4323
|
const confirmed2 = normalized.confirmedEntities;
|
|
4247
4324
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
4248
4325
|
let appId;
|
|
4326
|
+
let ingestAppId;
|
|
4249
4327
|
if (useCases.includes("search")) {
|
|
4250
4328
|
appId = (await requireApplication()).id;
|
|
4251
4329
|
}
|
|
4330
|
+
if (useCases.includes("ingestion")) {
|
|
4331
|
+
ingestAppId = appId ?? (await requireApplication()).id;
|
|
4332
|
+
}
|
|
4252
4333
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
4253
4334
|
try {
|
|
4254
4335
|
process.chdir(worktree);
|
|
@@ -4285,7 +4366,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4285
4366
|
ingestDir: INGEST_DIR,
|
|
4286
4367
|
ingestionSource,
|
|
4287
4368
|
uploadFilePath,
|
|
4288
|
-
|
|
4369
|
+
searchUiTarget: searchUiTarget(language)
|
|
4289
4370
|
};
|
|
4290
4371
|
const summaries = [];
|
|
4291
4372
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
@@ -4308,41 +4389,31 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4308
4389
|
}
|
|
4309
4390
|
let finalSearchEnvVars = input.searchEnvVars;
|
|
4310
4391
|
let agentRuns = 0;
|
|
4311
|
-
let
|
|
4312
|
-
let ingestEntrypoint;
|
|
4392
|
+
let ingestCommand;
|
|
4313
4393
|
let ingestScriptRan = false;
|
|
4314
4394
|
let ingestRecordCount;
|
|
4315
4395
|
let ingestDurationMs;
|
|
4316
|
-
let installFailed = false;
|
|
4317
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);
|
|
4318
4404
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4319
4405
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4320
4406
|
agentRuns += 1;
|
|
4321
|
-
|
|
4407
|
+
return runAgent({
|
|
4322
4408
|
instructions: buildAgentInstructions(
|
|
4323
4409
|
currentUseCase,
|
|
4324
4410
|
input,
|
|
4325
4411
|
extraInstructions
|
|
4326
4412
|
),
|
|
4327
4413
|
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4328
|
-
outputSchema: implementationOutputSchema
|
|
4329
|
-
|
|
4330
|
-
ctx.notify({
|
|
4331
|
-
messages: [`Installing dependencies for ${currentUseCase}\u2026`]
|
|
4414
|
+
outputSchema: implementationOutputSchema,
|
|
4415
|
+
toolContext: currentUseCase === "ingestion" ? ingestionTools ?? searchTools : searchTools
|
|
4332
4416
|
});
|
|
4333
|
-
const installLogId = ctx.logStart("installWorktreeDeps", {
|
|
4334
|
-
useCase: currentUseCase
|
|
4335
|
-
});
|
|
4336
|
-
const install = await installWorktreeDeps(worktree);
|
|
4337
|
-
ctx.logEnd(installLogId, install.ok ? "success" : "error");
|
|
4338
|
-
if (!install.ok) {
|
|
4339
|
-
installFailed = true;
|
|
4340
|
-
logger.warn(
|
|
4341
|
-
{ useCase: currentUseCase, output: install.output },
|
|
4342
|
-
"implement: dependency install in worktree failed; generated commands may not run until deps are installed"
|
|
4343
|
-
);
|
|
4344
|
-
}
|
|
4345
|
-
return result;
|
|
4346
4417
|
}
|
|
4347
4418
|
async function runVerificationUseCase() {
|
|
4348
4419
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
@@ -4350,111 +4421,55 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4350
4421
|
return runAgent({
|
|
4351
4422
|
instructions: buildAgentInstructions("verification", input),
|
|
4352
4423
|
tools: toolsForUseCase("verification"),
|
|
4353
|
-
outputSchema: verificationOutputSchema
|
|
4424
|
+
outputSchema: verificationOutputSchema,
|
|
4425
|
+
toolContext: searchTools
|
|
4354
4426
|
});
|
|
4355
4427
|
}
|
|
4356
4428
|
if (useCases.includes("ingestion")) {
|
|
4357
|
-
const
|
|
4358
|
-
summaries.push(formatSummary("ingestion", summary));
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
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
|
|
4376
4447
|
});
|
|
4377
|
-
const startedAt = Date.now();
|
|
4378
|
-
const run2 = await runIngestScript(
|
|
4379
|
-
worktree,
|
|
4380
|
-
ingestRuntime,
|
|
4381
|
-
ingestEntrypoint,
|
|
4382
|
-
{
|
|
4383
|
-
[APP_ID_VAR]: ingestApp.id,
|
|
4384
|
-
[API_KEY_VAR]: writeKey,
|
|
4385
|
-
[INDEX_NAME_VAR]: targetIndex
|
|
4386
|
-
}
|
|
4387
|
-
);
|
|
4388
|
-
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
4389
|
-
ingestScriptRan = run2.ran && run2.ok;
|
|
4390
|
-
if (ingestScriptRan) {
|
|
4391
|
-
ingestDurationMs = Date.now() - startedAt;
|
|
4392
|
-
ingestRecordCount = parseIngestRecordCount(run2.output);
|
|
4393
|
-
if (ingestRecordCount != null) {
|
|
4394
|
-
track("AI Wizard Ingest Successful", {
|
|
4395
|
-
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4396
|
-
record_count: ingestRecordCount,
|
|
4397
|
-
duration_ms: ingestDurationMs
|
|
4398
|
-
});
|
|
4399
|
-
}
|
|
4400
|
-
}
|
|
4401
|
-
let summaryLine;
|
|
4402
|
-
let outcomeMessage;
|
|
4403
|
-
if (!run2.ran) {
|
|
4404
|
-
summaryLine = `\u26A0\uFE0F Skipped running the ingestion script: ${run2.reason}`;
|
|
4405
|
-
outcomeMessage = `\u26A0\uFE0F The ingestion script did not run: ${run2.reason}`;
|
|
4406
|
-
logger.warn(
|
|
4407
|
-
{
|
|
4408
|
-
runtime: ingestRuntime,
|
|
4409
|
-
entrypoint: ingestEntrypoint,
|
|
4410
|
-
reason: run2.reason
|
|
4411
|
-
},
|
|
4412
|
-
"implement: refused to auto-run ingestion script"
|
|
4413
|
-
);
|
|
4414
|
-
track("Error", {
|
|
4415
|
-
step: "Push Data",
|
|
4416
|
-
error: `ingestion script skipped: ${run2.reason}`,
|
|
4417
|
-
product_area: "AI Wizard"
|
|
4418
|
-
});
|
|
4419
|
-
} else if (run2.ok) {
|
|
4420
|
-
const status = "Ingestion run: succeeded.";
|
|
4421
|
-
summaryLine = run2.output ? `${status}
|
|
4422
|
-
${run2.output}` : status;
|
|
4423
|
-
outcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4424
|
-
} else {
|
|
4425
|
-
const status = "\u26A0\uFE0F Ingestion run failed:";
|
|
4426
|
-
summaryLine = run2.output ? `${status}
|
|
4427
|
-
${run2.output}` : status;
|
|
4428
|
-
outcomeMessage = `\u274C Ingestion failed.${run2.output ? ` ${run2.output}` : ""}`;
|
|
4429
|
-
logger.warn(
|
|
4430
|
-
{
|
|
4431
|
-
runtime: ingestRuntime,
|
|
4432
|
-
entrypoint: ingestEntrypoint,
|
|
4433
|
-
output: run2.output
|
|
4434
|
-
},
|
|
4435
|
-
"implement: ingestion script run failed"
|
|
4436
|
-
);
|
|
4437
|
-
track("Error", {
|
|
4438
|
-
step: "Push Data",
|
|
4439
|
-
error: run2.output || "ingestion script exited non-zero",
|
|
4440
|
-
product_area: "AI Wizard"
|
|
4441
|
-
});
|
|
4442
|
-
}
|
|
4443
|
-
summaries.push(summaryLine);
|
|
4444
|
-
ingestOutcomeMessage = outcomeMessage;
|
|
4445
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
|
+
});
|
|
4446
4463
|
}
|
|
4447
4464
|
const commandMessages = [`Open the worktree: cd ${shellQuote(worktree)}`];
|
|
4448
|
-
if (
|
|
4449
|
-
commandMessages.push(
|
|
4450
|
-
`Ingestion command: ${buildIngestCommand(worktree, ingestRuntime, ingestEntrypoint)}`
|
|
4451
|
-
);
|
|
4465
|
+
if (ingestCommand) {
|
|
4466
|
+
commandMessages.push(`Ingestion command: ${ingestCommand}`);
|
|
4452
4467
|
}
|
|
4453
4468
|
await ctx.requestUserInput({
|
|
4454
4469
|
prompt: "",
|
|
4455
4470
|
promptType: "enterToContinue",
|
|
4456
4471
|
options: [],
|
|
4457
|
-
messages:
|
|
4472
|
+
messages: [ingestOutcomeMessage, ...commandMessages]
|
|
4458
4473
|
});
|
|
4459
4474
|
}
|
|
4460
4475
|
if (useCases.includes("search")) {
|
|
@@ -4571,22 +4586,13 @@ ${run2.output}` : status;
|
|
|
4571
4586
|
"implement: agent reported success but no files changed in the worktree"
|
|
4572
4587
|
);
|
|
4573
4588
|
}
|
|
4574
|
-
if (installFailed) {
|
|
4575
|
-
summaries.push(
|
|
4576
|
-
'\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".'
|
|
4577
|
-
);
|
|
4578
|
-
}
|
|
4579
4589
|
return {
|
|
4580
4590
|
ingestionSource,
|
|
4581
4591
|
filesChanged,
|
|
4582
4592
|
summary: summaries.join("\n\n"),
|
|
4583
4593
|
worktreePath: worktree,
|
|
4584
|
-
...useCases.includes("ingestion") &&
|
|
4585
|
-
ingestCommand
|
|
4586
|
-
worktree,
|
|
4587
|
-
ingestRuntime,
|
|
4588
|
-
ingestEntrypoint
|
|
4589
|
-
),
|
|
4594
|
+
...useCases.includes("ingestion") && ingestCommand ? {
|
|
4595
|
+
ingestCommand,
|
|
4590
4596
|
ingestScriptRan,
|
|
4591
4597
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4592
4598
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
@@ -4917,7 +4923,7 @@ function parseCliArgs(argv) {
|
|
|
4917
4923
|
|
|
4918
4924
|
// src/lib/resetState.ts
|
|
4919
4925
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4920
|
-
import { join as
|
|
4926
|
+
import { join as join10 } from "node:path";
|
|
4921
4927
|
var KEEP = ["wizard.log"];
|
|
4922
4928
|
async function resetProjectState() {
|
|
4923
4929
|
const dir = stateDir();
|
|
@@ -4931,14 +4937,14 @@ async function resetProjectState() {
|
|
|
4931
4937
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4932
4938
|
await Promise.all(
|
|
4933
4939
|
targets.map(
|
|
4934
|
-
(name) => rm2(
|
|
4940
|
+
(name) => rm2(join10(dir, name), { recursive: true, force: true })
|
|
4935
4941
|
)
|
|
4936
4942
|
);
|
|
4937
4943
|
return { dir, removed: targets };
|
|
4938
4944
|
}
|
|
4939
4945
|
|
|
4940
4946
|
// src/main.tsx
|
|
4941
|
-
import { jsx as
|
|
4947
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4942
4948
|
async function startup() {
|
|
4943
4949
|
setProjectRoot(process.cwd());
|
|
4944
4950
|
let args;
|
|
@@ -4988,7 +4994,7 @@ ${formatStepList(workflow)}`);
|
|
|
4988
4994
|
}
|
|
4989
4995
|
async function run(workflow) {
|
|
4990
4996
|
const store = useWizard.getState();
|
|
4991
|
-
const instance = render(/* @__PURE__ */
|
|
4997
|
+
const instance = render(/* @__PURE__ */ jsx15(App, {}), { incrementalRendering: true });
|
|
4992
4998
|
await store.waitForStart();
|
|
4993
4999
|
let user = await getUser();
|
|
4994
5000
|
if (!user) {
|