@algolia/wizard 0.9.0-rc.88.85 → 0.9.0-rc.93.91
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";
|
|
@@ -626,10 +626,13 @@ function Notices() {
|
|
|
626
626
|
}
|
|
627
627
|
|
|
628
628
|
// src/ui/PromptInput.tsx
|
|
629
|
-
import { Box as
|
|
629
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
630
630
|
import TextInput from "ink-text-input";
|
|
631
631
|
import { useState as useState5 } from "react";
|
|
632
632
|
|
|
633
|
+
// src/ui/CommandApproval.tsx
|
|
634
|
+
import { Box as Box5, Text as Text5, useInput } from "ink";
|
|
635
|
+
|
|
633
636
|
// src/ui/NextAction.tsx
|
|
634
637
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
635
638
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -653,14 +656,69 @@ function NextAction({
|
|
|
653
656
|
] });
|
|
654
657
|
}
|
|
655
658
|
|
|
659
|
+
// src/ui/CommandApproval.tsx
|
|
660
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
661
|
+
function CommandApproval({
|
|
662
|
+
command,
|
|
663
|
+
onDecide
|
|
664
|
+
}) {
|
|
665
|
+
useInput((input, key) => {
|
|
666
|
+
if (key.return) onDecide("approve");
|
|
667
|
+
else if (key.escape) onDecide("reject");
|
|
668
|
+
else if (input.toLowerCase() === "a") onDecide("always");
|
|
669
|
+
});
|
|
670
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
|
|
671
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, bold: true, children: "Run this command?" }),
|
|
672
|
+
/* @__PURE__ */ jsxs4(
|
|
673
|
+
Box5,
|
|
674
|
+
{
|
|
675
|
+
flexDirection: "column",
|
|
676
|
+
paddingLeft: 2,
|
|
677
|
+
borderStyle: "single",
|
|
678
|
+
borderColor: COLORS.success,
|
|
679
|
+
borderTop: false,
|
|
680
|
+
borderBottom: false,
|
|
681
|
+
borderRight: false,
|
|
682
|
+
gap: 1,
|
|
683
|
+
children: [
|
|
684
|
+
/* @__PURE__ */ jsxs4(Box5, { children: [
|
|
685
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "$ " }),
|
|
686
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.strong, wrap: "wrap", children: command.command })
|
|
687
|
+
] }),
|
|
688
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
689
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "in:" }),
|
|
690
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
|
|
691
|
+
] }),
|
|
692
|
+
command.explanation && /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
693
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "why:" }),
|
|
694
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
|
|
695
|
+
] })
|
|
696
|
+
]
|
|
697
|
+
}
|
|
698
|
+
),
|
|
699
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
700
|
+
/* @__PURE__ */ jsx4(NextAction, { action: "approve", keyHint: "enter" }),
|
|
701
|
+
/* @__PURE__ */ jsx4(NextAction, { action: "reject", keyHint: "esc", hierarchy: "secondary" }),
|
|
702
|
+
/* @__PURE__ */ jsx4(
|
|
703
|
+
NextAction,
|
|
704
|
+
{
|
|
705
|
+
action: "approve, and don't ask again for this command",
|
|
706
|
+
keyHint: "a",
|
|
707
|
+
hierarchy: "secondary"
|
|
708
|
+
}
|
|
709
|
+
)
|
|
710
|
+
] })
|
|
711
|
+
] });
|
|
712
|
+
}
|
|
713
|
+
|
|
656
714
|
// src/ui/SelectPrompt.tsx
|
|
657
|
-
import { Box as
|
|
715
|
+
import { Box as Box7, Text as Text7, useInput as useInput2, useWindowSize as useWindowSize5 } from "ink";
|
|
658
716
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
659
717
|
|
|
660
718
|
// src/ui/ScrollView.tsx
|
|
661
|
-
import { Box as
|
|
719
|
+
import { Box as Box6, Text as Text6, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
|
|
662
720
|
import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
663
|
-
import { jsxs as
|
|
721
|
+
import { jsxs as jsxs5 } from "react/jsx-runtime";
|
|
664
722
|
var INDICATOR_ROWS = 2;
|
|
665
723
|
function fittedWidth(node, columns) {
|
|
666
724
|
let left = 0;
|
|
@@ -730,14 +788,14 @@ function useScrollWindow({
|
|
|
730
788
|
};
|
|
731
789
|
}
|
|
732
790
|
function ScrollView({ scroll, children }) {
|
|
733
|
-
return /* @__PURE__ */
|
|
734
|
-
scroll.hiddenAbove > 0 && /* @__PURE__ */
|
|
791
|
+
return /* @__PURE__ */ jsxs5(Box6, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
792
|
+
scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
735
793
|
"\u2191 ",
|
|
736
794
|
scroll.hiddenAbove,
|
|
737
795
|
" more"
|
|
738
796
|
] }),
|
|
739
797
|
children,
|
|
740
|
-
scroll.hiddenBelow > 0 && /* @__PURE__ */
|
|
798
|
+
scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
741
799
|
"\u2193 ",
|
|
742
800
|
scroll.hiddenBelow,
|
|
743
801
|
" more"
|
|
@@ -746,7 +804,7 @@ function ScrollView({ scroll, children }) {
|
|
|
746
804
|
}
|
|
747
805
|
|
|
748
806
|
// src/ui/SelectPrompt.tsx
|
|
749
|
-
import { jsx as
|
|
807
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
750
808
|
var CANCEL = "cancel";
|
|
751
809
|
var ARROW_WIDTH = 4;
|
|
752
810
|
var COLUMN_GAP = 2;
|
|
@@ -808,7 +866,7 @@ function SelectPrompt({
|
|
|
808
866
|
revealIndex(index);
|
|
809
867
|
}, [index, revealIndex]);
|
|
810
868
|
const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
811
|
-
|
|
869
|
+
useInput2((input, key) => {
|
|
812
870
|
if (rows.length === 0) return;
|
|
813
871
|
if (key.upArrow || input === "k") {
|
|
814
872
|
setIndex((i) => (i - 1 + rows.length) % rows.length);
|
|
@@ -831,56 +889,56 @@ function SelectPrompt({
|
|
|
831
889
|
}
|
|
832
890
|
}
|
|
833
891
|
});
|
|
834
|
-
return /* @__PURE__ */
|
|
835
|
-
/* @__PURE__ */
|
|
836
|
-
error && /* @__PURE__ */
|
|
837
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
838
|
-
table && /* @__PURE__ */
|
|
839
|
-
/* @__PURE__ */
|
|
840
|
-
question && /* @__PURE__ */
|
|
841
|
-
helpText && /* @__PURE__ */
|
|
892
|
+
return /* @__PURE__ */ jsx5(Box7, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, width, children: [
|
|
893
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
|
|
894
|
+
error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: error }),
|
|
895
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
896
|
+
table && /* @__PURE__ */ jsx5(Table, { columns: table.columns, rows: table.rows }),
|
|
897
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
898
|
+
question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: question }),
|
|
899
|
+
helpText && /* @__PURE__ */ jsx5(Text7, { color: COLORS.dim, children: helpText })
|
|
842
900
|
] })
|
|
843
901
|
] }),
|
|
844
|
-
/* @__PURE__ */
|
|
902
|
+
/* @__PURE__ */ jsx5(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
|
|
845
903
|
const i = scroll.offset + visibleIndex;
|
|
846
904
|
const highlighted = i === index;
|
|
847
905
|
const isCancel = i === cancelIndex;
|
|
848
906
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
849
907
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
850
908
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
851
|
-
const label = /* @__PURE__ */
|
|
909
|
+
const label = /* @__PURE__ */ jsxs6(Text7, { color: labelColor, wrap: "truncate", children: [
|
|
852
910
|
highlighted ? "\u276F " : " ",
|
|
853
911
|
bullet,
|
|
854
912
|
option
|
|
855
913
|
] });
|
|
856
914
|
const isText = sec?.kind === "text";
|
|
857
|
-
return /* @__PURE__ */
|
|
858
|
-
|
|
915
|
+
return /* @__PURE__ */ jsxs6(
|
|
916
|
+
Box7,
|
|
859
917
|
{
|
|
860
918
|
width: isText ? "100%" : barWidth,
|
|
861
919
|
paddingX: 1,
|
|
862
920
|
paddingY: 1,
|
|
863
921
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
864
922
|
children: [
|
|
865
|
-
/* @__PURE__ */
|
|
866
|
-
isText && textWidth > 0 && /* @__PURE__ */
|
|
867
|
-
|
|
923
|
+
/* @__PURE__ */ jsx5(Box7, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
924
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx5(Box7, { width: textWidth, children: /* @__PURE__ */ jsx5(
|
|
925
|
+
Text7,
|
|
868
926
|
{
|
|
869
927
|
wrap: "truncate",
|
|
870
928
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
871
929
|
children: sec.value
|
|
872
930
|
}
|
|
873
931
|
) }),
|
|
874
|
-
sec?.kind === "badge" && /* @__PURE__ */
|
|
932
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx5(Box7, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx5(Text7, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
875
933
|
]
|
|
876
934
|
},
|
|
877
935
|
`row-${i}`
|
|
878
936
|
);
|
|
879
937
|
}) }),
|
|
880
|
-
/* @__PURE__ */
|
|
938
|
+
/* @__PURE__ */ jsx5(Box7, { flexShrink: 0, children: /* @__PURE__ */ jsx5(Text7, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
881
939
|
i > 0 ? " " : "",
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
/* @__PURE__ */
|
|
940
|
+
/* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: key }),
|
|
941
|
+
/* @__PURE__ */ jsxs6(Text7, { color: COLORS.dim, children: [
|
|
884
942
|
" ",
|
|
885
943
|
label
|
|
886
944
|
] })
|
|
@@ -889,23 +947,23 @@ function SelectPrompt({
|
|
|
889
947
|
}
|
|
890
948
|
|
|
891
949
|
// src/ui/PromptInput.tsx
|
|
892
|
-
import { jsx as
|
|
950
|
+
import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
893
951
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
894
952
|
function EnterToContinuePrompt({
|
|
895
953
|
question,
|
|
896
954
|
messages,
|
|
897
955
|
onDecide
|
|
898
956
|
}) {
|
|
899
|
-
|
|
957
|
+
useInput3((_input, key) => {
|
|
900
958
|
if (key.return) onDecide(true);
|
|
901
959
|
else if (key.escape) onDecide(false);
|
|
902
960
|
});
|
|
903
|
-
return /* @__PURE__ */
|
|
904
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
905
|
-
question && /* @__PURE__ */
|
|
906
|
-
/* @__PURE__ */
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
/* @__PURE__ */
|
|
961
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, children: [
|
|
962
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
963
|
+
question && /* @__PURE__ */ jsx6(Text8, { color: COLORS.primary, children: question }),
|
|
964
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
|
|
965
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "continue", keyHint: "enter" }),
|
|
966
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
909
967
|
] })
|
|
910
968
|
] });
|
|
911
969
|
}
|
|
@@ -913,11 +971,11 @@ function PromptInput() {
|
|
|
913
971
|
const { phase, inputReq, submitInput } = useWizard();
|
|
914
972
|
const [draft, setDraft] = useState5("");
|
|
915
973
|
if (phase === "done" || phase === "error") {
|
|
916
|
-
return /* @__PURE__ */
|
|
974
|
+
return /* @__PURE__ */ jsx6(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text8, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
917
975
|
}
|
|
918
976
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
919
977
|
if (inputReq.promptType === "multipleChoice") {
|
|
920
|
-
return /* @__PURE__ */
|
|
978
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
921
979
|
SelectPrompt,
|
|
922
980
|
{
|
|
923
981
|
question: inputReq.prompt,
|
|
@@ -934,7 +992,7 @@ function PromptInput() {
|
|
|
934
992
|
) });
|
|
935
993
|
}
|
|
936
994
|
if (inputReq.promptType === "multiSelect") {
|
|
937
|
-
return /* @__PURE__ */
|
|
995
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
938
996
|
SelectPrompt,
|
|
939
997
|
{
|
|
940
998
|
multi: true,
|
|
@@ -949,7 +1007,7 @@ function PromptInput() {
|
|
|
949
1007
|
) });
|
|
950
1008
|
}
|
|
951
1009
|
if (inputReq.promptType === "notice") {
|
|
952
|
-
return /* @__PURE__ */
|
|
1010
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
953
1011
|
SelectPrompt,
|
|
954
1012
|
{
|
|
955
1013
|
question: inputReq.prompt,
|
|
@@ -960,7 +1018,7 @@ function PromptInput() {
|
|
|
960
1018
|
) });
|
|
961
1019
|
}
|
|
962
1020
|
if (inputReq.promptType === "enterToContinue") {
|
|
963
|
-
return /* @__PURE__ */
|
|
1021
|
+
return /* @__PURE__ */ jsx6(
|
|
964
1022
|
EnterToContinuePrompt,
|
|
965
1023
|
{
|
|
966
1024
|
question: inputReq.prompt,
|
|
@@ -969,9 +1027,12 @@ function PromptInput() {
|
|
|
969
1027
|
}
|
|
970
1028
|
);
|
|
971
1029
|
}
|
|
1030
|
+
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1031
|
+
return /* @__PURE__ */ jsx6(CommandApproval, { command: inputReq.command, onDecide: submitInput });
|
|
1032
|
+
}
|
|
972
1033
|
if (inputReq.promptType === "acceptReject") {
|
|
973
1034
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
974
|
-
return /* @__PURE__ */
|
|
1035
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
975
1036
|
SelectPrompt,
|
|
976
1037
|
{
|
|
977
1038
|
question: inputReq.prompt,
|
|
@@ -982,15 +1043,15 @@ function PromptInput() {
|
|
|
982
1043
|
}
|
|
983
1044
|
) });
|
|
984
1045
|
}
|
|
985
|
-
return /* @__PURE__ */
|
|
986
|
-
inputReq.error && /* @__PURE__ */
|
|
987
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
989
|
-
/* @__PURE__ */
|
|
1046
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1047
|
+
inputReq.error && /* @__PURE__ */ jsx6(Text8, { color: COLORS.danger, children: inputReq.error }),
|
|
1048
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
1049
|
+
/* @__PURE__ */ jsxs7(Box8, { children: [
|
|
1050
|
+
/* @__PURE__ */ jsxs7(Text8, { color: COLORS.primary, children: [
|
|
990
1051
|
inputReq.prompt,
|
|
991
1052
|
" "
|
|
992
1053
|
] }),
|
|
993
|
-
/* @__PURE__ */
|
|
1054
|
+
/* @__PURE__ */ jsx6(
|
|
994
1055
|
TextInput,
|
|
995
1056
|
{
|
|
996
1057
|
value: draft,
|
|
@@ -1008,7 +1069,7 @@ function PromptInput() {
|
|
|
1008
1069
|
// src/ui/Welcome.tsx
|
|
1009
1070
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1010
1071
|
import { fileURLToPath } from "node:url";
|
|
1011
|
-
import { Box as
|
|
1072
|
+
import { Box as Box9, Spacer, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
1012
1073
|
|
|
1013
1074
|
// src/ui/copy/welcome.ts
|
|
1014
1075
|
var sidebarItems = [
|
|
@@ -1021,12 +1082,12 @@ var sidebarItems = [
|
|
|
1021
1082
|
description: "push 100 records to Algolia in seconds"
|
|
1022
1083
|
},
|
|
1023
1084
|
{
|
|
1024
|
-
title: "detect your
|
|
1025
|
-
description: "
|
|
1085
|
+
title: "detect your stack",
|
|
1086
|
+
description: "whatever language and framework you already use"
|
|
1026
1087
|
},
|
|
1027
1088
|
{
|
|
1028
1089
|
title: "scaffold a search UI",
|
|
1029
|
-
description: "a
|
|
1090
|
+
description: "a search box and results, wired into your app"
|
|
1030
1091
|
},
|
|
1031
1092
|
{
|
|
1032
1093
|
title: "ship it",
|
|
@@ -1036,20 +1097,20 @@ var sidebarItems = [
|
|
|
1036
1097
|
|
|
1037
1098
|
// src/ui/Welcome.tsx
|
|
1038
1099
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
1039
|
-
import { jsx as
|
|
1100
|
+
import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1040
1101
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
1041
1102
|
function SidebarItem({
|
|
1042
1103
|
title,
|
|
1043
1104
|
description
|
|
1044
1105
|
}) {
|
|
1045
|
-
return /* @__PURE__ */
|
|
1046
|
-
/* @__PURE__ */
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1106
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
|
|
1107
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, children: [
|
|
1108
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.success, children: "\u2192" }),
|
|
1109
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: title })
|
|
1049
1110
|
] }),
|
|
1050
|
-
/* @__PURE__ */
|
|
1051
|
-
/* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1111
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 2, children: [
|
|
1112
|
+
/* @__PURE__ */ jsx7(Spacer, {}),
|
|
1113
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: description })
|
|
1053
1114
|
] })
|
|
1054
1115
|
] });
|
|
1055
1116
|
}
|
|
@@ -1057,7 +1118,7 @@ function Welcome() {
|
|
|
1057
1118
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
1058
1119
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
1059
1120
|
const { rows } = useWindowSize6();
|
|
1060
|
-
|
|
1121
|
+
useInput4((input, key) => {
|
|
1061
1122
|
if (key.return) confirmStart();
|
|
1062
1123
|
else if (input === "i") openLearnMore();
|
|
1063
1124
|
});
|
|
@@ -1075,16 +1136,16 @@ function Welcome() {
|
|
|
1075
1136
|
if (rows < 30) {
|
|
1076
1137
|
layout = scales["small"];
|
|
1077
1138
|
}
|
|
1078
|
-
return /* @__PURE__ */
|
|
1079
|
-
/* @__PURE__ */
|
|
1080
|
-
|
|
1139
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
1140
|
+
/* @__PURE__ */ jsx7(
|
|
1141
|
+
Box9,
|
|
1081
1142
|
{
|
|
1082
1143
|
paddingY: layout.main.padding.y,
|
|
1083
1144
|
paddingX: layout.main.padding.x,
|
|
1084
1145
|
flexDirection: "column",
|
|
1085
1146
|
justifyContent: "center",
|
|
1086
|
-
children: /* @__PURE__ */
|
|
1087
|
-
/* @__PURE__ */
|
|
1147
|
+
children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 2, children: [
|
|
1148
|
+
/* @__PURE__ */ jsx7(InkPictureProvider, { children: /* @__PURE__ */ jsx7(
|
|
1088
1149
|
Image,
|
|
1089
1150
|
{
|
|
1090
1151
|
src: IMAGE_PATH,
|
|
@@ -1095,16 +1156,16 @@ function Welcome() {
|
|
|
1095
1156
|
protocol: "halfBlock"
|
|
1096
1157
|
}
|
|
1097
1158
|
) }),
|
|
1098
|
-
/* @__PURE__ */
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1159
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1160
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, flexDirection: "column", children: [
|
|
1161
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
1162
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
1102
1163
|
] })
|
|
1103
1164
|
] })
|
|
1104
1165
|
}
|
|
1105
1166
|
),
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
|
|
1167
|
+
/* @__PURE__ */ jsxs8(
|
|
1168
|
+
Box9,
|
|
1108
1169
|
{
|
|
1109
1170
|
backgroundColor: COLORS.bg.sidebar,
|
|
1110
1171
|
width: 40,
|
|
@@ -1114,8 +1175,8 @@ function Welcome() {
|
|
|
1114
1175
|
flexDirection: "column",
|
|
1115
1176
|
justifyContent: "center",
|
|
1116
1177
|
children: [
|
|
1117
|
-
/* @__PURE__ */
|
|
1118
|
-
sidebarItems.map((i, idx) => /* @__PURE__ */
|
|
1178
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
1179
|
+
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx7(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
1119
1180
|
]
|
|
1120
1181
|
}
|
|
1121
1182
|
)
|
|
@@ -1124,7 +1185,7 @@ function Welcome() {
|
|
|
1124
1185
|
|
|
1125
1186
|
// src/ui/LearnMore.tsx
|
|
1126
1187
|
import { Fragment as Fragment2 } from "react";
|
|
1127
|
-
import { Box as
|
|
1188
|
+
import { Box as Box10, Text as Text10, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1128
1189
|
|
|
1129
1190
|
// src/ui/copy/learn-more.ts
|
|
1130
1191
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -1132,12 +1193,17 @@ var accessItems = [
|
|
|
1132
1193
|
{
|
|
1133
1194
|
tag: "READ",
|
|
1134
1195
|
title: "Project files",
|
|
1135
|
-
description: "reads
|
|
1196
|
+
description: "reads manifests, configs & source to detect your stack. Read-only; nothing is uploaded."
|
|
1136
1197
|
},
|
|
1137
1198
|
{
|
|
1138
1199
|
tag: "WRITE",
|
|
1139
1200
|
title: "Code changes",
|
|
1140
|
-
description: "creates & edits files (search UI, config)
|
|
1201
|
+
description: "creates & edits files (search UI, config) in a throwaway git worktree \u2014 your checkout is never touched."
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
tag: "EXEC",
|
|
1205
|
+
title: "Setup commands",
|
|
1206
|
+
description: "runs dependency installs, the ingestion script & your own checks. Every command is shown in full and needs your OK; its output is shown as-is, so a command that prints a secret will display it."
|
|
1141
1207
|
},
|
|
1142
1208
|
{
|
|
1143
1209
|
tag: "NET",
|
|
@@ -1147,13 +1213,13 @@ var accessItems = [
|
|
|
1147
1213
|
{
|
|
1148
1214
|
tag: "KEY",
|
|
1149
1215
|
title: "Credentials",
|
|
1150
|
-
description: "
|
|
1216
|
+
description: "writes your Algolia app id and a search-only key (safe to expose) to .env in the worktree."
|
|
1151
1217
|
}
|
|
1152
1218
|
];
|
|
1153
1219
|
var neverItems = [
|
|
1154
1220
|
"Send your source code to a model or third party",
|
|
1155
1221
|
"Commit or push to git",
|
|
1156
|
-
"
|
|
1222
|
+
"Run a command you haven't approved"
|
|
1157
1223
|
];
|
|
1158
1224
|
var policyLinks = [
|
|
1159
1225
|
{ label: "Terms", url: "https://www.algolia.com/policies/terms" },
|
|
@@ -1161,10 +1227,11 @@ var policyLinks = [
|
|
|
1161
1227
|
];
|
|
1162
1228
|
|
|
1163
1229
|
// src/ui/LearnMore.tsx
|
|
1164
|
-
import { jsx as
|
|
1230
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1165
1231
|
var TAG_COLORS = {
|
|
1166
1232
|
READ: COLORS.success,
|
|
1167
1233
|
WRITE: COLORS.badge,
|
|
1234
|
+
EXEC: COLORS.danger,
|
|
1168
1235
|
NET: COLORS.accent,
|
|
1169
1236
|
KEY: COLORS.muted
|
|
1170
1237
|
};
|
|
@@ -1177,12 +1244,12 @@ function NeverLine({
|
|
|
1177
1244
|
}) {
|
|
1178
1245
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
1179
1246
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
1180
|
-
return /* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
1247
|
+
return /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1248
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" }),
|
|
1182
1249
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
1183
|
-
segments.map((s, i) => /* @__PURE__ */
|
|
1250
|
+
segments.map((s, i) => /* @__PURE__ */ jsx8(Text10, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
1184
1251
|
" ".repeat(rightPad),
|
|
1185
|
-
/* @__PURE__ */
|
|
1252
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" })
|
|
1186
1253
|
] });
|
|
1187
1254
|
}
|
|
1188
1255
|
function LearnMore() {
|
|
@@ -1190,12 +1257,12 @@ function LearnMore() {
|
|
|
1190
1257
|
const backToHome = useWizard((s) => s.backToHome);
|
|
1191
1258
|
const { columns } = useWindowSize7();
|
|
1192
1259
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
1193
|
-
|
|
1260
|
+
useInput5((_input, key) => {
|
|
1194
1261
|
if (key.escape) backToHome();
|
|
1195
1262
|
else if (key.return) confirmStart();
|
|
1196
1263
|
});
|
|
1197
|
-
return /* @__PURE__ */
|
|
1198
|
-
|
|
1264
|
+
return /* @__PURE__ */ jsxs9(
|
|
1265
|
+
Box10,
|
|
1199
1266
|
{
|
|
1200
1267
|
flexDirection: "column",
|
|
1201
1268
|
paddingX: PADDING_X,
|
|
@@ -1203,31 +1270,31 @@ function LearnMore() {
|
|
|
1203
1270
|
width: "100%",
|
|
1204
1271
|
gap: 1,
|
|
1205
1272
|
children: [
|
|
1206
|
-
/* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1208
|
-
/* @__PURE__ */
|
|
1209
|
-
/* @__PURE__ */
|
|
1210
|
-
/* @__PURE__ */
|
|
1211
|
-
/* @__PURE__ */
|
|
1212
|
-
/* @__PURE__ */
|
|
1213
|
-
/* @__PURE__ */
|
|
1214
|
-
/* @__PURE__ */
|
|
1273
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1274
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: accessIntro }),
|
|
1275
|
+
/* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
1276
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1277
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1278
|
+
/* @__PURE__ */ jsx8(Box10, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text10, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1279
|
+
/* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1280
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1281
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
1215
1282
|
] }) })
|
|
1216
1283
|
] })
|
|
1217
1284
|
] }, item.tag)) }),
|
|
1218
|
-
/* @__PURE__ */
|
|
1219
|
-
/* @__PURE__ */
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1285
|
+
/* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "column", children: [
|
|
1286
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
1287
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1288
|
+
/* @__PURE__ */ jsx8(
|
|
1222
1289
|
NeverLine,
|
|
1223
1290
|
{
|
|
1224
1291
|
width: dividerWidth,
|
|
1225
1292
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1226
1293
|
}
|
|
1227
1294
|
),
|
|
1228
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1229
|
-
/* @__PURE__ */
|
|
1230
|
-
/* @__PURE__ */
|
|
1295
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
1296
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1297
|
+
/* @__PURE__ */ jsx8(
|
|
1231
1298
|
NeverLine,
|
|
1232
1299
|
{
|
|
1233
1300
|
width: dividerWidth,
|
|
@@ -1239,24 +1306,24 @@ function LearnMore() {
|
|
|
1239
1306
|
}
|
|
1240
1307
|
)
|
|
1241
1308
|
] }, item)),
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */
|
|
1309
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1310
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1244
1311
|
] }),
|
|
1245
|
-
/* @__PURE__ */
|
|
1246
|
-
/* @__PURE__ */
|
|
1247
|
-
/* @__PURE__ */
|
|
1312
|
+
/* @__PURE__ */ jsx8(Box10, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1313
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1314
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.accent, children: link.url })
|
|
1248
1315
|
] }, link.label)) }),
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1253
|
-
/* @__PURE__ */
|
|
1316
|
+
/* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1317
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1318
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
|
|
1319
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "esc" }),
|
|
1320
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "] back" })
|
|
1254
1321
|
] }),
|
|
1255
|
-
/* @__PURE__ */
|
|
1256
|
-
/* @__PURE__ */
|
|
1257
|
-
/* @__PURE__ */
|
|
1258
|
-
/* @__PURE__ */
|
|
1259
|
-
/* @__PURE__ */
|
|
1322
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1323
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
|
|
1324
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "enter" }),
|
|
1325
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "]" }),
|
|
1326
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1260
1327
|
] })
|
|
1261
1328
|
] })
|
|
1262
1329
|
]
|
|
@@ -1265,10 +1332,10 @@ function LearnMore() {
|
|
|
1265
1332
|
}
|
|
1266
1333
|
|
|
1267
1334
|
// src/ui/Sidebar.tsx
|
|
1268
|
-
import { Box as
|
|
1335
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1269
1336
|
|
|
1270
1337
|
// src/ui/Steps.tsx
|
|
1271
|
-
import { Box as
|
|
1338
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1272
1339
|
import Spinner from "ink-spinner";
|
|
1273
1340
|
|
|
1274
1341
|
// src/core/persistence.ts
|
|
@@ -1297,12 +1364,12 @@ async function clearWorkflowState(workflowId) {
|
|
|
1297
1364
|
}
|
|
1298
1365
|
|
|
1299
1366
|
// src/ui/Steps.tsx
|
|
1300
|
-
import { jsx as
|
|
1367
|
+
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1301
1368
|
function Steps() {
|
|
1302
1369
|
const { steps } = useWizard();
|
|
1303
1370
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1304
|
-
return /* @__PURE__ */
|
|
1305
|
-
s.status === "running" ? /* @__PURE__ */
|
|
1371
|
+
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: [
|
|
1372
|
+
s.status === "running" ? /* @__PURE__ */ jsx9(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1306
1373
|
" ",
|
|
1307
1374
|
s.title
|
|
1308
1375
|
] }) }, s.id)) });
|
|
@@ -1311,27 +1378,27 @@ function CurrentStep() {
|
|
|
1311
1378
|
const { steps } = useWizard();
|
|
1312
1379
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1313
1380
|
if (!currentStep) return null;
|
|
1314
|
-
return /* @__PURE__ */
|
|
1315
|
-
/* @__PURE__ */
|
|
1381
|
+
return /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status.running, children: [
|
|
1382
|
+
/* @__PURE__ */ jsx9(Spinner, { type: "dots" }),
|
|
1316
1383
|
" ",
|
|
1317
1384
|
` ${currentStep.title}`
|
|
1318
1385
|
] });
|
|
1319
1386
|
}
|
|
1320
1387
|
|
|
1321
1388
|
// src/ui/Progress.tsx
|
|
1322
|
-
import { Box as
|
|
1323
|
-
import { jsx as
|
|
1389
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1390
|
+
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1324
1391
|
function Progress() {
|
|
1325
1392
|
const { steps, currentStepIndex } = useWizard();
|
|
1326
1393
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1327
1394
|
if (visibleSteps.length === 0) return null;
|
|
1328
1395
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1329
1396
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1330
|
-
return /* @__PURE__ */
|
|
1331
|
-
/* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1334
|
-
/* @__PURE__ */
|
|
1397
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1398
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "STEP" }),
|
|
1399
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: activeStepNumber }),
|
|
1400
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: "/" }),
|
|
1401
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: visibleSteps.length })
|
|
1335
1402
|
] });
|
|
1336
1403
|
}
|
|
1337
1404
|
|
|
@@ -1342,10 +1409,10 @@ var sidebarCommands = [
|
|
|
1342
1409
|
];
|
|
1343
1410
|
|
|
1344
1411
|
// src/ui/Sidebar.tsx
|
|
1345
|
-
import { jsx as
|
|
1412
|
+
import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1346
1413
|
function Sidebar() {
|
|
1347
|
-
return /* @__PURE__ */
|
|
1348
|
-
|
|
1414
|
+
return /* @__PURE__ */ jsxs12(
|
|
1415
|
+
Box13,
|
|
1349
1416
|
{
|
|
1350
1417
|
backgroundColor: "#14171E",
|
|
1351
1418
|
width: 30,
|
|
@@ -1354,16 +1421,16 @@ function Sidebar() {
|
|
|
1354
1421
|
flexDirection: "column",
|
|
1355
1422
|
justifyContent: "space-between",
|
|
1356
1423
|
children: [
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
/* @__PURE__ */
|
|
1424
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1425
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1426
|
+
/* @__PURE__ */ jsx11(Steps, {})
|
|
1360
1427
|
] }),
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__PURE__ */
|
|
1363
|
-
/* @__PURE__ */
|
|
1364
|
-
return /* @__PURE__ */
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
/* @__PURE__ */
|
|
1428
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1429
|
+
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1430
|
+
/* @__PURE__ */ jsx11(Box13, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1431
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
|
|
1432
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1433
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: c.description })
|
|
1367
1434
|
] });
|
|
1368
1435
|
}) })
|
|
1369
1436
|
] })
|
|
@@ -1373,12 +1440,12 @@ function Sidebar() {
|
|
|
1373
1440
|
}
|
|
1374
1441
|
|
|
1375
1442
|
// src/ui/Ribbon.tsx
|
|
1376
|
-
import { Box as
|
|
1377
|
-
import { jsx as
|
|
1443
|
+
import { Box as Box14, Text as Text14 } from "ink";
|
|
1444
|
+
import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1378
1445
|
function Ribbon() {
|
|
1379
1446
|
const firstCommand = sidebarCommands[0];
|
|
1380
|
-
return /* @__PURE__ */
|
|
1381
|
-
|
|
1447
|
+
return /* @__PURE__ */ jsxs13(
|
|
1448
|
+
Box14,
|
|
1382
1449
|
{
|
|
1383
1450
|
backgroundColor: "#14171E",
|
|
1384
1451
|
flexDirection: "row",
|
|
@@ -1386,11 +1453,11 @@ function Ribbon() {
|
|
|
1386
1453
|
paddingX: 2,
|
|
1387
1454
|
paddingY: 1,
|
|
1388
1455
|
children: [
|
|
1389
|
-
/* @__PURE__ */
|
|
1390
|
-
/* @__PURE__ */
|
|
1391
|
-
/* @__PURE__ */
|
|
1392
|
-
/* @__PURE__ */
|
|
1393
|
-
/* @__PURE__ */
|
|
1456
|
+
/* @__PURE__ */ jsx12(Progress, {}),
|
|
1457
|
+
/* @__PURE__ */ jsx12(CurrentStep, {}),
|
|
1458
|
+
/* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: 1, children: [
|
|
1459
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1460
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: firstCommand.description })
|
|
1394
1461
|
] })
|
|
1395
1462
|
]
|
|
1396
1463
|
}
|
|
@@ -1401,8 +1468,8 @@ function Ribbon() {
|
|
|
1401
1468
|
import { useState as useState6 } from "react";
|
|
1402
1469
|
|
|
1403
1470
|
// src/ui/Logs.tsx
|
|
1404
|
-
import { Box as
|
|
1405
|
-
import { jsx as
|
|
1471
|
+
import { Box as Box15, Text as Text15, useInput as useInput6 } from "ink";
|
|
1472
|
+
import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1406
1473
|
var KIND_COLOR = {
|
|
1407
1474
|
tool: COLORS.primary,
|
|
1408
1475
|
prompt: COLORS.badge
|
|
@@ -1433,14 +1500,14 @@ function formatTimestamp(ms) {
|
|
|
1433
1500
|
function Logs() {
|
|
1434
1501
|
const logs = useWizard((s) => s.logs);
|
|
1435
1502
|
const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
|
|
1436
|
-
|
|
1503
|
+
useInput6((_input, key) => {
|
|
1437
1504
|
if (key.upArrow) scroll.scrollBy(-1);
|
|
1438
1505
|
else if (key.downArrow) scroll.scrollBy(1);
|
|
1439
1506
|
});
|
|
1440
1507
|
const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
1441
|
-
return /* @__PURE__ */
|
|
1442
|
-
logs.length === 0 && /* @__PURE__ */
|
|
1443
|
-
/* @__PURE__ */
|
|
1508
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1509
|
+
logs.length === 0 && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "No logs yet." }),
|
|
1510
|
+
/* @__PURE__ */ jsx13(ScrollView, { scroll, children: visible.map((entry) => {
|
|
1444
1511
|
const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
|
|
1445
1512
|
const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
|
|
1446
1513
|
const rawPreview = rawInputText(entry.input);
|
|
@@ -1450,14 +1517,14 @@ function Logs() {
|
|
|
1450
1517
|
const name = truncate2(entry.name, budget);
|
|
1451
1518
|
budget -= name.length;
|
|
1452
1519
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1453
|
-
return /* @__PURE__ */
|
|
1454
|
-
/* @__PURE__ */
|
|
1455
|
-
/* @__PURE__ */
|
|
1456
|
-
preview && /* @__PURE__ */
|
|
1457
|
-
durationText && /* @__PURE__ */
|
|
1520
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1521
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: timestamp }),
|
|
1522
|
+
/* @__PURE__ */ jsx13(Text15, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1523
|
+
preview && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1524
|
+
durationText && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: durationText })
|
|
1458
1525
|
] }, entry.id);
|
|
1459
1526
|
}) }),
|
|
1460
|
-
/* @__PURE__ */
|
|
1527
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1461
1528
|
] });
|
|
1462
1529
|
}
|
|
1463
1530
|
|
|
@@ -1649,7 +1716,7 @@ function track(event, payload) {
|
|
|
1649
1716
|
}
|
|
1650
1717
|
|
|
1651
1718
|
// src/ui/App.tsx
|
|
1652
|
-
import { jsx as
|
|
1719
|
+
import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1653
1720
|
function App() {
|
|
1654
1721
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1655
1722
|
const { exit } = useApp();
|
|
@@ -1657,7 +1724,7 @@ function App() {
|
|
|
1657
1724
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1658
1725
|
const finished = phase === "done" || phase === "error";
|
|
1659
1726
|
const currentStep = steps[currentStepIndex];
|
|
1660
|
-
|
|
1727
|
+
useInput7(
|
|
1661
1728
|
(_input, key) => {
|
|
1662
1729
|
if (key.return) {
|
|
1663
1730
|
exit();
|
|
@@ -1665,7 +1732,7 @@ function App() {
|
|
|
1665
1732
|
},
|
|
1666
1733
|
{ isActive: finished }
|
|
1667
1734
|
);
|
|
1668
|
-
|
|
1735
|
+
useInput7((_input, key) => {
|
|
1669
1736
|
if (phase === "idle" || phase === "authenticating") return;
|
|
1670
1737
|
if (key.tab) {
|
|
1671
1738
|
setShowLogs(!showLogs);
|
|
@@ -1676,8 +1743,8 @@ function App() {
|
|
|
1676
1743
|
});
|
|
1677
1744
|
}
|
|
1678
1745
|
});
|
|
1679
|
-
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1680
|
-
|
|
1746
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
|
|
1747
|
+
useInput7((_input, key) => {
|
|
1681
1748
|
if (escOwnedElsewhere) return;
|
|
1682
1749
|
if (key.escape) {
|
|
1683
1750
|
track("AI Wizard Interaction", {
|
|
@@ -1696,8 +1763,8 @@ function App() {
|
|
|
1696
1763
|
/* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
|
|
1697
1764
|
the whole screen, and the scrolling throws off its cursor arithmetic —
|
|
1698
1765
|
flicker and leftover rows. */
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
|
|
1766
|
+
/* @__PURE__ */ jsxs15(
|
|
1767
|
+
Box16,
|
|
1701
1768
|
{
|
|
1702
1769
|
backgroundColor: COLORS.bg.main,
|
|
1703
1770
|
flexDirection: "row",
|
|
@@ -1705,16 +1772,16 @@ function App() {
|
|
|
1705
1772
|
height: scrollsPastViewport ? void 0 : rows,
|
|
1706
1773
|
overflow: scrollsPastViewport ? "visible" : "hidden",
|
|
1707
1774
|
children: [
|
|
1708
|
-
mainWindowVisible && /* @__PURE__ */
|
|
1709
|
-
|
|
1775
|
+
mainWindowVisible && /* @__PURE__ */ jsxs15(
|
|
1776
|
+
Box16,
|
|
1710
1777
|
{
|
|
1711
1778
|
flexDirection,
|
|
1712
1779
|
width: "100%",
|
|
1713
1780
|
maxHeight: rows,
|
|
1714
1781
|
justifyContent: "space-between",
|
|
1715
1782
|
children: [
|
|
1716
|
-
showLogs ? /* @__PURE__ */
|
|
1717
|
-
|
|
1783
|
+
showLogs ? /* @__PURE__ */ jsx14(Logs, {}) : /* @__PURE__ */ jsxs15(
|
|
1784
|
+
Box16,
|
|
1718
1785
|
{
|
|
1719
1786
|
flexDirection: "column",
|
|
1720
1787
|
paddingX: 4,
|
|
@@ -1722,26 +1789,26 @@ function App() {
|
|
|
1722
1789
|
width: showSidebar ? 70 : "100%",
|
|
1723
1790
|
flexGrow: 1,
|
|
1724
1791
|
children: [
|
|
1725
|
-
phase === "authenticating" && /* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
/* @__PURE__ */
|
|
1792
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
1793
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1794
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1728
1795
|
] }),
|
|
1729
|
-
/* @__PURE__ */
|
|
1730
|
-
/* @__PURE__ */
|
|
1731
|
-
/* @__PURE__ */
|
|
1732
|
-
phase === "running" && showSidebar && /* @__PURE__ */
|
|
1733
|
-
phase === "error" && error && /* @__PURE__ */
|
|
1796
|
+
/* @__PURE__ */ jsx14(CliOutput, {}),
|
|
1797
|
+
/* @__PURE__ */ jsx14(Notices, {}),
|
|
1798
|
+
/* @__PURE__ */ jsx14(PromptInput, {}),
|
|
1799
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx14(CurrentStep, {}) }),
|
|
1800
|
+
phase === "error" && error && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: COLORS.status.error, children: [
|
|
1734
1801
|
"\u2716 ",
|
|
1735
1802
|
error
|
|
1736
1803
|
] }) })
|
|
1737
1804
|
]
|
|
1738
1805
|
}
|
|
1739
1806
|
),
|
|
1740
|
-
showSidebar ? /* @__PURE__ */
|
|
1807
|
+
showSidebar ? /* @__PURE__ */ jsx14(Sidebar, {}) : /* @__PURE__ */ jsx14(Ribbon, {})
|
|
1741
1808
|
]
|
|
1742
1809
|
}
|
|
1743
1810
|
),
|
|
1744
|
-
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */
|
|
1811
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx14(LearnMore, {}) : /* @__PURE__ */ jsx14(Welcome, {}))
|
|
1745
1812
|
]
|
|
1746
1813
|
}
|
|
1747
1814
|
)
|
|
@@ -1821,7 +1888,7 @@ async function ensureConsent() {
|
|
|
1821
1888
|
if (config.aiConsent) return;
|
|
1822
1889
|
const store = useWizard.getState();
|
|
1823
1890
|
const answer = await store.requestUserInput({
|
|
1824
|
-
prompt: "Wizard will make AI-authored changes to this repository.",
|
|
1891
|
+
prompt: "Wizard will make AI-authored changes to this repository, and will propose shell commands to set it up. You approve each command before it runs.",
|
|
1825
1892
|
promptType: "enterToContinue",
|
|
1826
1893
|
options: []
|
|
1827
1894
|
});
|
|
@@ -2576,11 +2643,19 @@ import z13 from "zod";
|
|
|
2576
2643
|
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2577
2644
|
import { join as join7 } from "node:path";
|
|
2578
2645
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2646
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2647
|
+
"node_modules",
|
|
2648
|
+
"dist",
|
|
2649
|
+
"build",
|
|
2650
|
+
"vendor",
|
|
2651
|
+
"venv",
|
|
2652
|
+
"__pycache__",
|
|
2653
|
+
"target"
|
|
2654
|
+
]);
|
|
2579
2655
|
async function walkFiles(dir) {
|
|
2580
|
-
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2581
2656
|
const out = [];
|
|
2582
2657
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2583
|
-
if (e.name.startsWith(".") ||
|
|
2658
|
+
if (e.name.startsWith(".") || SKIP_DIRS.has(e.name)) continue;
|
|
2584
2659
|
const full = join7(dir, e.name);
|
|
2585
2660
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2586
2661
|
else if (e.isFile()) out.push(full);
|
|
@@ -2634,92 +2709,194 @@ function searchFilesTool(ctx) {
|
|
|
2634
2709
|
});
|
|
2635
2710
|
}
|
|
2636
2711
|
|
|
2637
|
-
// src/lib/tools/
|
|
2712
|
+
// src/lib/tools/runShell.ts
|
|
2638
2713
|
import { tool as tool8 } from "ai";
|
|
2639
2714
|
import z14 from "zod";
|
|
2715
|
+
import { relative as relative2 } from "node:path";
|
|
2640
2716
|
|
|
2641
|
-
// src/lib/tools/utils/
|
|
2717
|
+
// src/lib/tools/utils/runShell.ts
|
|
2642
2718
|
import { spawn as spawn2 } from "node:child_process";
|
|
2643
|
-
|
|
2719
|
+
|
|
2720
|
+
// src/lib/tools/context.ts
|
|
2721
|
+
var DEFAULT_TOOL_LIMITS = {
|
|
2722
|
+
list: 10,
|
|
2723
|
+
search: 10,
|
|
2724
|
+
read: 20,
|
|
2725
|
+
match: 100,
|
|
2726
|
+
shell: 30
|
|
2727
|
+
};
|
|
2728
|
+
var DEFAULT_SHELL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2729
|
+
async function refuseByDefault() {
|
|
2730
|
+
return "reject";
|
|
2731
|
+
}
|
|
2732
|
+
function createShellContext(overrides = {}) {
|
|
2733
|
+
return {
|
|
2734
|
+
env: async () => ({}),
|
|
2735
|
+
timeoutMs: DEFAULT_SHELL_TIMEOUT_MS,
|
|
2736
|
+
approved: /* @__PURE__ */ new Set(),
|
|
2737
|
+
executions: [],
|
|
2738
|
+
approve: refuseByDefault,
|
|
2739
|
+
...overrides
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd(), shell2 = createShellContext()) {
|
|
2743
|
+
return {
|
|
2744
|
+
root: cwd,
|
|
2745
|
+
cwd,
|
|
2746
|
+
limits: { ...limits },
|
|
2747
|
+
counts: { list: 0, search: 0, read: 0, shell: 0 },
|
|
2748
|
+
shell: shell2
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// src/lib/tools/utils/runShell.ts
|
|
2753
|
+
var SIGKILL_DELAY_MS = 5e3;
|
|
2754
|
+
var HEAD_CHARS = 4e3;
|
|
2755
|
+
var TAIL_CHARS = 8e3;
|
|
2756
|
+
function truncateOutput(output) {
|
|
2757
|
+
if (output.length <= HEAD_CHARS + TAIL_CHARS) return output;
|
|
2758
|
+
const omitted = output.length - HEAD_CHARS - TAIL_CHARS;
|
|
2759
|
+
return [
|
|
2760
|
+
output.slice(0, HEAD_CHARS),
|
|
2761
|
+
`
|
|
2762
|
+
\u2026 [${omitted} characters omitted] \u2026
|
|
2763
|
+
`,
|
|
2764
|
+
output.slice(-TAIL_CHARS)
|
|
2765
|
+
].join("");
|
|
2766
|
+
}
|
|
2767
|
+
function runShell(command, opts) {
|
|
2768
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
|
|
2769
|
+
const startedAt = Date.now();
|
|
2644
2770
|
return new Promise((resolve4) => {
|
|
2645
2771
|
let output = "";
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2772
|
+
let timedOut = false;
|
|
2773
|
+
let settled = false;
|
|
2774
|
+
const child = spawn2(command, {
|
|
2775
|
+
shell: true,
|
|
2776
|
+
cwd: opts.cwd,
|
|
2777
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2778
|
+
env: { ...process.env, ...opts.env }
|
|
2649
2779
|
});
|
|
2780
|
+
const finish = (exitCode) => {
|
|
2781
|
+
if (settled) return;
|
|
2782
|
+
settled = true;
|
|
2783
|
+
clearTimeout(timer);
|
|
2784
|
+
clearTimeout(killTimer);
|
|
2785
|
+
resolve4({
|
|
2786
|
+
exitCode,
|
|
2787
|
+
output: truncateOutput(output.trim()),
|
|
2788
|
+
timedOut,
|
|
2789
|
+
durationMs: Date.now() - startedAt
|
|
2790
|
+
});
|
|
2791
|
+
};
|
|
2792
|
+
let killTimer;
|
|
2793
|
+
const timer = setTimeout(() => {
|
|
2794
|
+
timedOut = true;
|
|
2795
|
+
output += `
|
|
2796
|
+
[timed out after ${timeoutMs}ms]`;
|
|
2797
|
+
child.kill("SIGTERM");
|
|
2798
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), SIGKILL_DELAY_MS);
|
|
2799
|
+
}, timeoutMs);
|
|
2650
2800
|
child.stdout?.on("data", (d) => output += d);
|
|
2651
2801
|
child.stderr?.on("data", (d) => output += d);
|
|
2652
|
-
child.on(
|
|
2653
|
-
|
|
2654
|
-
(
|
|
2655
|
-
);
|
|
2656
|
-
child.on("close", (code) =>
|
|
2802
|
+
child.on("error", (err) => {
|
|
2803
|
+
output += `Failed to run ${command}: ${err.message}`;
|
|
2804
|
+
finish(1);
|
|
2805
|
+
});
|
|
2806
|
+
child.on("close", (code) => finish(code ?? 1));
|
|
2657
2807
|
});
|
|
2658
2808
|
}
|
|
2659
2809
|
|
|
2660
|
-
// src/lib/tools/
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
]
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2679
|
-
}
|
|
2680
|
-
async function detectPackageManager(cwd) {
|
|
2681
|
-
try {
|
|
2682
|
-
const pkg = await readPackageJson(cwd);
|
|
2683
|
-
if (pkg.packageManager) return packageManagerFrom(pkg);
|
|
2684
|
-
} catch {
|
|
2685
|
-
}
|
|
2686
|
-
return packageManagerFromLockfile(cwd) ?? "npm";
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
// src/lib/tools/repoVerification.ts
|
|
2690
|
-
var VERIFICATION_SCRIPT_CANDIDATES = ["lint", "typecheck", "check"];
|
|
2691
|
-
async function runRepoVerificationCheck() {
|
|
2692
|
-
let pkg;
|
|
2693
|
-
try {
|
|
2694
|
-
pkg = await readPackageJson();
|
|
2695
|
-
} catch (err) {
|
|
2696
|
-
const limitation = `Could not read package.json to detect verification conventions: ${err.message}`;
|
|
2697
|
-
return { ok: false, checks: [], limitation };
|
|
2698
|
-
}
|
|
2699
|
-
const scripts = pkg.scripts ?? {};
|
|
2700
|
-
const present = VERIFICATION_SCRIPT_CANDIDATES.filter((s) => s in scripts);
|
|
2701
|
-
if (present.length === 0) {
|
|
2702
|
-
const limitation = `No verification script found in package.json (looked for: ${VERIFICATION_SCRIPT_CANDIDATES.join(", ")}).`;
|
|
2703
|
-
return { ok: false, checks: [], limitation };
|
|
2704
|
-
}
|
|
2705
|
-
const pm = await detectPackageManager(process.cwd());
|
|
2706
|
-
const checks = [];
|
|
2707
|
-
for (const script of present) {
|
|
2708
|
-
const command = `${pm} run ${script}`;
|
|
2709
|
-
const { code, output } = await runCommand(pm, ["run", script]);
|
|
2710
|
-
checks.push({ command, exitCode: code, ok: code === 0, output: output.trim() });
|
|
2711
|
-
}
|
|
2712
|
-
return { ok: checks.every((c) => c.ok), checks };
|
|
2810
|
+
// src/lib/tools/runShell.ts
|
|
2811
|
+
function approvalKey(cwd, command) {
|
|
2812
|
+
return `${cwd}\0${command}`;
|
|
2813
|
+
}
|
|
2814
|
+
function storeApproval(root) {
|
|
2815
|
+
return async (req) => {
|
|
2816
|
+
const rel = relative2(root, req.cwd);
|
|
2817
|
+
const answer = await useWizard.getState().requestUserInput({
|
|
2818
|
+
prompt: "Run this command?",
|
|
2819
|
+
promptType: "commandApproval",
|
|
2820
|
+
options: [],
|
|
2821
|
+
command: {
|
|
2822
|
+
...req,
|
|
2823
|
+
cwd: rel === "" || rel.startsWith("..") ? req.cwd : rel
|
|
2824
|
+
}
|
|
2825
|
+
});
|
|
2826
|
+
return answer === "approve" || answer === "always" ? answer : "reject";
|
|
2827
|
+
};
|
|
2713
2828
|
}
|
|
2714
|
-
|
|
2715
|
-
// src/lib/tools/verifyImplementation.ts
|
|
2716
|
-
function verifyImplementationTool() {
|
|
2829
|
+
function runShellTool(ctx) {
|
|
2717
2830
|
return tool8({
|
|
2718
|
-
description: "Run the
|
|
2719
|
-
inputSchema: z14.object(
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2831
|
+
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.",
|
|
2832
|
+
inputSchema: z14.object({
|
|
2833
|
+
command: z14.string().describe(
|
|
2834
|
+
"The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
|
|
2835
|
+
),
|
|
2836
|
+
cwd: z14.string().optional().describe(
|
|
2837
|
+
"Directory to run in, relative to the project root. Defaults to the project root."
|
|
2838
|
+
),
|
|
2839
|
+
explanation: z14.string().describe(
|
|
2840
|
+
"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."
|
|
2841
|
+
)
|
|
2842
|
+
}),
|
|
2843
|
+
execute: async ({ command, cwd, explanation }) => {
|
|
2844
|
+
if (++ctx.counts.shell > ctx.limits.shell) {
|
|
2845
|
+
return `Refused: command limit (${ctx.limits.shell}) reached. Stop running commands and report what you have.`;
|
|
2846
|
+
}
|
|
2847
|
+
const resolved2 = resolveInRoot(ctx, cwd ?? ".");
|
|
2848
|
+
if (!resolved2.ok) return resolved2.error;
|
|
2849
|
+
logger.info({ command, cwd: resolved2.target }, "called runShell tool");
|
|
2850
|
+
const key = approvalKey(resolved2.target, command);
|
|
2851
|
+
const decision = ctx.shell.approved.has(key) ? "approve" : await ctx.shell.approve({
|
|
2852
|
+
command,
|
|
2853
|
+
cwd: resolved2.target,
|
|
2854
|
+
explanation
|
|
2855
|
+
});
|
|
2856
|
+
if (decision === "reject") {
|
|
2857
|
+
ctx.shell.executions.push({
|
|
2858
|
+
command,
|
|
2859
|
+
cwd: resolved2.target,
|
|
2860
|
+
approved: false
|
|
2861
|
+
});
|
|
2862
|
+
logger.info({ command }, "runShell: user rejected the command");
|
|
2863
|
+
return "The user rejected this command. Do not retry it. Propose a different command, or report the limitation via reportStatus.";
|
|
2864
|
+
}
|
|
2865
|
+
if (decision === "always") ctx.shell.approved.add(key);
|
|
2866
|
+
useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
|
|
2867
|
+
const env = await ctx.shell.env().catch((err) => {
|
|
2868
|
+
logger.warn({ err, command }, "runShell: could not resolve command env");
|
|
2869
|
+
return {};
|
|
2870
|
+
});
|
|
2871
|
+
const run2 = await (ctx.shell.run ?? runShell)(command, {
|
|
2872
|
+
cwd: resolved2.target,
|
|
2873
|
+
env,
|
|
2874
|
+
timeoutMs: ctx.shell.timeoutMs
|
|
2875
|
+
});
|
|
2876
|
+
logger.info(
|
|
2877
|
+
{
|
|
2878
|
+
command,
|
|
2879
|
+
exitCode: run2.exitCode,
|
|
2880
|
+
timedOut: run2.timedOut,
|
|
2881
|
+
durationMs: run2.durationMs
|
|
2882
|
+
},
|
|
2883
|
+
"runShell finished"
|
|
2884
|
+
);
|
|
2885
|
+
await markInteraction();
|
|
2886
|
+
ctx.shell.executions.push({
|
|
2887
|
+
command,
|
|
2888
|
+
cwd: resolved2.target,
|
|
2889
|
+
approved: true,
|
|
2890
|
+
exitCode: run2.exitCode,
|
|
2891
|
+
output: run2.output,
|
|
2892
|
+
timedOut: run2.timedOut,
|
|
2893
|
+
durationMs: run2.durationMs
|
|
2894
|
+
});
|
|
2895
|
+
return {
|
|
2896
|
+
exitCode: run2.exitCode,
|
|
2897
|
+
timedOut: run2.timedOut,
|
|
2898
|
+
output: run2.output
|
|
2899
|
+
};
|
|
2723
2900
|
}
|
|
2724
2901
|
});
|
|
2725
2902
|
}
|
|
@@ -2810,7 +2987,7 @@ function generateRecordTool(ctx) {
|
|
|
2810
2987
|
return {
|
|
2811
2988
|
filePath: relPath,
|
|
2812
2989
|
count: records.length,
|
|
2813
|
-
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime
|
|
2990
|
+
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime using your language's standard JSON support \u2014 do not inline the records as literals.`
|
|
2814
2991
|
};
|
|
2815
2992
|
} catch (err) {
|
|
2816
2993
|
return `Error generating records: ${err.message}`;
|
|
@@ -2838,22 +3015,6 @@ function notifyUserTool() {
|
|
|
2838
3015
|
});
|
|
2839
3016
|
}
|
|
2840
3017
|
|
|
2841
|
-
// src/lib/tools/context.ts
|
|
2842
|
-
var DEFAULT_TOOL_LIMITS = {
|
|
2843
|
-
list: 10,
|
|
2844
|
-
search: 10,
|
|
2845
|
-
read: 20,
|
|
2846
|
-
match: 100
|
|
2847
|
-
};
|
|
2848
|
-
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd()) {
|
|
2849
|
-
return {
|
|
2850
|
-
root: cwd,
|
|
2851
|
-
cwd,
|
|
2852
|
-
limits,
|
|
2853
|
-
counts: { list: 0, search: 0, read: 0 }
|
|
2854
|
-
};
|
|
2855
|
-
}
|
|
2856
|
-
|
|
2857
3018
|
// src/lib/tools/index.ts
|
|
2858
3019
|
function withLogging(name, def) {
|
|
2859
3020
|
const execute = def.execute;
|
|
@@ -2885,10 +3046,7 @@ function createTools(ctx, { output, tools }) {
|
|
|
2885
3046
|
writeCredentialsTool(ctx)
|
|
2886
3047
|
),
|
|
2887
3048
|
searchFiles: withLogging("searchFiles", searchFilesTool(ctx)),
|
|
2888
|
-
|
|
2889
|
-
"verifyImplementation",
|
|
2890
|
-
verifyImplementationTool()
|
|
2891
|
-
),
|
|
3049
|
+
runShell: withLogging("runShell", runShellTool(ctx)),
|
|
2892
3050
|
generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
|
|
2893
3051
|
notifyUser: withLogging("notifyUser", notifyUserTool())
|
|
2894
3052
|
};
|
|
@@ -2923,7 +3081,7 @@ async function runAgent(req) {
|
|
|
2923
3081
|
baseURL: PROXY_BASE_URL,
|
|
2924
3082
|
fetch: proxyFetch
|
|
2925
3083
|
});
|
|
2926
|
-
const toolContext = createToolContext();
|
|
3084
|
+
const toolContext = req.toolContext ?? createToolContext();
|
|
2927
3085
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
2928
3086
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
2929
3087
|
const instructions = [
|
|
@@ -2988,7 +3146,11 @@ async function runAgent(req) {
|
|
|
2988
3146
|
"runAgent finished"
|
|
2989
3147
|
);
|
|
2990
3148
|
logger.info(
|
|
2991
|
-
{
|
|
3149
|
+
{
|
|
3150
|
+
counts: toolContext.counts,
|
|
3151
|
+
limits: toolContext.limits,
|
|
3152
|
+
commandsRun: toolContext.shell.executions.length
|
|
3153
|
+
},
|
|
2992
3154
|
"tool usage"
|
|
2993
3155
|
);
|
|
2994
3156
|
const toolResults = await stream.toolResults;
|
|
@@ -3014,8 +3176,8 @@ var detectLanguageSchema = z19.object({
|
|
|
3014
3176
|
var detectLanguage = () => runAgent({
|
|
3015
3177
|
instructions: [
|
|
3016
3178
|
"Analyze the codebase and determine the programming languages and frameworks used",
|
|
3017
|
-
"If a superset language is found, exclude the subset language.
|
|
3018
|
-
"If a meta-framework is used, exclude the framework. Next
|
|
3179
|
+
"If a superset language is found, exclude the subset language (e.g. TypeScript over JavaScript).",
|
|
3180
|
+
"If a meta-framework is used, exclude the framework it builds on (e.g. Next.js over React, Rails over Rack).",
|
|
3019
3181
|
"Return the exact version",
|
|
3020
3182
|
"Exclude things like CSS frameworks, build tools, or testing frameworks",
|
|
3021
3183
|
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
@@ -3113,7 +3275,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3113
3275
|
// package.json
|
|
3114
3276
|
var package_default = {
|
|
3115
3277
|
name: "@algolia/wizard",
|
|
3116
|
-
version: "0.9.0-rc.
|
|
3278
|
+
version: "0.9.0-rc.93.91",
|
|
3117
3279
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3118
3280
|
type: "module",
|
|
3119
3281
|
engines: {
|
|
@@ -3262,9 +3424,11 @@ var CURATED_FRAMEWORKS = [
|
|
|
3262
3424
|
"Next.js",
|
|
3263
3425
|
"React",
|
|
3264
3426
|
"Vue",
|
|
3265
|
-
"
|
|
3266
|
-
"
|
|
3267
|
-
"
|
|
3427
|
+
"Vanilla JS",
|
|
3428
|
+
"Django",
|
|
3429
|
+
"Laravel",
|
|
3430
|
+
"Rails",
|
|
3431
|
+
"Symfony"
|
|
3268
3432
|
];
|
|
3269
3433
|
var OTHER_OPTION = "Other";
|
|
3270
3434
|
var normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -3277,12 +3441,15 @@ var FRAMEWORK_ALIASES = {
|
|
|
3277
3441
|
vuejs: "vue",
|
|
3278
3442
|
angular: "angular",
|
|
3279
3443
|
angularjs: "angular",
|
|
3280
|
-
svelte: "svelte",
|
|
3281
|
-
sveltekit: "svelte",
|
|
3282
3444
|
vanillajs: "vanillajs",
|
|
3283
3445
|
vanilla: "vanillajs",
|
|
3284
3446
|
javascript: "vanillajs",
|
|
3285
|
-
js: "vanillajs"
|
|
3447
|
+
js: "vanillajs",
|
|
3448
|
+
django: "django",
|
|
3449
|
+
laravel: "laravel",
|
|
3450
|
+
rails: "rails",
|
|
3451
|
+
rubyonrails: "rails",
|
|
3452
|
+
symfony: "symfony"
|
|
3286
3453
|
};
|
|
3287
3454
|
var isSameFramework = (a, b) => {
|
|
3288
3455
|
const x = FRAMEWORK_ALIASES[normalize(a)] ?? normalize(a);
|
|
@@ -3516,16 +3683,9 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3516
3683
|
import z26 from "zod";
|
|
3517
3684
|
|
|
3518
3685
|
// src/lib/worktree.ts
|
|
3519
|
-
import { execFile
|
|
3520
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3521
|
-
import {
|
|
3522
|
-
basename as basename2,
|
|
3523
|
-
dirname as dirname7,
|
|
3524
|
-
isAbsolute as isAbsolute2,
|
|
3525
|
-
join as join9,
|
|
3526
|
-
relative as relative2,
|
|
3527
|
-
resolve as resolve3
|
|
3528
|
-
} from "node:path";
|
|
3686
|
+
import { execFile } from "node:child_process";
|
|
3687
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile6, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3688
|
+
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join8, resolve as resolve3 } from "node:path";
|
|
3529
3689
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
3530
3690
|
var MAX_WIZARD_WORKTREES = 3;
|
|
3531
3691
|
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
@@ -3556,7 +3716,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3556
3716
|
return out.trim().length > 0;
|
|
3557
3717
|
}
|
|
3558
3718
|
async function pruneOldWorktrees(repoRoot) {
|
|
3559
|
-
const dir =
|
|
3719
|
+
const dir = join8(stateDir(repoRoot), "worktrees");
|
|
3560
3720
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3561
3721
|
for (const slug of stale) {
|
|
3562
3722
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3567,7 +3727,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3567
3727
|
"worktree",
|
|
3568
3728
|
"remove",
|
|
3569
3729
|
"--force",
|
|
3570
|
-
|
|
3730
|
+
join8(dir, slug)
|
|
3571
3731
|
]);
|
|
3572
3732
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3573
3733
|
} catch (err) {
|
|
@@ -3581,113 +3741,13 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3581
3741
|
async function createWorktree(repoRoot) {
|
|
3582
3742
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3583
3743
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3584
|
-
const path =
|
|
3744
|
+
const path = join8(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3585
3745
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3586
3746
|
await pruneOldWorktrees(repoRoot);
|
|
3587
3747
|
await mkdir6(dirname7(path), { recursive: true });
|
|
3588
3748
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3589
3749
|
return { path, branch };
|
|
3590
3750
|
}
|
|
3591
|
-
async function installWorktreeDeps(worktreePath) {
|
|
3592
|
-
try {
|
|
3593
|
-
await readPackageJson(worktreePath);
|
|
3594
|
-
} catch {
|
|
3595
|
-
return { ok: true, output: "no package.json; skipped install" };
|
|
3596
|
-
}
|
|
3597
|
-
const pm = await detectPackageManager(worktreePath);
|
|
3598
|
-
return new Promise((resolve4) => {
|
|
3599
|
-
let output = "";
|
|
3600
|
-
const child = spawn3(pm, ["install"], {
|
|
3601
|
-
cwd: worktreePath,
|
|
3602
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3603
|
-
});
|
|
3604
|
-
child.stdout?.on("data", (d) => output += d);
|
|
3605
|
-
child.stderr?.on("data", (d) => output += d);
|
|
3606
|
-
child.on(
|
|
3607
|
-
"error",
|
|
3608
|
-
(err) => resolve4({
|
|
3609
|
-
ok: false,
|
|
3610
|
-
output: `Failed to run ${pm} install: ${err.message}`
|
|
3611
|
-
})
|
|
3612
|
-
);
|
|
3613
|
-
child.on(
|
|
3614
|
-
"close",
|
|
3615
|
-
(code) => resolve4({ ok: code === 0, output: output.trim() })
|
|
3616
|
-
);
|
|
3617
|
-
});
|
|
3618
|
-
}
|
|
3619
|
-
var INGEST_RUNTIMES = ["node", "python", "python3", "bun"];
|
|
3620
|
-
function validateIngestEntrypoint(worktreePath, entrypoint) {
|
|
3621
|
-
if (!entrypoint || entrypoint.startsWith("-")) {
|
|
3622
|
-
return {
|
|
3623
|
-
ok: false,
|
|
3624
|
-
reason: `entrypoint "${entrypoint}" is not a plain file path`
|
|
3625
|
-
};
|
|
3626
|
-
}
|
|
3627
|
-
const target = resolve3(worktreePath, entrypoint);
|
|
3628
|
-
const rel = relative2(worktreePath, target);
|
|
3629
|
-
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
3630
|
-
return {
|
|
3631
|
-
ok: false,
|
|
3632
|
-
reason: `entrypoint "${entrypoint}" resolves outside the worktree`
|
|
3633
|
-
};
|
|
3634
|
-
}
|
|
3635
|
-
return { ok: true, target };
|
|
3636
|
-
}
|
|
3637
|
-
async function runIngestScript(worktreePath, runtime, entrypoint, env = {}) {
|
|
3638
|
-
if (!INGEST_RUNTIMES.includes(runtime)) {
|
|
3639
|
-
return {
|
|
3640
|
-
ran: false,
|
|
3641
|
-
ok: false,
|
|
3642
|
-
output: "",
|
|
3643
|
-
reason: `runtime "${runtime}" is not an allowed interpreter (${INGEST_RUNTIMES.join(", ")})`
|
|
3644
|
-
};
|
|
3645
|
-
}
|
|
3646
|
-
const validated = validateIngestEntrypoint(worktreePath, entrypoint);
|
|
3647
|
-
if (!validated.ok) {
|
|
3648
|
-
return { ran: false, ok: false, output: "", reason: validated.reason };
|
|
3649
|
-
}
|
|
3650
|
-
try {
|
|
3651
|
-
if (!(await stat2(validated.target)).isFile()) {
|
|
3652
|
-
return {
|
|
3653
|
-
ran: false,
|
|
3654
|
-
ok: false,
|
|
3655
|
-
output: "",
|
|
3656
|
-
reason: `entrypoint "${entrypoint}" is not a file`
|
|
3657
|
-
};
|
|
3658
|
-
}
|
|
3659
|
-
} catch {
|
|
3660
|
-
return {
|
|
3661
|
-
ran: false,
|
|
3662
|
-
ok: false,
|
|
3663
|
-
output: "",
|
|
3664
|
-
reason: `entrypoint "${entrypoint}" does not exist`
|
|
3665
|
-
};
|
|
3666
|
-
}
|
|
3667
|
-
return new Promise((resolveRun) => {
|
|
3668
|
-
let output = "";
|
|
3669
|
-
const child = spawn3(runtime, [entrypoint], {
|
|
3670
|
-
cwd: worktreePath,
|
|
3671
|
-
shell: false,
|
|
3672
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3673
|
-
env: { ...process.env, ...env }
|
|
3674
|
-
});
|
|
3675
|
-
child.stdout?.on("data", (d) => output += d);
|
|
3676
|
-
child.stderr?.on("data", (d) => output += d);
|
|
3677
|
-
child.on(
|
|
3678
|
-
"error",
|
|
3679
|
-
(err) => resolveRun({
|
|
3680
|
-
ran: true,
|
|
3681
|
-
ok: false,
|
|
3682
|
-
output: `Failed to run ${runtime} ${entrypoint}: ${err.message}`
|
|
3683
|
-
})
|
|
3684
|
-
);
|
|
3685
|
-
child.on(
|
|
3686
|
-
"close",
|
|
3687
|
-
(code) => resolveRun({ ran: true, ok: code === 0, output: output.trim() })
|
|
3688
|
-
);
|
|
3689
|
-
});
|
|
3690
|
-
}
|
|
3691
3751
|
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
3692
3752
|
const trimmed = sourcePath.trim();
|
|
3693
3753
|
if (!trimmed) {
|
|
@@ -3701,8 +3761,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3701
3761
|
} catch {
|
|
3702
3762
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3703
3763
|
}
|
|
3704
|
-
const relPath =
|
|
3705
|
-
const dest =
|
|
3764
|
+
const relPath = join8(ingestDir, basename2(source));
|
|
3765
|
+
const dest = join8(worktreePath, relPath);
|
|
3706
3766
|
try {
|
|
3707
3767
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3708
3768
|
await copyFile(source, dest);
|
|
@@ -3720,7 +3780,7 @@ function hasEnvVar(content, name) {
|
|
|
3720
3780
|
async function readEnvVar(worktreePath, name) {
|
|
3721
3781
|
let content;
|
|
3722
3782
|
try {
|
|
3723
|
-
content = await
|
|
3783
|
+
content = await readFile6(join8(worktreePath, ".env"), "utf8");
|
|
3724
3784
|
} catch (err) {
|
|
3725
3785
|
if (err.code !== "ENOENT") throw err;
|
|
3726
3786
|
return void 0;
|
|
@@ -3735,10 +3795,10 @@ async function readEnvVar(worktreePath, name) {
|
|
|
3735
3795
|
return value;
|
|
3736
3796
|
}
|
|
3737
3797
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3738
|
-
const target =
|
|
3798
|
+
const target = join8(worktreePath, ".env");
|
|
3739
3799
|
let existing = "";
|
|
3740
3800
|
try {
|
|
3741
|
-
existing = await
|
|
3801
|
+
existing = await readFile6(target, "utf8");
|
|
3742
3802
|
} catch (err) {
|
|
3743
3803
|
if (err.code !== "ENOENT") throw err;
|
|
3744
3804
|
}
|
|
@@ -3807,15 +3867,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3807
3867
|
}
|
|
3808
3868
|
|
|
3809
3869
|
// src/lib/algoliaDocs.ts
|
|
3810
|
-
import { readFileSync, readdirSync, existsSync
|
|
3811
|
-
import { dirname as dirname8, join as
|
|
3870
|
+
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
3871
|
+
import { dirname as dirname8, join as join9 } from "node:path";
|
|
3812
3872
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3813
|
-
var DOCS_SUBPATH =
|
|
3873
|
+
var DOCS_SUBPATH = join9("docs", "algolia-sdk");
|
|
3814
3874
|
function findDocsDir() {
|
|
3815
3875
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3816
3876
|
for (; ; ) {
|
|
3817
|
-
const candidate =
|
|
3818
|
-
if (
|
|
3877
|
+
const candidate = join9(dir, DOCS_SUBPATH);
|
|
3878
|
+
if (existsSync(candidate)) return candidate;
|
|
3819
3879
|
const parent = dirname8(dir);
|
|
3820
3880
|
if (parent === dir) return void 0;
|
|
3821
3881
|
dir = parent;
|
|
@@ -3837,7 +3897,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3837
3897
|
);
|
|
3838
3898
|
return "";
|
|
3839
3899
|
}
|
|
3840
|
-
return readFileSync(
|
|
3900
|
+
return readFileSync(join9(docsDir, files[0]), "utf8").trim();
|
|
3841
3901
|
}
|
|
3842
3902
|
function getNamedDoc(name, language) {
|
|
3843
3903
|
const docsDir = findDocsDir();
|
|
@@ -3845,14 +3905,15 @@ function getNamedDoc(name, language) {
|
|
|
3845
3905
|
logger.warn("docs/algolia-sdk not found");
|
|
3846
3906
|
return "";
|
|
3847
3907
|
}
|
|
3848
|
-
const file =
|
|
3849
|
-
if (!
|
|
3908
|
+
const file = join9(docsDir, `${name}-${language}.md`);
|
|
3909
|
+
if (!existsSync(file)) {
|
|
3850
3910
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3851
3911
|
return "";
|
|
3852
3912
|
}
|
|
3853
3913
|
return readFileSync(file, "utf8").trim();
|
|
3854
3914
|
}
|
|
3855
3915
|
function getFrameworkSpecificDoc(frameworks) {
|
|
3916
|
+
if (frameworks.length === 0) return "";
|
|
3856
3917
|
const fw = frameworks.map((f) => f.toLowerCase());
|
|
3857
3918
|
if (fw.includes("vue") || fw.includes("nuxt")) {
|
|
3858
3919
|
return loadAlgoliaDoc("vue");
|
|
@@ -3860,9 +3921,6 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
3860
3921
|
if (fw.includes("react") || fw.includes("next.js")) {
|
|
3861
3922
|
return loadAlgoliaDoc("react");
|
|
3862
3923
|
}
|
|
3863
|
-
if (fw.includes("angular")) {
|
|
3864
|
-
return loadAlgoliaDoc("angular");
|
|
3865
|
-
}
|
|
3866
3924
|
return loadAlgoliaDoc("js");
|
|
3867
3925
|
}
|
|
3868
3926
|
|
|
@@ -3890,11 +3948,7 @@ var implementSchema = z26.object({
|
|
|
3890
3948
|
});
|
|
3891
3949
|
var implementationOutputSchema = z26.object({
|
|
3892
3950
|
summary: z26.string(),
|
|
3893
|
-
|
|
3894
|
-
// free-form command string. `runtime` is allowlisted and `entrypoint` is
|
|
3895
|
-
// validated worktree-relative, so the agent cannot inject extra commands.
|
|
3896
|
-
runtime: z26.enum(INGEST_RUNTIMES).optional(),
|
|
3897
|
-
entrypoint: z26.string().optional()
|
|
3951
|
+
ingestCommand: z26.string().optional()
|
|
3898
3952
|
});
|
|
3899
3953
|
var verificationOutputSchema = z26.object({
|
|
3900
3954
|
summary: z26.string(),
|
|
@@ -3904,30 +3958,35 @@ var verificationOutputSchema = z26.object({
|
|
|
3904
3958
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3905
3959
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
3906
3960
|
var INGEST_DIR = ".algolia-wizard";
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
if (names.some((n) => n.includes("react") || n.includes("next")))
|
|
3911
|
-
return "React";
|
|
3912
|
-
if (names.some((n) => n.includes("angular"))) return "Angular";
|
|
3913
|
-
return "JavaScript";
|
|
3914
|
-
}
|
|
3915
|
-
function frameworksForDoc(framework) {
|
|
3916
|
-
switch (framework) {
|
|
3917
|
-
case "React":
|
|
3918
|
-
return ["react"];
|
|
3919
|
-
case "Vue":
|
|
3920
|
-
return ["vue"];
|
|
3921
|
-
case "Angular":
|
|
3922
|
-
return ["angular"];
|
|
3923
|
-
case "JavaScript":
|
|
3924
|
-
return [];
|
|
3925
|
-
}
|
|
3961
|
+
var JS_LANGUAGES = ["javascript", "typescript", "jsx", "tsx", "node"];
|
|
3962
|
+
function lower(entries) {
|
|
3963
|
+
return entries.map((entry) => entry.name.toLowerCase());
|
|
3926
3964
|
}
|
|
3927
|
-
function
|
|
3928
|
-
|
|
3929
|
-
(
|
|
3965
|
+
function isJsProject(language) {
|
|
3966
|
+
return lower(language.languages).some(
|
|
3967
|
+
(name) => JS_LANGUAGES.some((js) => name.includes(js))
|
|
3930
3968
|
);
|
|
3969
|
+
}
|
|
3970
|
+
var UI_FRAMEWORKS = [
|
|
3971
|
+
{ match: ["vue", "nuxt"], target: "Vue", doc: "vue" },
|
|
3972
|
+
{ match: ["react", "next"], target: "React", doc: "react" },
|
|
3973
|
+
{ match: ["angular"], target: "Angular" }
|
|
3974
|
+
];
|
|
3975
|
+
function matchUiFramework(language) {
|
|
3976
|
+
const names = lower(language.frameworks);
|
|
3977
|
+
return UI_FRAMEWORKS.find(
|
|
3978
|
+
(ui) => ui.match.some((needle) => names.some((name) => name.includes(needle)))
|
|
3979
|
+
);
|
|
3980
|
+
}
|
|
3981
|
+
function searchUiTarget(language) {
|
|
3982
|
+
return matchUiFramework(language)?.target ?? language.frameworks[0]?.name ?? (isJsProject(language) ? "JavaScript" : "this project");
|
|
3983
|
+
}
|
|
3984
|
+
function frameworksForDoc(language) {
|
|
3985
|
+
if (!isJsProject(language)) return [];
|
|
3986
|
+
return [matchUiFramework(language)?.doc ?? "js"];
|
|
3987
|
+
}
|
|
3988
|
+
function publicEnvPrefix(language) {
|
|
3989
|
+
const frameworkNames = lower(language.frameworks);
|
|
3931
3990
|
if (frameworkNames.some((name) => name.includes("next"))) {
|
|
3932
3991
|
return "NEXT_PUBLIC_";
|
|
3933
3992
|
}
|
|
@@ -3940,7 +3999,7 @@ function publicEnvPrefix(language) {
|
|
|
3940
3999
|
if (frameworkNames.some((name) => name.includes("vite"))) {
|
|
3941
4000
|
return "VITE_";
|
|
3942
4001
|
}
|
|
3943
|
-
return "PUBLIC_";
|
|
4002
|
+
return isJsProject(language) ? "PUBLIC_" : "";
|
|
3944
4003
|
}
|
|
3945
4004
|
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
3946
4005
|
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
@@ -3979,7 +4038,10 @@ function baseInstructions(input) {
|
|
|
3979
4038
|
// index-scoped keys then reject with a 403.
|
|
3980
4039
|
`Target Algolia index, to be used exactly as written \u2014 never renamed, re-cased, prefixed, or suffixed: "${input.targetIndex}"`,
|
|
3981
4040
|
`Project languages and frameworks: ${JSON.stringify(input.language)}`,
|
|
3982
|
-
"Make minimal, idiomatic changes; do not touch unrelated code."
|
|
4041
|
+
"Make minimal, idiomatic changes; do not touch unrelated code.",
|
|
4042
|
+
`Commands run through a shell on ${process.platform}. Write commands that work there.`,
|
|
4043
|
+
"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.",
|
|
4044
|
+
"runShell needs the developer to approve each command, so give every call a clear `explanation` naming what it does and any side effect. If a command is rejected, do not retry it \u2014 take a different approach or report the limitation."
|
|
3983
4045
|
];
|
|
3984
4046
|
}
|
|
3985
4047
|
function sourceSpecificInstructions(input) {
|
|
@@ -3999,12 +4061,21 @@ function sourceSpecificInstructions(input) {
|
|
|
3999
4061
|
generated: [
|
|
4000
4062
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
4001
4063
|
"Call the generateRecord tool once per entity (entityName, attributes, count 20-50); it invents the values and unique objectIDs and writes them to a JSON file in the worktree, returning the file path. Do not write records or objectIDs yourself.",
|
|
4002
|
-
"In the script, read and parse each returned file path at runtime
|
|
4064
|
+
"In the script, read and parse each returned file path at runtime using your language's standard JSON support, instead of inlining the records as literals.",
|
|
4003
4065
|
"Add a prominent TODO where the developer swaps the generated records (and the JSON file under `.algolia-wizard/data/`) for their real record source."
|
|
4004
4066
|
]
|
|
4005
4067
|
};
|
|
4006
4068
|
return byLine[input.ingestionSource];
|
|
4007
4069
|
}
|
|
4070
|
+
function algoliaClientDoc(input) {
|
|
4071
|
+
const doc = getNamedDoc("save-records", "js");
|
|
4072
|
+
if (!doc) return [];
|
|
4073
|
+
if (isJsProject(input.language)) return [doc];
|
|
4074
|
+
return [
|
|
4075
|
+
"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:",
|
|
4076
|
+
doc
|
|
4077
|
+
];
|
|
4078
|
+
}
|
|
4008
4079
|
function ingestionInstructions(input) {
|
|
4009
4080
|
return [
|
|
4010
4081
|
...input.confirmed && input.confirmed.length ? [
|
|
@@ -4012,25 +4083,30 @@ function ingestionInstructions(input) {
|
|
|
4012
4083
|
`Ingest only these confirmed entities (name, source paths, attributes): ${JSON.stringify(input.confirmed)}.`,
|
|
4013
4084
|
`Ingesting writes to Algolia, so the script needs a write API key and App ID \u2014 read them from the ${API_KEY_VAR} and ${APP_ID_VAR} environment variables rather than hardcoding them. The wizard sets these when it runs the script.`,
|
|
4014
4085
|
`Read the index name from the ${INDEX_NAME_VAR} environment variable, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or entity name \u2014 the write key only works for that exact index. Exit with an error if ${INDEX_NAME_VAR} is unset.`,
|
|
4015
|
-
"
|
|
4086
|
+
"Write the script in the project's primary language, using Algolia's official client for that language. Do not use the raw HTTP API.",
|
|
4016
4087
|
"After a successful ingest, the script must print exactly one line to stdout in the form `ALGOLIA_WIZARD_RECORD_COUNT=<n>`, where <n> is the total number of records pushed to Algolia. Print it last, on its own line, with no surrounding text.",
|
|
4017
|
-
|
|
4018
|
-
'
|
|
4088
|
+
...algoliaClientDoc(input),
|
|
4089
|
+
"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.",
|
|
4090
|
+
'Then run the script yourself via runShell, and report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
|
|
4019
4091
|
"The summary should be extremely concise.",
|
|
4020
|
-
`Return how to run the script as two fields, not a command string: "runtime" (one of ${INGEST_RUNTIMES.join(", ")}) and "entrypoint" (the script path relative to the worktree root, e.g. "${input.ingestDir}/ingest.mjs"). The wizard runs \`<runtime> <entrypoint>\` directly, so the entrypoint must be a plain path with no flags or arguments. Write a script one of those interpreters can run as-is.`,
|
|
4021
4092
|
...sourceSpecificInstructions(input)
|
|
4022
4093
|
] : []
|
|
4023
4094
|
];
|
|
4024
4095
|
}
|
|
4025
4096
|
function searchInstructions(input) {
|
|
4026
|
-
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.
|
|
4097
|
+
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.language));
|
|
4027
4098
|
return [
|
|
4028
4099
|
"Implement an in-app Algolia search experience.",
|
|
4029
|
-
`Build the search UI for ${input.
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4100
|
+
`Build the search UI for ${input.searchUiTarget}.`,
|
|
4101
|
+
...doc ? [
|
|
4102
|
+
"Follow the Algolia SDK reference below for client setup and search UI wiring; prefer it over prior knowledge:",
|
|
4103
|
+
doc
|
|
4104
|
+
] : [
|
|
4105
|
+
"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."
|
|
4106
|
+
],
|
|
4107
|
+
`Add the search UI at ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working search input and results list against the target index.`,
|
|
4033
4108
|
`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.`,
|
|
4109
|
+
"Read the App ID, the search-only API key, and the index name from env vars; never hardcode them. A search-only key is safe to expose client-side.",
|
|
4034
4110
|
// The key is provisioned only after verification passes, so the agent never
|
|
4035
4111
|
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
4036
4112
|
// decide whether a key already exists, and an agent-invented value there
|
|
@@ -4039,9 +4115,8 @@ function searchInstructions(input) {
|
|
|
4039
4115
|
// Not the agent's to rename: the wizard writes these exact names into
|
|
4040
4116
|
// ".env" right after this step, so a renamed prefix would leave the code
|
|
4041
4117
|
// reading a var the wizard never wrote.
|
|
4042
|
-
`Use exactly these
|
|
4043
|
-
"
|
|
4044
|
-
'Add any Algolia/InstantSearch packages you import to package.json "dependencies" with a valid version range; the wizard installs them in the worktree after you finish.',
|
|
4118
|
+
`Use exactly these env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4119
|
+
"Install any Algolia packages you import with the project's own package manager via runShell, and declare them in the project's dependency manifest.",
|
|
4045
4120
|
"The summary should be extremely concise; do not mention env var setup or manual testing steps \u2014 the wizard writes the resolved credentials to .env and reports that separately."
|
|
4046
4121
|
];
|
|
4047
4122
|
}
|
|
@@ -4049,11 +4124,12 @@ function verificationInstructions(input) {
|
|
|
4049
4124
|
return [
|
|
4050
4125
|
"Verify the Algolia implementation changes in the current worktree.",
|
|
4051
4126
|
`Verification tools found in the codebase: ${JSON.stringify(input.findings.verification ?? [])}.`,
|
|
4052
|
-
"
|
|
4053
|
-
"
|
|
4054
|
-
"
|
|
4127
|
+
"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.",
|
|
4128
|
+
"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.",
|
|
4129
|
+
"For issues caused by the implementation, make minimal fixes with writeFile and re-run the checks.",
|
|
4130
|
+
"Do not make speculative fixes when no checks exist, a check cannot run, or failures are unrelated to these changes \u2014 note the limitation in your summary.",
|
|
4055
4131
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
4056
|
-
`Do not modify "${input.ingestDir}/" unless
|
|
4132
|
+
`Do not modify "${input.ingestDir}/" unless a check reports an actionable issue in its files.`,
|
|
4057
4133
|
"Always call reportStatus with status=success once verification has run, even when sufficient=false.",
|
|
4058
4134
|
"Set sufficient=true only when the implementation is complete and checks pass (or fail for a clearly unrelated reason).",
|
|
4059
4135
|
"Set sufficient=false when the implementation is incomplete or has implementation-caused failures; include concrete additionalInstructions for the next pass."
|
|
@@ -4074,14 +4150,15 @@ var IMPLEMENT_CONFIG = {
|
|
|
4074
4150
|
}
|
|
4075
4151
|
};
|
|
4076
4152
|
var useCaseToolMap = {
|
|
4077
|
-
ingestion: [
|
|
4078
|
-
search: [...FS_READ_TOOLS, "writeFile", "notifyUser"],
|
|
4079
|
-
verification: [
|
|
4153
|
+
ingestion: [
|
|
4080
4154
|
...FS_READ_TOOLS,
|
|
4081
4155
|
"writeFile",
|
|
4082
|
-
"
|
|
4156
|
+
"writeCredentials",
|
|
4157
|
+
"runShell",
|
|
4083
4158
|
"notifyUser"
|
|
4084
|
-
]
|
|
4159
|
+
],
|
|
4160
|
+
search: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"],
|
|
4161
|
+
verification: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"]
|
|
4085
4162
|
};
|
|
4086
4163
|
function toolsForUseCase(useCase, ingestionSource) {
|
|
4087
4164
|
const tools = useCaseToolMap[useCase];
|
|
@@ -4104,15 +4181,30 @@ function formatSummary(useCase, summary) {
|
|
|
4104
4181
|
const label = useCase === "ingestion" ? "Ingestion" : useCase === "search" ? "Search" : "Verification";
|
|
4105
4182
|
return `${label}: ${summary}`;
|
|
4106
4183
|
}
|
|
4107
|
-
function buildIngestCommand(worktree, runtime, entrypoint) {
|
|
4108
|
-
return `cd ${shellQuote(worktree)} && ${runtime} ${shellQuote(entrypoint)}`;
|
|
4109
|
-
}
|
|
4110
4184
|
function parseIngestRecordCount(output) {
|
|
4111
4185
|
const match = output.match(/ALGOLIA_WIZARD_RECORD_COUNT=(\d+)/);
|
|
4112
4186
|
if (!match) return void 0;
|
|
4113
4187
|
const count = Number(match[1]);
|
|
4114
4188
|
return Number.isFinite(count) ? count : void 0;
|
|
4115
4189
|
}
|
|
4190
|
+
function ingestOutcome(executions, ingestCommand) {
|
|
4191
|
+
const newestFirst = [...executions].reverse();
|
|
4192
|
+
const withCount = newestFirst.filter(
|
|
4193
|
+
(e) => parseIngestRecordCount(e.output ?? "") != null
|
|
4194
|
+
);
|
|
4195
|
+
const succeeded = newestFirst.filter((e) => e.approved && e.exitCode === 0);
|
|
4196
|
+
return {
|
|
4197
|
+
run: succeeded.find((e) => e.command === ingestCommand) ?? succeeded.find((e) => withCount.includes(e)),
|
|
4198
|
+
recordCount: parseIngestRecordCount(withCount[0]?.output ?? "")
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4201
|
+
function makeToolContext(worktree, env = async () => ({})) {
|
|
4202
|
+
return createToolContext(
|
|
4203
|
+
DEFAULT_TOOL_LIMITS,
|
|
4204
|
+
worktree,
|
|
4205
|
+
createShellContext({ env, approve: storeApproval(worktree) })
|
|
4206
|
+
);
|
|
4207
|
+
}
|
|
4116
4208
|
function verificationRetryInstructions(verification) {
|
|
4117
4209
|
return [
|
|
4118
4210
|
`Implementation insufficient. Address these findings before reporting completion: ${verification.additionalInstructions ?? verification.summary}`
|
|
@@ -4191,9 +4283,13 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4191
4283
|
const confirmed2 = normalized.confirmedEntities;
|
|
4192
4284
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
4193
4285
|
let appId;
|
|
4286
|
+
let ingestAppId;
|
|
4194
4287
|
if (useCases.includes("search")) {
|
|
4195
4288
|
appId = (await requireApplication()).id;
|
|
4196
4289
|
}
|
|
4290
|
+
if (useCases.includes("ingestion")) {
|
|
4291
|
+
ingestAppId = appId ?? (await requireApplication()).id;
|
|
4292
|
+
}
|
|
4197
4293
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
4198
4294
|
try {
|
|
4199
4295
|
process.chdir(worktree);
|
|
@@ -4230,7 +4326,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4230
4326
|
ingestDir: INGEST_DIR,
|
|
4231
4327
|
ingestionSource,
|
|
4232
4328
|
uploadFilePath,
|
|
4233
|
-
|
|
4329
|
+
searchUiTarget: searchUiTarget(language)
|
|
4234
4330
|
};
|
|
4235
4331
|
const summaries = [];
|
|
4236
4332
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
@@ -4253,41 +4349,31 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4253
4349
|
}
|
|
4254
4350
|
let finalSearchEnvVars = input.searchEnvVars;
|
|
4255
4351
|
let agentRuns = 0;
|
|
4256
|
-
let
|
|
4257
|
-
let ingestEntrypoint;
|
|
4352
|
+
let ingestCommand;
|
|
4258
4353
|
let ingestScriptRan = false;
|
|
4259
4354
|
let ingestRecordCount;
|
|
4260
4355
|
let ingestDurationMs;
|
|
4261
|
-
let installFailed = false;
|
|
4262
4356
|
let ingestOutcomeMessage;
|
|
4357
|
+
const ingestKeyAppId = ingestAppId;
|
|
4358
|
+
const ingestionTools = ingestKeyAppId ? makeToolContext(worktree, async () => ({
|
|
4359
|
+
[APP_ID_VAR]: ingestKeyAppId,
|
|
4360
|
+
[API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
|
|
4361
|
+
[INDEX_NAME_VAR]: targetIndex
|
|
4362
|
+
})) : void 0;
|
|
4363
|
+
const searchTools = makeToolContext(worktree);
|
|
4263
4364
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4264
4365
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4265
4366
|
agentRuns += 1;
|
|
4266
|
-
|
|
4367
|
+
return runAgent({
|
|
4267
4368
|
instructions: buildAgentInstructions(
|
|
4268
4369
|
currentUseCase,
|
|
4269
4370
|
input,
|
|
4270
4371
|
extraInstructions
|
|
4271
4372
|
),
|
|
4272
4373
|
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4273
|
-
outputSchema: implementationOutputSchema
|
|
4274
|
-
|
|
4275
|
-
ctx.notify({
|
|
4276
|
-
messages: [`Installing dependencies for ${currentUseCase}\u2026`]
|
|
4277
|
-
});
|
|
4278
|
-
const installLogId = ctx.logStart("installWorktreeDeps", {
|
|
4279
|
-
useCase: currentUseCase
|
|
4374
|
+
outputSchema: implementationOutputSchema,
|
|
4375
|
+
toolContext: currentUseCase === "ingestion" ? ingestionTools ?? searchTools : searchTools
|
|
4280
4376
|
});
|
|
4281
|
-
const install = await installWorktreeDeps(worktree);
|
|
4282
|
-
ctx.logEnd(installLogId, install.ok ? "success" : "error");
|
|
4283
|
-
if (!install.ok) {
|
|
4284
|
-
installFailed = true;
|
|
4285
|
-
logger.warn(
|
|
4286
|
-
{ useCase: currentUseCase, output: install.output },
|
|
4287
|
-
"implement: dependency install in worktree failed; generated commands may not run until deps are installed"
|
|
4288
|
-
);
|
|
4289
|
-
}
|
|
4290
|
-
return result;
|
|
4291
4377
|
}
|
|
4292
4378
|
async function runVerificationUseCase() {
|
|
4293
4379
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
@@ -4295,111 +4381,55 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4295
4381
|
return runAgent({
|
|
4296
4382
|
instructions: buildAgentInstructions("verification", input),
|
|
4297
4383
|
tools: toolsForUseCase("verification"),
|
|
4298
|
-
outputSchema: verificationOutputSchema
|
|
4384
|
+
outputSchema: verificationOutputSchema,
|
|
4385
|
+
toolContext: searchTools
|
|
4299
4386
|
});
|
|
4300
4387
|
}
|
|
4301
4388
|
if (useCases.includes("ingestion")) {
|
|
4302
|
-
const
|
|
4303
|
-
summaries.push(formatSummary("ingestion", summary));
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
entrypoint: ingestEntrypoint
|
|
4389
|
+
const result = await runImplementationUseCase("ingestion");
|
|
4390
|
+
summaries.push(formatSummary("ingestion", result.summary));
|
|
4391
|
+
ingestCommand = result.ingestCommand;
|
|
4392
|
+
const executions = (ingestionTools ?? searchTools).shell.executions;
|
|
4393
|
+
const { run: ingestRun, recordCount } = ingestOutcome(
|
|
4394
|
+
executions,
|
|
4395
|
+
ingestCommand
|
|
4396
|
+
);
|
|
4397
|
+
ingestScriptRan = ingestRun != null;
|
|
4398
|
+
ingestRecordCount = recordCount;
|
|
4399
|
+
ingestDurationMs = ingestRun?.durationMs;
|
|
4400
|
+
if (ingestScriptRan) {
|
|
4401
|
+
ingestOutcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4402
|
+
if (ingestRecordCount != null) {
|
|
4403
|
+
track("AI Wizard Ingest Successful", {
|
|
4404
|
+
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4405
|
+
record_count: ingestRecordCount,
|
|
4406
|
+
duration_ms: ingestDurationMs ?? 0
|
|
4321
4407
|
});
|
|
4322
|
-
const startedAt = Date.now();
|
|
4323
|
-
const run2 = await runIngestScript(
|
|
4324
|
-
worktree,
|
|
4325
|
-
ingestRuntime,
|
|
4326
|
-
ingestEntrypoint,
|
|
4327
|
-
{
|
|
4328
|
-
[APP_ID_VAR]: ingestApp.id,
|
|
4329
|
-
[API_KEY_VAR]: writeKey,
|
|
4330
|
-
[INDEX_NAME_VAR]: targetIndex
|
|
4331
|
-
}
|
|
4332
|
-
);
|
|
4333
|
-
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
4334
|
-
ingestScriptRan = run2.ran && run2.ok;
|
|
4335
|
-
if (ingestScriptRan) {
|
|
4336
|
-
ingestDurationMs = Date.now() - startedAt;
|
|
4337
|
-
ingestRecordCount = parseIngestRecordCount(run2.output);
|
|
4338
|
-
if (ingestRecordCount != null) {
|
|
4339
|
-
track("AI Wizard Ingest Successful", {
|
|
4340
|
-
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4341
|
-
record_count: ingestRecordCount,
|
|
4342
|
-
duration_ms: ingestDurationMs
|
|
4343
|
-
});
|
|
4344
|
-
}
|
|
4345
|
-
}
|
|
4346
|
-
let summaryLine;
|
|
4347
|
-
let outcomeMessage;
|
|
4348
|
-
if (!run2.ran) {
|
|
4349
|
-
summaryLine = `\u26A0\uFE0F Skipped running the ingestion script: ${run2.reason}`;
|
|
4350
|
-
outcomeMessage = `\u26A0\uFE0F The ingestion script did not run: ${run2.reason}`;
|
|
4351
|
-
logger.warn(
|
|
4352
|
-
{
|
|
4353
|
-
runtime: ingestRuntime,
|
|
4354
|
-
entrypoint: ingestEntrypoint,
|
|
4355
|
-
reason: run2.reason
|
|
4356
|
-
},
|
|
4357
|
-
"implement: refused to auto-run ingestion script"
|
|
4358
|
-
);
|
|
4359
|
-
track("Error", {
|
|
4360
|
-
step: "Push Data",
|
|
4361
|
-
error: `ingestion script skipped: ${run2.reason}`,
|
|
4362
|
-
product_area: "AI Wizard"
|
|
4363
|
-
});
|
|
4364
|
-
} else if (run2.ok) {
|
|
4365
|
-
const status = "Ingestion run: succeeded.";
|
|
4366
|
-
summaryLine = run2.output ? `${status}
|
|
4367
|
-
${run2.output}` : status;
|
|
4368
|
-
outcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4369
|
-
} else {
|
|
4370
|
-
const status = "\u26A0\uFE0F Ingestion run failed:";
|
|
4371
|
-
summaryLine = run2.output ? `${status}
|
|
4372
|
-
${run2.output}` : status;
|
|
4373
|
-
outcomeMessage = `\u274C Ingestion failed.${run2.output ? ` ${run2.output}` : ""}`;
|
|
4374
|
-
logger.warn(
|
|
4375
|
-
{
|
|
4376
|
-
runtime: ingestRuntime,
|
|
4377
|
-
entrypoint: ingestEntrypoint,
|
|
4378
|
-
output: run2.output
|
|
4379
|
-
},
|
|
4380
|
-
"implement: ingestion script run failed"
|
|
4381
|
-
);
|
|
4382
|
-
track("Error", {
|
|
4383
|
-
step: "Push Data",
|
|
4384
|
-
error: run2.output || "ingestion script exited non-zero",
|
|
4385
|
-
product_area: "AI Wizard"
|
|
4386
|
-
});
|
|
4387
|
-
}
|
|
4388
|
-
summaries.push(summaryLine);
|
|
4389
|
-
ingestOutcomeMessage = outcomeMessage;
|
|
4390
4408
|
}
|
|
4409
|
+
} else {
|
|
4410
|
+
const rejected = executions.some((e) => !e.approved);
|
|
4411
|
+
const reason = rejected ? "you declined to run it" : "no successful run was recorded";
|
|
4412
|
+
ingestOutcomeMessage = `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.${ingestCommand ? " Run the command below when you are ready." : ""}`;
|
|
4413
|
+
summaries.push(`\u26A0\uFE0F The ingestion script did not run: ${reason}.`);
|
|
4414
|
+
logger.warn(
|
|
4415
|
+
{ ingestCommand, rejected, commandsRun: executions.length },
|
|
4416
|
+
"implement: ingestion script did not complete successfully"
|
|
4417
|
+
);
|
|
4418
|
+
track("Error", {
|
|
4419
|
+
step: "Push Data",
|
|
4420
|
+
error: `ingestion did not run: ${reason}`,
|
|
4421
|
+
product_area: "AI Wizard"
|
|
4422
|
+
});
|
|
4391
4423
|
}
|
|
4392
4424
|
const commandMessages = [`Open the worktree: cd ${shellQuote(worktree)}`];
|
|
4393
|
-
if (
|
|
4394
|
-
commandMessages.push(
|
|
4395
|
-
`Ingestion command: ${buildIngestCommand(worktree, ingestRuntime, ingestEntrypoint)}`
|
|
4396
|
-
);
|
|
4425
|
+
if (ingestCommand) {
|
|
4426
|
+
commandMessages.push(`Ingestion command: ${ingestCommand}`);
|
|
4397
4427
|
}
|
|
4398
4428
|
await ctx.requestUserInput({
|
|
4399
4429
|
prompt: "",
|
|
4400
4430
|
promptType: "enterToContinue",
|
|
4401
4431
|
options: [],
|
|
4402
|
-
messages:
|
|
4432
|
+
messages: [ingestOutcomeMessage, ...commandMessages]
|
|
4403
4433
|
});
|
|
4404
4434
|
}
|
|
4405
4435
|
if (useCases.includes("search")) {
|
|
@@ -4516,22 +4546,13 @@ ${run2.output}` : status;
|
|
|
4516
4546
|
"implement: agent reported success but no files changed in the worktree"
|
|
4517
4547
|
);
|
|
4518
4548
|
}
|
|
4519
|
-
if (installFailed) {
|
|
4520
|
-
summaries.push(
|
|
4521
|
-
'\u26A0\uFE0F Dependency install in the worktree failed. Run your package manager install in the worktree before the command below, or it will fail with "Cannot find module".'
|
|
4522
|
-
);
|
|
4523
|
-
}
|
|
4524
4549
|
return {
|
|
4525
4550
|
ingestionSource,
|
|
4526
4551
|
filesChanged,
|
|
4527
4552
|
summary: summaries.join("\n\n"),
|
|
4528
4553
|
worktreePath: worktree,
|
|
4529
|
-
...useCases.includes("ingestion") &&
|
|
4530
|
-
ingestCommand
|
|
4531
|
-
worktree,
|
|
4532
|
-
ingestRuntime,
|
|
4533
|
-
ingestEntrypoint
|
|
4534
|
-
),
|
|
4554
|
+
...useCases.includes("ingestion") && ingestCommand ? {
|
|
4555
|
+
ingestCommand,
|
|
4535
4556
|
ingestScriptRan,
|
|
4536
4557
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4537
4558
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
@@ -4859,7 +4880,7 @@ function parseCliArgs(argv) {
|
|
|
4859
4880
|
|
|
4860
4881
|
// src/lib/resetState.ts
|
|
4861
4882
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4862
|
-
import { join as
|
|
4883
|
+
import { join as join10 } from "node:path";
|
|
4863
4884
|
var KEEP = ["wizard.log"];
|
|
4864
4885
|
async function resetProjectState() {
|
|
4865
4886
|
const dir = stateDir();
|
|
@@ -4873,14 +4894,14 @@ async function resetProjectState() {
|
|
|
4873
4894
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4874
4895
|
await Promise.all(
|
|
4875
4896
|
targets.map(
|
|
4876
|
-
(name) => rm2(
|
|
4897
|
+
(name) => rm2(join10(dir, name), { recursive: true, force: true })
|
|
4877
4898
|
)
|
|
4878
4899
|
);
|
|
4879
4900
|
return { dir, removed: targets };
|
|
4880
4901
|
}
|
|
4881
4902
|
|
|
4882
4903
|
// src/main.tsx
|
|
4883
|
-
import { jsx as
|
|
4904
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4884
4905
|
async function startup() {
|
|
4885
4906
|
setProjectRoot(process.cwd());
|
|
4886
4907
|
let args;
|
|
@@ -4930,7 +4951,7 @@ ${formatStepList(workflow)}`);
|
|
|
4930
4951
|
}
|
|
4931
4952
|
async function run(workflow) {
|
|
4932
4953
|
const store = useWizard.getState();
|
|
4933
|
-
const instance = render(/* @__PURE__ */
|
|
4954
|
+
const instance = render(/* @__PURE__ */ jsx15(App, {}), { incrementalRendering: true });
|
|
4934
4955
|
await store.waitForStart();
|
|
4935
4956
|
let user = await getUser();
|
|
4936
4957
|
if (!user) {
|