@algolia/wizard 0.9.0-rc.88.85 → 0.9.0-rc.93.95
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,60 @@ 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
|
+
});
|
|
669
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
|
|
670
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, bold: true, children: "Run this command?" }),
|
|
671
|
+
/* @__PURE__ */ jsxs4(
|
|
672
|
+
Box5,
|
|
673
|
+
{
|
|
674
|
+
flexDirection: "column",
|
|
675
|
+
paddingLeft: 2,
|
|
676
|
+
borderStyle: "single",
|
|
677
|
+
borderColor: COLORS.success,
|
|
678
|
+
borderTop: false,
|
|
679
|
+
borderBottom: false,
|
|
680
|
+
borderRight: false,
|
|
681
|
+
gap: 1,
|
|
682
|
+
children: [
|
|
683
|
+
/* @__PURE__ */ jsxs4(Box5, { children: [
|
|
684
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "$ " }),
|
|
685
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.strong, wrap: "wrap", children: command.command })
|
|
686
|
+
] }),
|
|
687
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
688
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "in:" }),
|
|
689
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
|
|
690
|
+
] }),
|
|
691
|
+
command.explanation && /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
692
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "why:" }),
|
|
693
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
|
|
694
|
+
] })
|
|
695
|
+
]
|
|
696
|
+
}
|
|
697
|
+
),
|
|
698
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
699
|
+
/* @__PURE__ */ jsx4(NextAction, { action: "approve", keyHint: "enter" }),
|
|
700
|
+
/* @__PURE__ */ jsx4(NextAction, { action: "reject", keyHint: "esc", hierarchy: "secondary" })
|
|
701
|
+
] })
|
|
702
|
+
] });
|
|
703
|
+
}
|
|
704
|
+
|
|
656
705
|
// src/ui/SelectPrompt.tsx
|
|
657
|
-
import { Box as
|
|
706
|
+
import { Box as Box7, Text as Text7, useInput as useInput2, useWindowSize as useWindowSize5 } from "ink";
|
|
658
707
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
659
708
|
|
|
660
709
|
// src/ui/ScrollView.tsx
|
|
661
|
-
import { Box as
|
|
710
|
+
import { Box as Box6, Text as Text6, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
|
|
662
711
|
import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
663
|
-
import { jsxs as
|
|
712
|
+
import { jsxs as jsxs5 } from "react/jsx-runtime";
|
|
664
713
|
var INDICATOR_ROWS = 2;
|
|
665
714
|
function fittedWidth(node, columns) {
|
|
666
715
|
let left = 0;
|
|
@@ -730,14 +779,14 @@ function useScrollWindow({
|
|
|
730
779
|
};
|
|
731
780
|
}
|
|
732
781
|
function ScrollView({ scroll, children }) {
|
|
733
|
-
return /* @__PURE__ */
|
|
734
|
-
scroll.hiddenAbove > 0 && /* @__PURE__ */
|
|
782
|
+
return /* @__PURE__ */ jsxs5(Box6, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
783
|
+
scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
735
784
|
"\u2191 ",
|
|
736
785
|
scroll.hiddenAbove,
|
|
737
786
|
" more"
|
|
738
787
|
] }),
|
|
739
788
|
children,
|
|
740
|
-
scroll.hiddenBelow > 0 && /* @__PURE__ */
|
|
789
|
+
scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
741
790
|
"\u2193 ",
|
|
742
791
|
scroll.hiddenBelow,
|
|
743
792
|
" more"
|
|
@@ -746,7 +795,7 @@ function ScrollView({ scroll, children }) {
|
|
|
746
795
|
}
|
|
747
796
|
|
|
748
797
|
// src/ui/SelectPrompt.tsx
|
|
749
|
-
import { jsx as
|
|
798
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
750
799
|
var CANCEL = "cancel";
|
|
751
800
|
var ARROW_WIDTH = 4;
|
|
752
801
|
var COLUMN_GAP = 2;
|
|
@@ -808,7 +857,7 @@ function SelectPrompt({
|
|
|
808
857
|
revealIndex(index);
|
|
809
858
|
}, [index, revealIndex]);
|
|
810
859
|
const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
811
|
-
|
|
860
|
+
useInput2((input, key) => {
|
|
812
861
|
if (rows.length === 0) return;
|
|
813
862
|
if (key.upArrow || input === "k") {
|
|
814
863
|
setIndex((i) => (i - 1 + rows.length) % rows.length);
|
|
@@ -831,56 +880,56 @@ function SelectPrompt({
|
|
|
831
880
|
}
|
|
832
881
|
}
|
|
833
882
|
});
|
|
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__ */
|
|
883
|
+
return /* @__PURE__ */ jsx5(Box7, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, width, children: [
|
|
884
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
|
|
885
|
+
error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: error }),
|
|
886
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
887
|
+
table && /* @__PURE__ */ jsx5(Table, { columns: table.columns, rows: table.rows }),
|
|
888
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
889
|
+
question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: question }),
|
|
890
|
+
helpText && /* @__PURE__ */ jsx5(Text7, { color: COLORS.dim, children: helpText })
|
|
842
891
|
] })
|
|
843
892
|
] }),
|
|
844
|
-
/* @__PURE__ */
|
|
893
|
+
/* @__PURE__ */ jsx5(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
|
|
845
894
|
const i = scroll.offset + visibleIndex;
|
|
846
895
|
const highlighted = i === index;
|
|
847
896
|
const isCancel = i === cancelIndex;
|
|
848
897
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
849
898
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
850
899
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
851
|
-
const label = /* @__PURE__ */
|
|
900
|
+
const label = /* @__PURE__ */ jsxs6(Text7, { color: labelColor, wrap: "truncate", children: [
|
|
852
901
|
highlighted ? "\u276F " : " ",
|
|
853
902
|
bullet,
|
|
854
903
|
option
|
|
855
904
|
] });
|
|
856
905
|
const isText = sec?.kind === "text";
|
|
857
|
-
return /* @__PURE__ */
|
|
858
|
-
|
|
906
|
+
return /* @__PURE__ */ jsxs6(
|
|
907
|
+
Box7,
|
|
859
908
|
{
|
|
860
909
|
width: isText ? "100%" : barWidth,
|
|
861
910
|
paddingX: 1,
|
|
862
911
|
paddingY: 1,
|
|
863
912
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
864
913
|
children: [
|
|
865
|
-
/* @__PURE__ */
|
|
866
|
-
isText && textWidth > 0 && /* @__PURE__ */
|
|
867
|
-
|
|
914
|
+
/* @__PURE__ */ jsx5(Box7, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
915
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx5(Box7, { width: textWidth, children: /* @__PURE__ */ jsx5(
|
|
916
|
+
Text7,
|
|
868
917
|
{
|
|
869
918
|
wrap: "truncate",
|
|
870
919
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
871
920
|
children: sec.value
|
|
872
921
|
}
|
|
873
922
|
) }),
|
|
874
|
-
sec?.kind === "badge" && /* @__PURE__ */
|
|
923
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx5(Box7, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx5(Text7, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
875
924
|
]
|
|
876
925
|
},
|
|
877
926
|
`row-${i}`
|
|
878
927
|
);
|
|
879
928
|
}) }),
|
|
880
|
-
/* @__PURE__ */
|
|
929
|
+
/* @__PURE__ */ jsx5(Box7, { flexShrink: 0, children: /* @__PURE__ */ jsx5(Text7, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
881
930
|
i > 0 ? " " : "",
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
/* @__PURE__ */
|
|
931
|
+
/* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: key }),
|
|
932
|
+
/* @__PURE__ */ jsxs6(Text7, { color: COLORS.dim, children: [
|
|
884
933
|
" ",
|
|
885
934
|
label
|
|
886
935
|
] })
|
|
@@ -889,23 +938,23 @@ function SelectPrompt({
|
|
|
889
938
|
}
|
|
890
939
|
|
|
891
940
|
// src/ui/PromptInput.tsx
|
|
892
|
-
import { jsx as
|
|
941
|
+
import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
893
942
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
894
943
|
function EnterToContinuePrompt({
|
|
895
944
|
question,
|
|
896
945
|
messages,
|
|
897
946
|
onDecide
|
|
898
947
|
}) {
|
|
899
|
-
|
|
948
|
+
useInput3((_input, key) => {
|
|
900
949
|
if (key.return) onDecide(true);
|
|
901
950
|
else if (key.escape) onDecide(false);
|
|
902
951
|
});
|
|
903
|
-
return /* @__PURE__ */
|
|
904
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
905
|
-
question && /* @__PURE__ */
|
|
906
|
-
/* @__PURE__ */
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
/* @__PURE__ */
|
|
952
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, children: [
|
|
953
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
954
|
+
question && /* @__PURE__ */ jsx6(Text8, { color: COLORS.primary, children: question }),
|
|
955
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
|
|
956
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "continue", keyHint: "enter" }),
|
|
957
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
909
958
|
] })
|
|
910
959
|
] });
|
|
911
960
|
}
|
|
@@ -913,11 +962,11 @@ function PromptInput() {
|
|
|
913
962
|
const { phase, inputReq, submitInput } = useWizard();
|
|
914
963
|
const [draft, setDraft] = useState5("");
|
|
915
964
|
if (phase === "done" || phase === "error") {
|
|
916
|
-
return /* @__PURE__ */
|
|
965
|
+
return /* @__PURE__ */ jsx6(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text8, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
917
966
|
}
|
|
918
967
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
919
968
|
if (inputReq.promptType === "multipleChoice") {
|
|
920
|
-
return /* @__PURE__ */
|
|
969
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
921
970
|
SelectPrompt,
|
|
922
971
|
{
|
|
923
972
|
question: inputReq.prompt,
|
|
@@ -934,7 +983,7 @@ function PromptInput() {
|
|
|
934
983
|
) });
|
|
935
984
|
}
|
|
936
985
|
if (inputReq.promptType === "multiSelect") {
|
|
937
|
-
return /* @__PURE__ */
|
|
986
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
938
987
|
SelectPrompt,
|
|
939
988
|
{
|
|
940
989
|
multi: true,
|
|
@@ -949,7 +998,7 @@ function PromptInput() {
|
|
|
949
998
|
) });
|
|
950
999
|
}
|
|
951
1000
|
if (inputReq.promptType === "notice") {
|
|
952
|
-
return /* @__PURE__ */
|
|
1001
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
953
1002
|
SelectPrompt,
|
|
954
1003
|
{
|
|
955
1004
|
question: inputReq.prompt,
|
|
@@ -960,7 +1009,7 @@ function PromptInput() {
|
|
|
960
1009
|
) });
|
|
961
1010
|
}
|
|
962
1011
|
if (inputReq.promptType === "enterToContinue") {
|
|
963
|
-
return /* @__PURE__ */
|
|
1012
|
+
return /* @__PURE__ */ jsx6(
|
|
964
1013
|
EnterToContinuePrompt,
|
|
965
1014
|
{
|
|
966
1015
|
question: inputReq.prompt,
|
|
@@ -969,9 +1018,12 @@ function PromptInput() {
|
|
|
969
1018
|
}
|
|
970
1019
|
);
|
|
971
1020
|
}
|
|
1021
|
+
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1022
|
+
return /* @__PURE__ */ jsx6(CommandApproval, { command: inputReq.command, onDecide: submitInput });
|
|
1023
|
+
}
|
|
972
1024
|
if (inputReq.promptType === "acceptReject") {
|
|
973
1025
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
974
|
-
return /* @__PURE__ */
|
|
1026
|
+
return /* @__PURE__ */ jsx6(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx6(
|
|
975
1027
|
SelectPrompt,
|
|
976
1028
|
{
|
|
977
1029
|
question: inputReq.prompt,
|
|
@@ -982,15 +1034,15 @@ function PromptInput() {
|
|
|
982
1034
|
}
|
|
983
1035
|
) });
|
|
984
1036
|
}
|
|
985
|
-
return /* @__PURE__ */
|
|
986
|
-
inputReq.error && /* @__PURE__ */
|
|
987
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
989
|
-
/* @__PURE__ */
|
|
1037
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1038
|
+
inputReq.error && /* @__PURE__ */ jsx6(Text8, { color: COLORS.danger, children: inputReq.error }),
|
|
1039
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
1040
|
+
/* @__PURE__ */ jsxs7(Box8, { children: [
|
|
1041
|
+
/* @__PURE__ */ jsxs7(Text8, { color: COLORS.primary, children: [
|
|
990
1042
|
inputReq.prompt,
|
|
991
1043
|
" "
|
|
992
1044
|
] }),
|
|
993
|
-
/* @__PURE__ */
|
|
1045
|
+
/* @__PURE__ */ jsx6(
|
|
994
1046
|
TextInput,
|
|
995
1047
|
{
|
|
996
1048
|
value: draft,
|
|
@@ -1008,7 +1060,7 @@ function PromptInput() {
|
|
|
1008
1060
|
// src/ui/Welcome.tsx
|
|
1009
1061
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1010
1062
|
import { fileURLToPath } from "node:url";
|
|
1011
|
-
import { Box as
|
|
1063
|
+
import { Box as Box9, Spacer, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
1012
1064
|
|
|
1013
1065
|
// src/ui/copy/welcome.ts
|
|
1014
1066
|
var sidebarItems = [
|
|
@@ -1021,12 +1073,12 @@ var sidebarItems = [
|
|
|
1021
1073
|
description: "push 100 records to Algolia in seconds"
|
|
1022
1074
|
},
|
|
1023
1075
|
{
|
|
1024
|
-
title: "detect your
|
|
1025
|
-
description: "
|
|
1076
|
+
title: "detect your stack",
|
|
1077
|
+
description: "whatever language and framework you already use"
|
|
1026
1078
|
},
|
|
1027
1079
|
{
|
|
1028
1080
|
title: "scaffold a search UI",
|
|
1029
|
-
description: "a
|
|
1081
|
+
description: "a search box and results, wired into your app"
|
|
1030
1082
|
},
|
|
1031
1083
|
{
|
|
1032
1084
|
title: "ship it",
|
|
@@ -1036,20 +1088,20 @@ var sidebarItems = [
|
|
|
1036
1088
|
|
|
1037
1089
|
// src/ui/Welcome.tsx
|
|
1038
1090
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
1039
|
-
import { jsx as
|
|
1091
|
+
import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1040
1092
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
1041
1093
|
function SidebarItem({
|
|
1042
1094
|
title,
|
|
1043
1095
|
description
|
|
1044
1096
|
}) {
|
|
1045
|
-
return /* @__PURE__ */
|
|
1046
|
-
/* @__PURE__ */
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1097
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
|
|
1098
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, children: [
|
|
1099
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.success, children: "\u2192" }),
|
|
1100
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: title })
|
|
1049
1101
|
] }),
|
|
1050
|
-
/* @__PURE__ */
|
|
1051
|
-
/* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1102
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 2, children: [
|
|
1103
|
+
/* @__PURE__ */ jsx7(Spacer, {}),
|
|
1104
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: description })
|
|
1053
1105
|
] })
|
|
1054
1106
|
] });
|
|
1055
1107
|
}
|
|
@@ -1057,7 +1109,7 @@ function Welcome() {
|
|
|
1057
1109
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
1058
1110
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
1059
1111
|
const { rows } = useWindowSize6();
|
|
1060
|
-
|
|
1112
|
+
useInput4((input, key) => {
|
|
1061
1113
|
if (key.return) confirmStart();
|
|
1062
1114
|
else if (input === "i") openLearnMore();
|
|
1063
1115
|
});
|
|
@@ -1075,16 +1127,16 @@ function Welcome() {
|
|
|
1075
1127
|
if (rows < 30) {
|
|
1076
1128
|
layout = scales["small"];
|
|
1077
1129
|
}
|
|
1078
|
-
return /* @__PURE__ */
|
|
1079
|
-
/* @__PURE__ */
|
|
1080
|
-
|
|
1130
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
1131
|
+
/* @__PURE__ */ jsx7(
|
|
1132
|
+
Box9,
|
|
1081
1133
|
{
|
|
1082
1134
|
paddingY: layout.main.padding.y,
|
|
1083
1135
|
paddingX: layout.main.padding.x,
|
|
1084
1136
|
flexDirection: "column",
|
|
1085
1137
|
justifyContent: "center",
|
|
1086
|
-
children: /* @__PURE__ */
|
|
1087
|
-
/* @__PURE__ */
|
|
1138
|
+
children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 2, children: [
|
|
1139
|
+
/* @__PURE__ */ jsx7(InkPictureProvider, { children: /* @__PURE__ */ jsx7(
|
|
1088
1140
|
Image,
|
|
1089
1141
|
{
|
|
1090
1142
|
src: IMAGE_PATH,
|
|
@@ -1095,16 +1147,16 @@ function Welcome() {
|
|
|
1095
1147
|
protocol: "halfBlock"
|
|
1096
1148
|
}
|
|
1097
1149
|
) }),
|
|
1098
|
-
/* @__PURE__ */
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1150
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1151
|
+
/* @__PURE__ */ jsxs8(Box9, { gap: 1, flexDirection: "column", children: [
|
|
1152
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
1153
|
+
/* @__PURE__ */ jsx7(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
1102
1154
|
] })
|
|
1103
1155
|
] })
|
|
1104
1156
|
}
|
|
1105
1157
|
),
|
|
1106
|
-
/* @__PURE__ */
|
|
1107
|
-
|
|
1158
|
+
/* @__PURE__ */ jsxs8(
|
|
1159
|
+
Box9,
|
|
1108
1160
|
{
|
|
1109
1161
|
backgroundColor: COLORS.bg.sidebar,
|
|
1110
1162
|
width: 40,
|
|
@@ -1114,8 +1166,8 @@ function Welcome() {
|
|
|
1114
1166
|
flexDirection: "column",
|
|
1115
1167
|
justifyContent: "center",
|
|
1116
1168
|
children: [
|
|
1117
|
-
/* @__PURE__ */
|
|
1118
|
-
sidebarItems.map((i, idx) => /* @__PURE__ */
|
|
1169
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
1170
|
+
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx7(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
1119
1171
|
]
|
|
1120
1172
|
}
|
|
1121
1173
|
)
|
|
@@ -1124,7 +1176,7 @@ function Welcome() {
|
|
|
1124
1176
|
|
|
1125
1177
|
// src/ui/LearnMore.tsx
|
|
1126
1178
|
import { Fragment as Fragment2 } from "react";
|
|
1127
|
-
import { Box as
|
|
1179
|
+
import { Box as Box10, Text as Text10, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1128
1180
|
|
|
1129
1181
|
// src/ui/copy/learn-more.ts
|
|
1130
1182
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -1132,12 +1184,17 @@ var accessItems = [
|
|
|
1132
1184
|
{
|
|
1133
1185
|
tag: "READ",
|
|
1134
1186
|
title: "Project files",
|
|
1135
|
-
description: "reads
|
|
1187
|
+
description: "reads manifests, configs & source to detect your stack. Read-only; nothing is uploaded."
|
|
1136
1188
|
},
|
|
1137
1189
|
{
|
|
1138
1190
|
tag: "WRITE",
|
|
1139
1191
|
title: "Code changes",
|
|
1140
|
-
description: "creates & edits files (search UI, config)
|
|
1192
|
+
description: "creates & edits files (search UI, config) in a throwaway git worktree \u2014 your checkout is never touched."
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
tag: "EXEC",
|
|
1196
|
+
title: "Setup commands",
|
|
1197
|
+
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
1198
|
},
|
|
1142
1199
|
{
|
|
1143
1200
|
tag: "NET",
|
|
@@ -1147,13 +1204,13 @@ var accessItems = [
|
|
|
1147
1204
|
{
|
|
1148
1205
|
tag: "KEY",
|
|
1149
1206
|
title: "Credentials",
|
|
1150
|
-
description: "
|
|
1207
|
+
description: "writes your Algolia app id and a search-only key (safe to expose) to .env in the worktree."
|
|
1151
1208
|
}
|
|
1152
1209
|
];
|
|
1153
1210
|
var neverItems = [
|
|
1154
1211
|
"Send your source code to a model or third party",
|
|
1155
1212
|
"Commit or push to git",
|
|
1156
|
-
"
|
|
1213
|
+
"Run a command you haven't approved"
|
|
1157
1214
|
];
|
|
1158
1215
|
var policyLinks = [
|
|
1159
1216
|
{ label: "Terms", url: "https://www.algolia.com/policies/terms" },
|
|
@@ -1161,10 +1218,11 @@ var policyLinks = [
|
|
|
1161
1218
|
];
|
|
1162
1219
|
|
|
1163
1220
|
// src/ui/LearnMore.tsx
|
|
1164
|
-
import { jsx as
|
|
1221
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1165
1222
|
var TAG_COLORS = {
|
|
1166
1223
|
READ: COLORS.success,
|
|
1167
1224
|
WRITE: COLORS.badge,
|
|
1225
|
+
EXEC: COLORS.danger,
|
|
1168
1226
|
NET: COLORS.accent,
|
|
1169
1227
|
KEY: COLORS.muted
|
|
1170
1228
|
};
|
|
@@ -1177,12 +1235,12 @@ function NeverLine({
|
|
|
1177
1235
|
}) {
|
|
1178
1236
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
1179
1237
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
1180
|
-
return /* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
1238
|
+
return /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1239
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" }),
|
|
1182
1240
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
1183
|
-
segments.map((s, i) => /* @__PURE__ */
|
|
1241
|
+
segments.map((s, i) => /* @__PURE__ */ jsx8(Text10, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
1184
1242
|
" ".repeat(rightPad),
|
|
1185
|
-
/* @__PURE__ */
|
|
1243
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: "\u2502" })
|
|
1186
1244
|
] });
|
|
1187
1245
|
}
|
|
1188
1246
|
function LearnMore() {
|
|
@@ -1190,12 +1248,12 @@ function LearnMore() {
|
|
|
1190
1248
|
const backToHome = useWizard((s) => s.backToHome);
|
|
1191
1249
|
const { columns } = useWindowSize7();
|
|
1192
1250
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
1193
|
-
|
|
1251
|
+
useInput5((_input, key) => {
|
|
1194
1252
|
if (key.escape) backToHome();
|
|
1195
1253
|
else if (key.return) confirmStart();
|
|
1196
1254
|
});
|
|
1197
|
-
return /* @__PURE__ */
|
|
1198
|
-
|
|
1255
|
+
return /* @__PURE__ */ jsxs9(
|
|
1256
|
+
Box10,
|
|
1199
1257
|
{
|
|
1200
1258
|
flexDirection: "column",
|
|
1201
1259
|
paddingX: PADDING_X,
|
|
@@ -1203,31 +1261,31 @@ function LearnMore() {
|
|
|
1203
1261
|
width: "100%",
|
|
1204
1262
|
gap: 1,
|
|
1205
1263
|
children: [
|
|
1206
|
-
/* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1208
|
-
/* @__PURE__ */
|
|
1209
|
-
/* @__PURE__ */
|
|
1210
|
-
/* @__PURE__ */
|
|
1211
|
-
/* @__PURE__ */
|
|
1212
|
-
/* @__PURE__ */
|
|
1213
|
-
/* @__PURE__ */
|
|
1214
|
-
/* @__PURE__ */
|
|
1264
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1265
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: accessIntro }),
|
|
1266
|
+
/* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
1267
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1268
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1269
|
+
/* @__PURE__ */ jsx8(Box10, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text10, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1270
|
+
/* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
1271
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1272
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
1215
1273
|
] }) })
|
|
1216
1274
|
] })
|
|
1217
1275
|
] }, item.tag)) }),
|
|
1218
|
-
/* @__PURE__ */
|
|
1219
|
-
/* @__PURE__ */
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1276
|
+
/* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "column", children: [
|
|
1277
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
1278
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1279
|
+
/* @__PURE__ */ jsx8(
|
|
1222
1280
|
NeverLine,
|
|
1223
1281
|
{
|
|
1224
1282
|
width: dividerWidth,
|
|
1225
1283
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1226
1284
|
}
|
|
1227
1285
|
),
|
|
1228
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1229
|
-
/* @__PURE__ */
|
|
1230
|
-
/* @__PURE__ */
|
|
1286
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
1287
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1288
|
+
/* @__PURE__ */ jsx8(
|
|
1231
1289
|
NeverLine,
|
|
1232
1290
|
{
|
|
1233
1291
|
width: dividerWidth,
|
|
@@ -1239,24 +1297,24 @@ function LearnMore() {
|
|
|
1239
1297
|
}
|
|
1240
1298
|
)
|
|
1241
1299
|
] }, item)),
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */
|
|
1300
|
+
/* @__PURE__ */ jsx8(NeverLine, { width: dividerWidth }),
|
|
1301
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1244
1302
|
] }),
|
|
1245
|
-
/* @__PURE__ */
|
|
1246
|
-
/* @__PURE__ */
|
|
1247
|
-
/* @__PURE__ */
|
|
1303
|
+
/* @__PURE__ */ jsx8(Box10, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1304
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1305
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.accent, children: link.url })
|
|
1248
1306
|
] }, link.label)) }),
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1253
|
-
/* @__PURE__ */
|
|
1307
|
+
/* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1308
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1309
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
|
|
1310
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "esc" }),
|
|
1311
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "] back" })
|
|
1254
1312
|
] }),
|
|
1255
|
-
/* @__PURE__ */
|
|
1256
|
-
/* @__PURE__ */
|
|
1257
|
-
/* @__PURE__ */
|
|
1258
|
-
/* @__PURE__ */
|
|
1259
|
-
/* @__PURE__ */
|
|
1313
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1314
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "[" }),
|
|
1315
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.primary, children: "enter" }),
|
|
1316
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.muted, children: "]" }),
|
|
1317
|
+
/* @__PURE__ */ jsx8(Text10, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1260
1318
|
] })
|
|
1261
1319
|
] })
|
|
1262
1320
|
]
|
|
@@ -1265,10 +1323,10 @@ function LearnMore() {
|
|
|
1265
1323
|
}
|
|
1266
1324
|
|
|
1267
1325
|
// src/ui/Sidebar.tsx
|
|
1268
|
-
import { Box as
|
|
1326
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1269
1327
|
|
|
1270
1328
|
// src/ui/Steps.tsx
|
|
1271
|
-
import { Box as
|
|
1329
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1272
1330
|
import Spinner from "ink-spinner";
|
|
1273
1331
|
|
|
1274
1332
|
// src/core/persistence.ts
|
|
@@ -1297,12 +1355,12 @@ async function clearWorkflowState(workflowId) {
|
|
|
1297
1355
|
}
|
|
1298
1356
|
|
|
1299
1357
|
// src/ui/Steps.tsx
|
|
1300
|
-
import { jsx as
|
|
1358
|
+
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1301
1359
|
function Steps() {
|
|
1302
1360
|
const { steps } = useWizard();
|
|
1303
1361
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1304
|
-
return /* @__PURE__ */
|
|
1305
|
-
s.status === "running" ? /* @__PURE__ */
|
|
1362
|
+
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: [
|
|
1363
|
+
s.status === "running" ? /* @__PURE__ */ jsx9(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1306
1364
|
" ",
|
|
1307
1365
|
s.title
|
|
1308
1366
|
] }) }, s.id)) });
|
|
@@ -1311,27 +1369,27 @@ function CurrentStep() {
|
|
|
1311
1369
|
const { steps } = useWizard();
|
|
1312
1370
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1313
1371
|
if (!currentStep) return null;
|
|
1314
|
-
return /* @__PURE__ */
|
|
1315
|
-
/* @__PURE__ */
|
|
1372
|
+
return /* @__PURE__ */ jsxs10(Text11, { color: COLORS.status.running, children: [
|
|
1373
|
+
/* @__PURE__ */ jsx9(Spinner, { type: "dots" }),
|
|
1316
1374
|
" ",
|
|
1317
1375
|
` ${currentStep.title}`
|
|
1318
1376
|
] });
|
|
1319
1377
|
}
|
|
1320
1378
|
|
|
1321
1379
|
// src/ui/Progress.tsx
|
|
1322
|
-
import { Box as
|
|
1323
|
-
import { jsx as
|
|
1380
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1381
|
+
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1324
1382
|
function Progress() {
|
|
1325
1383
|
const { steps, currentStepIndex } = useWizard();
|
|
1326
1384
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1327
1385
|
if (visibleSteps.length === 0) return null;
|
|
1328
1386
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1329
1387
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1330
|
-
return /* @__PURE__ */
|
|
1331
|
-
/* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1334
|
-
/* @__PURE__ */
|
|
1388
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1389
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "STEP" }),
|
|
1390
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: activeStepNumber }),
|
|
1391
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: "/" }),
|
|
1392
|
+
/* @__PURE__ */ jsx10(Text12, { bold: true, children: visibleSteps.length })
|
|
1335
1393
|
] });
|
|
1336
1394
|
}
|
|
1337
1395
|
|
|
@@ -1342,10 +1400,10 @@ var sidebarCommands = [
|
|
|
1342
1400
|
];
|
|
1343
1401
|
|
|
1344
1402
|
// src/ui/Sidebar.tsx
|
|
1345
|
-
import { jsx as
|
|
1403
|
+
import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1346
1404
|
function Sidebar() {
|
|
1347
|
-
return /* @__PURE__ */
|
|
1348
|
-
|
|
1405
|
+
return /* @__PURE__ */ jsxs12(
|
|
1406
|
+
Box13,
|
|
1349
1407
|
{
|
|
1350
1408
|
backgroundColor: "#14171E",
|
|
1351
1409
|
width: 30,
|
|
@@ -1354,16 +1412,16 @@ function Sidebar() {
|
|
|
1354
1412
|
flexDirection: "column",
|
|
1355
1413
|
justifyContent: "space-between",
|
|
1356
1414
|
children: [
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
/* @__PURE__ */
|
|
1415
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1416
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1417
|
+
/* @__PURE__ */ jsx11(Steps, {})
|
|
1360
1418
|
] }),
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__PURE__ */
|
|
1363
|
-
/* @__PURE__ */
|
|
1364
|
-
return /* @__PURE__ */
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
/* @__PURE__ */
|
|
1419
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1420
|
+
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1421
|
+
/* @__PURE__ */ jsx11(Box13, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1422
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
|
|
1423
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1424
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: c.description })
|
|
1367
1425
|
] });
|
|
1368
1426
|
}) })
|
|
1369
1427
|
] })
|
|
@@ -1373,12 +1431,12 @@ function Sidebar() {
|
|
|
1373
1431
|
}
|
|
1374
1432
|
|
|
1375
1433
|
// src/ui/Ribbon.tsx
|
|
1376
|
-
import { Box as
|
|
1377
|
-
import { jsx as
|
|
1434
|
+
import { Box as Box14, Text as Text14 } from "ink";
|
|
1435
|
+
import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1378
1436
|
function Ribbon() {
|
|
1379
1437
|
const firstCommand = sidebarCommands[0];
|
|
1380
|
-
return /* @__PURE__ */
|
|
1381
|
-
|
|
1438
|
+
return /* @__PURE__ */ jsxs13(
|
|
1439
|
+
Box14,
|
|
1382
1440
|
{
|
|
1383
1441
|
backgroundColor: "#14171E",
|
|
1384
1442
|
flexDirection: "row",
|
|
@@ -1386,11 +1444,11 @@ function Ribbon() {
|
|
|
1386
1444
|
paddingX: 2,
|
|
1387
1445
|
paddingY: 1,
|
|
1388
1446
|
children: [
|
|
1389
|
-
/* @__PURE__ */
|
|
1390
|
-
/* @__PURE__ */
|
|
1391
|
-
/* @__PURE__ */
|
|
1392
|
-
/* @__PURE__ */
|
|
1393
|
-
/* @__PURE__ */
|
|
1447
|
+
/* @__PURE__ */ jsx12(Progress, {}),
|
|
1448
|
+
/* @__PURE__ */ jsx12(CurrentStep, {}),
|
|
1449
|
+
/* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: 1, children: [
|
|
1450
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1451
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: firstCommand.description })
|
|
1394
1452
|
] })
|
|
1395
1453
|
]
|
|
1396
1454
|
}
|
|
@@ -1401,8 +1459,8 @@ function Ribbon() {
|
|
|
1401
1459
|
import { useState as useState6 } from "react";
|
|
1402
1460
|
|
|
1403
1461
|
// src/ui/Logs.tsx
|
|
1404
|
-
import { Box as
|
|
1405
|
-
import { jsx as
|
|
1462
|
+
import { Box as Box15, Text as Text15, useInput as useInput6 } from "ink";
|
|
1463
|
+
import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1406
1464
|
var KIND_COLOR = {
|
|
1407
1465
|
tool: COLORS.primary,
|
|
1408
1466
|
prompt: COLORS.badge
|
|
@@ -1433,14 +1491,14 @@ function formatTimestamp(ms) {
|
|
|
1433
1491
|
function Logs() {
|
|
1434
1492
|
const logs = useWizard((s) => s.logs);
|
|
1435
1493
|
const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
|
|
1436
|
-
|
|
1494
|
+
useInput6((_input, key) => {
|
|
1437
1495
|
if (key.upArrow) scroll.scrollBy(-1);
|
|
1438
1496
|
else if (key.downArrow) scroll.scrollBy(1);
|
|
1439
1497
|
});
|
|
1440
1498
|
const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
1441
|
-
return /* @__PURE__ */
|
|
1442
|
-
logs.length === 0 && /* @__PURE__ */
|
|
1443
|
-
/* @__PURE__ */
|
|
1499
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1500
|
+
logs.length === 0 && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "No logs yet." }),
|
|
1501
|
+
/* @__PURE__ */ jsx13(ScrollView, { scroll, children: visible.map((entry) => {
|
|
1444
1502
|
const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
|
|
1445
1503
|
const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
|
|
1446
1504
|
const rawPreview = rawInputText(entry.input);
|
|
@@ -1450,14 +1508,14 @@ function Logs() {
|
|
|
1450
1508
|
const name = truncate2(entry.name, budget);
|
|
1451
1509
|
budget -= name.length;
|
|
1452
1510
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1453
|
-
return /* @__PURE__ */
|
|
1454
|
-
/* @__PURE__ */
|
|
1455
|
-
/* @__PURE__ */
|
|
1456
|
-
preview && /* @__PURE__ */
|
|
1457
|
-
durationText && /* @__PURE__ */
|
|
1511
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1512
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: timestamp }),
|
|
1513
|
+
/* @__PURE__ */ jsx13(Text15, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1514
|
+
preview && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1515
|
+
durationText && /* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: durationText })
|
|
1458
1516
|
] }, entry.id);
|
|
1459
1517
|
}) }),
|
|
1460
|
-
/* @__PURE__ */
|
|
1518
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1461
1519
|
] });
|
|
1462
1520
|
}
|
|
1463
1521
|
|
|
@@ -1649,7 +1707,7 @@ function track(event, payload) {
|
|
|
1649
1707
|
}
|
|
1650
1708
|
|
|
1651
1709
|
// src/ui/App.tsx
|
|
1652
|
-
import { jsx as
|
|
1710
|
+
import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1653
1711
|
function App() {
|
|
1654
1712
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1655
1713
|
const { exit } = useApp();
|
|
@@ -1657,7 +1715,7 @@ function App() {
|
|
|
1657
1715
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1658
1716
|
const finished = phase === "done" || phase === "error";
|
|
1659
1717
|
const currentStep = steps[currentStepIndex];
|
|
1660
|
-
|
|
1718
|
+
useInput7(
|
|
1661
1719
|
(_input, key) => {
|
|
1662
1720
|
if (key.return) {
|
|
1663
1721
|
exit();
|
|
@@ -1665,7 +1723,7 @@ function App() {
|
|
|
1665
1723
|
},
|
|
1666
1724
|
{ isActive: finished }
|
|
1667
1725
|
);
|
|
1668
|
-
|
|
1726
|
+
useInput7((_input, key) => {
|
|
1669
1727
|
if (phase === "idle" || phase === "authenticating") return;
|
|
1670
1728
|
if (key.tab) {
|
|
1671
1729
|
setShowLogs(!showLogs);
|
|
@@ -1676,8 +1734,8 @@ function App() {
|
|
|
1676
1734
|
});
|
|
1677
1735
|
}
|
|
1678
1736
|
});
|
|
1679
|
-
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1680
|
-
|
|
1737
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
|
|
1738
|
+
useInput7((_input, key) => {
|
|
1681
1739
|
if (escOwnedElsewhere) return;
|
|
1682
1740
|
if (key.escape) {
|
|
1683
1741
|
track("AI Wizard Interaction", {
|
|
@@ -1696,8 +1754,8 @@ function App() {
|
|
|
1696
1754
|
/* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
|
|
1697
1755
|
the whole screen, and the scrolling throws off its cursor arithmetic —
|
|
1698
1756
|
flicker and leftover rows. */
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
|
|
1757
|
+
/* @__PURE__ */ jsxs15(
|
|
1758
|
+
Box16,
|
|
1701
1759
|
{
|
|
1702
1760
|
backgroundColor: COLORS.bg.main,
|
|
1703
1761
|
flexDirection: "row",
|
|
@@ -1705,16 +1763,16 @@ function App() {
|
|
|
1705
1763
|
height: scrollsPastViewport ? void 0 : rows,
|
|
1706
1764
|
overflow: scrollsPastViewport ? "visible" : "hidden",
|
|
1707
1765
|
children: [
|
|
1708
|
-
mainWindowVisible && /* @__PURE__ */
|
|
1709
|
-
|
|
1766
|
+
mainWindowVisible && /* @__PURE__ */ jsxs15(
|
|
1767
|
+
Box16,
|
|
1710
1768
|
{
|
|
1711
1769
|
flexDirection,
|
|
1712
1770
|
width: "100%",
|
|
1713
1771
|
maxHeight: rows,
|
|
1714
1772
|
justifyContent: "space-between",
|
|
1715
1773
|
children: [
|
|
1716
|
-
showLogs ? /* @__PURE__ */
|
|
1717
|
-
|
|
1774
|
+
showLogs ? /* @__PURE__ */ jsx14(Logs, {}) : /* @__PURE__ */ jsxs15(
|
|
1775
|
+
Box16,
|
|
1718
1776
|
{
|
|
1719
1777
|
flexDirection: "column",
|
|
1720
1778
|
paddingX: 4,
|
|
@@ -1722,26 +1780,26 @@ function App() {
|
|
|
1722
1780
|
width: showSidebar ? 70 : "100%",
|
|
1723
1781
|
flexGrow: 1,
|
|
1724
1782
|
children: [
|
|
1725
|
-
phase === "authenticating" && /* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
/* @__PURE__ */
|
|
1783
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
1784
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1785
|
+
/* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1728
1786
|
] }),
|
|
1729
|
-
/* @__PURE__ */
|
|
1730
|
-
/* @__PURE__ */
|
|
1731
|
-
/* @__PURE__ */
|
|
1732
|
-
phase === "running" && showSidebar && /* @__PURE__ */
|
|
1733
|
-
phase === "error" && error && /* @__PURE__ */
|
|
1787
|
+
/* @__PURE__ */ jsx14(CliOutput, {}),
|
|
1788
|
+
/* @__PURE__ */ jsx14(Notices, {}),
|
|
1789
|
+
/* @__PURE__ */ jsx14(PromptInput, {}),
|
|
1790
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx14(CurrentStep, {}) }),
|
|
1791
|
+
phase === "error" && error && /* @__PURE__ */ jsx14(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: COLORS.status.error, children: [
|
|
1734
1792
|
"\u2716 ",
|
|
1735
1793
|
error
|
|
1736
1794
|
] }) })
|
|
1737
1795
|
]
|
|
1738
1796
|
}
|
|
1739
1797
|
),
|
|
1740
|
-
showSidebar ? /* @__PURE__ */
|
|
1798
|
+
showSidebar ? /* @__PURE__ */ jsx14(Sidebar, {}) : /* @__PURE__ */ jsx14(Ribbon, {})
|
|
1741
1799
|
]
|
|
1742
1800
|
}
|
|
1743
1801
|
),
|
|
1744
|
-
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */
|
|
1802
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx14(LearnMore, {}) : /* @__PURE__ */ jsx14(Welcome, {}))
|
|
1745
1803
|
]
|
|
1746
1804
|
}
|
|
1747
1805
|
)
|
|
@@ -1821,7 +1879,7 @@ async function ensureConsent() {
|
|
|
1821
1879
|
if (config.aiConsent) return;
|
|
1822
1880
|
const store = useWizard.getState();
|
|
1823
1881
|
const answer = await store.requestUserInput({
|
|
1824
|
-
prompt: "Wizard will make AI-authored changes to this repository.",
|
|
1882
|
+
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
1883
|
promptType: "enterToContinue",
|
|
1826
1884
|
options: []
|
|
1827
1885
|
});
|
|
@@ -2576,11 +2634,19 @@ import z13 from "zod";
|
|
|
2576
2634
|
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2577
2635
|
import { join as join7 } from "node:path";
|
|
2578
2636
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2637
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2638
|
+
"node_modules",
|
|
2639
|
+
"dist",
|
|
2640
|
+
"build",
|
|
2641
|
+
"vendor",
|
|
2642
|
+
"venv",
|
|
2643
|
+
"__pycache__",
|
|
2644
|
+
"target"
|
|
2645
|
+
]);
|
|
2579
2646
|
async function walkFiles(dir) {
|
|
2580
|
-
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2581
2647
|
const out = [];
|
|
2582
2648
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2583
|
-
if (e.name.startsWith(".") ||
|
|
2649
|
+
if (e.name.startsWith(".") || SKIP_DIRS.has(e.name)) continue;
|
|
2584
2650
|
const full = join7(dir, e.name);
|
|
2585
2651
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2586
2652
|
else if (e.isFile()) out.push(full);
|
|
@@ -2634,92 +2700,188 @@ function searchFilesTool(ctx) {
|
|
|
2634
2700
|
});
|
|
2635
2701
|
}
|
|
2636
2702
|
|
|
2637
|
-
// src/lib/tools/
|
|
2703
|
+
// src/lib/tools/runShell.ts
|
|
2638
2704
|
import { tool as tool8 } from "ai";
|
|
2639
2705
|
import z14 from "zod";
|
|
2706
|
+
import { relative as relative2 } from "node:path";
|
|
2640
2707
|
|
|
2641
|
-
// src/lib/tools/utils/
|
|
2708
|
+
// src/lib/tools/utils/runShell.ts
|
|
2642
2709
|
import { spawn as spawn2 } from "node:child_process";
|
|
2643
|
-
|
|
2710
|
+
|
|
2711
|
+
// src/lib/tools/context.ts
|
|
2712
|
+
var DEFAULT_TOOL_LIMITS = {
|
|
2713
|
+
list: 10,
|
|
2714
|
+
search: 10,
|
|
2715
|
+
read: 20,
|
|
2716
|
+
match: 100,
|
|
2717
|
+
shell: 30
|
|
2718
|
+
};
|
|
2719
|
+
var DEFAULT_SHELL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2720
|
+
async function refuseByDefault() {
|
|
2721
|
+
return "reject";
|
|
2722
|
+
}
|
|
2723
|
+
function createShellContext(overrides = {}) {
|
|
2724
|
+
return {
|
|
2725
|
+
env: async () => ({}),
|
|
2726
|
+
timeoutMs: DEFAULT_SHELL_TIMEOUT_MS,
|
|
2727
|
+
executions: [],
|
|
2728
|
+
approve: refuseByDefault,
|
|
2729
|
+
...overrides
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd(), shell2 = createShellContext()) {
|
|
2733
|
+
return {
|
|
2734
|
+
root: cwd,
|
|
2735
|
+
cwd,
|
|
2736
|
+
limits: { ...limits },
|
|
2737
|
+
counts: { list: 0, search: 0, read: 0, shell: 0 },
|
|
2738
|
+
shell: shell2
|
|
2739
|
+
};
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
// src/lib/tools/utils/runShell.ts
|
|
2743
|
+
var SIGKILL_DELAY_MS = 5e3;
|
|
2744
|
+
var HEAD_CHARS = 4e3;
|
|
2745
|
+
var TAIL_CHARS = 8e3;
|
|
2746
|
+
function truncateOutput(output) {
|
|
2747
|
+
if (output.length <= HEAD_CHARS + TAIL_CHARS) return output;
|
|
2748
|
+
const omitted = output.length - HEAD_CHARS - TAIL_CHARS;
|
|
2749
|
+
return [
|
|
2750
|
+
output.slice(0, HEAD_CHARS),
|
|
2751
|
+
`
|
|
2752
|
+
\u2026 [${omitted} characters omitted] \u2026
|
|
2753
|
+
`,
|
|
2754
|
+
output.slice(-TAIL_CHARS)
|
|
2755
|
+
].join("");
|
|
2756
|
+
}
|
|
2757
|
+
function runShell(command, opts) {
|
|
2758
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
|
|
2759
|
+
const startedAt = Date.now();
|
|
2644
2760
|
return new Promise((resolve4) => {
|
|
2645
2761
|
let output = "";
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2762
|
+
let timedOut = false;
|
|
2763
|
+
let settled = false;
|
|
2764
|
+
const child = spawn2(command, {
|
|
2765
|
+
shell: true,
|
|
2766
|
+
cwd: opts.cwd,
|
|
2767
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2768
|
+
env: { ...process.env, ...opts.env }
|
|
2649
2769
|
});
|
|
2770
|
+
const finish = (exitCode) => {
|
|
2771
|
+
if (settled) return;
|
|
2772
|
+
settled = true;
|
|
2773
|
+
clearTimeout(timer);
|
|
2774
|
+
clearTimeout(killTimer);
|
|
2775
|
+
resolve4({
|
|
2776
|
+
exitCode,
|
|
2777
|
+
output: truncateOutput(output.trim()),
|
|
2778
|
+
timedOut,
|
|
2779
|
+
durationMs: Date.now() - startedAt
|
|
2780
|
+
});
|
|
2781
|
+
};
|
|
2782
|
+
let killTimer;
|
|
2783
|
+
const timer = setTimeout(() => {
|
|
2784
|
+
timedOut = true;
|
|
2785
|
+
output += `
|
|
2786
|
+
[timed out after ${timeoutMs}ms]`;
|
|
2787
|
+
child.kill("SIGTERM");
|
|
2788
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), SIGKILL_DELAY_MS);
|
|
2789
|
+
}, timeoutMs);
|
|
2650
2790
|
child.stdout?.on("data", (d) => output += d);
|
|
2651
2791
|
child.stderr?.on("data", (d) => output += d);
|
|
2652
|
-
child.on(
|
|
2653
|
-
|
|
2654
|
-
(
|
|
2655
|
-
);
|
|
2656
|
-
child.on("close", (code) =>
|
|
2792
|
+
child.on("error", (err) => {
|
|
2793
|
+
output += `Failed to run ${command}: ${err.message}`;
|
|
2794
|
+
finish(1);
|
|
2795
|
+
});
|
|
2796
|
+
child.on("close", (code) => finish(code ?? 1));
|
|
2657
2797
|
});
|
|
2658
2798
|
}
|
|
2659
2799
|
|
|
2660
|
-
// src/lib/tools/
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2676
|
-
}
|
|
2677
|
-
function packageManagerFromLockfile(cwd) {
|
|
2678
|
-
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2679
|
-
}
|
|
2680
|
-
async function detectPackageManager(cwd) {
|
|
2681
|
-
try {
|
|
2682
|
-
const pkg = await readPackageJson(cwd);
|
|
2683
|
-
if (pkg.packageManager) return packageManagerFrom(pkg);
|
|
2684
|
-
} catch {
|
|
2685
|
-
}
|
|
2686
|
-
return packageManagerFromLockfile(cwd) ?? "npm";
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
// src/lib/tools/repoVerification.ts
|
|
2690
|
-
var VERIFICATION_SCRIPT_CANDIDATES = ["lint", "typecheck", "check"];
|
|
2691
|
-
async function runRepoVerificationCheck() {
|
|
2692
|
-
let pkg;
|
|
2693
|
-
try {
|
|
2694
|
-
pkg = await readPackageJson();
|
|
2695
|
-
} catch (err) {
|
|
2696
|
-
const limitation = `Could not read package.json to detect verification conventions: ${err.message}`;
|
|
2697
|
-
return { ok: false, checks: [], limitation };
|
|
2698
|
-
}
|
|
2699
|
-
const scripts = pkg.scripts ?? {};
|
|
2700
|
-
const present = VERIFICATION_SCRIPT_CANDIDATES.filter((s) => s in scripts);
|
|
2701
|
-
if (present.length === 0) {
|
|
2702
|
-
const limitation = `No verification script found in package.json (looked for: ${VERIFICATION_SCRIPT_CANDIDATES.join(", ")}).`;
|
|
2703
|
-
return { ok: false, checks: [], limitation };
|
|
2704
|
-
}
|
|
2705
|
-
const pm = await detectPackageManager(process.cwd());
|
|
2706
|
-
const checks = [];
|
|
2707
|
-
for (const script of present) {
|
|
2708
|
-
const command = `${pm} run ${script}`;
|
|
2709
|
-
const { code, output } = await runCommand(pm, ["run", script]);
|
|
2710
|
-
checks.push({ command, exitCode: code, ok: code === 0, output: output.trim() });
|
|
2711
|
-
}
|
|
2712
|
-
return { ok: checks.every((c) => c.ok), checks };
|
|
2800
|
+
// src/lib/tools/runShell.ts
|
|
2801
|
+
function storeApproval(root) {
|
|
2802
|
+
return async (req) => {
|
|
2803
|
+
const rel = relative2(root, req.cwd);
|
|
2804
|
+
const answer = await useWizard.getState().requestUserInput({
|
|
2805
|
+
prompt: "Run this command?",
|
|
2806
|
+
promptType: "commandApproval",
|
|
2807
|
+
options: [],
|
|
2808
|
+
command: {
|
|
2809
|
+
...req,
|
|
2810
|
+
cwd: rel === "" || rel.startsWith("..") ? req.cwd : rel
|
|
2811
|
+
}
|
|
2812
|
+
});
|
|
2813
|
+
return answer === "approve" ? "approve" : "reject";
|
|
2814
|
+
};
|
|
2713
2815
|
}
|
|
2714
|
-
|
|
2715
|
-
// src/lib/tools/verifyImplementation.ts
|
|
2716
|
-
function verifyImplementationTool() {
|
|
2816
|
+
function runShellTool(ctx) {
|
|
2717
2817
|
return tool8({
|
|
2718
|
-
description: "Run the
|
|
2719
|
-
inputSchema: z14.object(
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2818
|
+
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.",
|
|
2819
|
+
inputSchema: z14.object({
|
|
2820
|
+
command: z14.string().describe(
|
|
2821
|
+
"The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
|
|
2822
|
+
),
|
|
2823
|
+
cwd: z14.string().optional().describe(
|
|
2824
|
+
"Directory to run in, relative to the project root. Defaults to the project root."
|
|
2825
|
+
),
|
|
2826
|
+
explanation: z14.string().describe(
|
|
2827
|
+
"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."
|
|
2828
|
+
)
|
|
2829
|
+
}),
|
|
2830
|
+
execute: async ({ command, cwd, explanation }) => {
|
|
2831
|
+
if (++ctx.counts.shell > ctx.limits.shell) {
|
|
2832
|
+
return `Refused: command limit (${ctx.limits.shell}) reached. Stop running commands and report what you have.`;
|
|
2833
|
+
}
|
|
2834
|
+
const resolved2 = resolveInRoot(ctx, cwd ?? ".");
|
|
2835
|
+
if (!resolved2.ok) return resolved2.error;
|
|
2836
|
+
logger.info({ command, cwd: resolved2.target }, "called runShell tool");
|
|
2837
|
+
const decision = await ctx.shell.approve({
|
|
2838
|
+
command,
|
|
2839
|
+
cwd: resolved2.target,
|
|
2840
|
+
explanation
|
|
2841
|
+
});
|
|
2842
|
+
if (decision === "reject") {
|
|
2843
|
+
ctx.shell.executions.push({
|
|
2844
|
+
command,
|
|
2845
|
+
cwd: resolved2.target,
|
|
2846
|
+
approved: false
|
|
2847
|
+
});
|
|
2848
|
+
logger.info({ command }, "runShell: user rejected the command");
|
|
2849
|
+
return "The user rejected this command. Do not retry it. Propose a different command, or report the limitation via reportStatus.";
|
|
2850
|
+
}
|
|
2851
|
+
useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
|
|
2852
|
+
const env = await ctx.shell.env().catch((err) => {
|
|
2853
|
+
logger.warn({ err, command }, "runShell: could not resolve command env");
|
|
2854
|
+
return {};
|
|
2855
|
+
});
|
|
2856
|
+
const run2 = await (ctx.shell.run ?? runShell)(command, {
|
|
2857
|
+
cwd: resolved2.target,
|
|
2858
|
+
env,
|
|
2859
|
+
timeoutMs: ctx.shell.timeoutMs
|
|
2860
|
+
});
|
|
2861
|
+
logger.info(
|
|
2862
|
+
{
|
|
2863
|
+
command,
|
|
2864
|
+
exitCode: run2.exitCode,
|
|
2865
|
+
timedOut: run2.timedOut,
|
|
2866
|
+
durationMs: run2.durationMs
|
|
2867
|
+
},
|
|
2868
|
+
"runShell finished"
|
|
2869
|
+
);
|
|
2870
|
+
await markInteraction();
|
|
2871
|
+
ctx.shell.executions.push({
|
|
2872
|
+
command,
|
|
2873
|
+
cwd: resolved2.target,
|
|
2874
|
+
approved: true,
|
|
2875
|
+
exitCode: run2.exitCode,
|
|
2876
|
+
output: run2.output,
|
|
2877
|
+
timedOut: run2.timedOut,
|
|
2878
|
+
durationMs: run2.durationMs
|
|
2879
|
+
});
|
|
2880
|
+
return {
|
|
2881
|
+
exitCode: run2.exitCode,
|
|
2882
|
+
timedOut: run2.timedOut,
|
|
2883
|
+
output: run2.output
|
|
2884
|
+
};
|
|
2723
2885
|
}
|
|
2724
2886
|
});
|
|
2725
2887
|
}
|
|
@@ -2810,7 +2972,7 @@ function generateRecordTool(ctx) {
|
|
|
2810
2972
|
return {
|
|
2811
2973
|
filePath: relPath,
|
|
2812
2974
|
count: records.length,
|
|
2813
|
-
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime
|
|
2975
|
+
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
2976
|
};
|
|
2815
2977
|
} catch (err) {
|
|
2816
2978
|
return `Error generating records: ${err.message}`;
|
|
@@ -2838,22 +3000,6 @@ function notifyUserTool() {
|
|
|
2838
3000
|
});
|
|
2839
3001
|
}
|
|
2840
3002
|
|
|
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
3003
|
// src/lib/tools/index.ts
|
|
2858
3004
|
function withLogging(name, def) {
|
|
2859
3005
|
const execute = def.execute;
|
|
@@ -2885,10 +3031,7 @@ function createTools(ctx, { output, tools }) {
|
|
|
2885
3031
|
writeCredentialsTool(ctx)
|
|
2886
3032
|
),
|
|
2887
3033
|
searchFiles: withLogging("searchFiles", searchFilesTool(ctx)),
|
|
2888
|
-
|
|
2889
|
-
"verifyImplementation",
|
|
2890
|
-
verifyImplementationTool()
|
|
2891
|
-
),
|
|
3034
|
+
runShell: withLogging("runShell", runShellTool(ctx)),
|
|
2892
3035
|
generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
|
|
2893
3036
|
notifyUser: withLogging("notifyUser", notifyUserTool())
|
|
2894
3037
|
};
|
|
@@ -2923,7 +3066,7 @@ async function runAgent(req) {
|
|
|
2923
3066
|
baseURL: PROXY_BASE_URL,
|
|
2924
3067
|
fetch: proxyFetch
|
|
2925
3068
|
});
|
|
2926
|
-
const toolContext = createToolContext();
|
|
3069
|
+
const toolContext = req.toolContext ?? createToolContext();
|
|
2927
3070
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
2928
3071
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
2929
3072
|
const instructions = [
|
|
@@ -2988,7 +3131,11 @@ async function runAgent(req) {
|
|
|
2988
3131
|
"runAgent finished"
|
|
2989
3132
|
);
|
|
2990
3133
|
logger.info(
|
|
2991
|
-
{
|
|
3134
|
+
{
|
|
3135
|
+
counts: toolContext.counts,
|
|
3136
|
+
limits: toolContext.limits,
|
|
3137
|
+
commandsRun: toolContext.shell.executions.length
|
|
3138
|
+
},
|
|
2992
3139
|
"tool usage"
|
|
2993
3140
|
);
|
|
2994
3141
|
const toolResults = await stream.toolResults;
|
|
@@ -3014,8 +3161,8 @@ var detectLanguageSchema = z19.object({
|
|
|
3014
3161
|
var detectLanguage = () => runAgent({
|
|
3015
3162
|
instructions: [
|
|
3016
3163
|
"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
|
|
3164
|
+
"If a superset language is found, exclude the subset language (e.g. TypeScript over JavaScript).",
|
|
3165
|
+
"If a meta-framework is used, exclude the framework it builds on (e.g. Next.js over React, Rails over Rack).",
|
|
3019
3166
|
"Return the exact version",
|
|
3020
3167
|
"Exclude things like CSS frameworks, build tools, or testing frameworks",
|
|
3021
3168
|
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
@@ -3113,7 +3260,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3113
3260
|
// package.json
|
|
3114
3261
|
var package_default = {
|
|
3115
3262
|
name: "@algolia/wizard",
|
|
3116
|
-
version: "0.9.0-rc.
|
|
3263
|
+
version: "0.9.0-rc.93.95",
|
|
3117
3264
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3118
3265
|
type: "module",
|
|
3119
3266
|
engines: {
|
|
@@ -3262,9 +3409,11 @@ var CURATED_FRAMEWORKS = [
|
|
|
3262
3409
|
"Next.js",
|
|
3263
3410
|
"React",
|
|
3264
3411
|
"Vue",
|
|
3265
|
-
"
|
|
3266
|
-
"
|
|
3267
|
-
"
|
|
3412
|
+
"Vanilla JS",
|
|
3413
|
+
"Django",
|
|
3414
|
+
"Laravel",
|
|
3415
|
+
"Rails",
|
|
3416
|
+
"Symfony"
|
|
3268
3417
|
];
|
|
3269
3418
|
var OTHER_OPTION = "Other";
|
|
3270
3419
|
var normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -3277,12 +3426,15 @@ var FRAMEWORK_ALIASES = {
|
|
|
3277
3426
|
vuejs: "vue",
|
|
3278
3427
|
angular: "angular",
|
|
3279
3428
|
angularjs: "angular",
|
|
3280
|
-
svelte: "svelte",
|
|
3281
|
-
sveltekit: "svelte",
|
|
3282
3429
|
vanillajs: "vanillajs",
|
|
3283
3430
|
vanilla: "vanillajs",
|
|
3284
3431
|
javascript: "vanillajs",
|
|
3285
|
-
js: "vanillajs"
|
|
3432
|
+
js: "vanillajs",
|
|
3433
|
+
django: "django",
|
|
3434
|
+
laravel: "laravel",
|
|
3435
|
+
rails: "rails",
|
|
3436
|
+
rubyonrails: "rails",
|
|
3437
|
+
symfony: "symfony"
|
|
3286
3438
|
};
|
|
3287
3439
|
var isSameFramework = (a, b) => {
|
|
3288
3440
|
const x = FRAMEWORK_ALIASES[normalize(a)] ?? normalize(a);
|
|
@@ -3516,16 +3668,9 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3516
3668
|
import z26 from "zod";
|
|
3517
3669
|
|
|
3518
3670
|
// 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";
|
|
3671
|
+
import { execFile } from "node:child_process";
|
|
3672
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile6, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3673
|
+
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join8, resolve as resolve3 } from "node:path";
|
|
3529
3674
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
3530
3675
|
var MAX_WIZARD_WORKTREES = 3;
|
|
3531
3676
|
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
@@ -3556,7 +3701,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3556
3701
|
return out.trim().length > 0;
|
|
3557
3702
|
}
|
|
3558
3703
|
async function pruneOldWorktrees(repoRoot) {
|
|
3559
|
-
const dir =
|
|
3704
|
+
const dir = join8(stateDir(repoRoot), "worktrees");
|
|
3560
3705
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3561
3706
|
for (const slug of stale) {
|
|
3562
3707
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3567,7 +3712,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3567
3712
|
"worktree",
|
|
3568
3713
|
"remove",
|
|
3569
3714
|
"--force",
|
|
3570
|
-
|
|
3715
|
+
join8(dir, slug)
|
|
3571
3716
|
]);
|
|
3572
3717
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3573
3718
|
} catch (err) {
|
|
@@ -3581,113 +3726,13 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3581
3726
|
async function createWorktree(repoRoot) {
|
|
3582
3727
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3583
3728
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3584
|
-
const path =
|
|
3729
|
+
const path = join8(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3585
3730
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3586
3731
|
await pruneOldWorktrees(repoRoot);
|
|
3587
3732
|
await mkdir6(dirname7(path), { recursive: true });
|
|
3588
3733
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3589
3734
|
return { path, branch };
|
|
3590
3735
|
}
|
|
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
3736
|
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
3692
3737
|
const trimmed = sourcePath.trim();
|
|
3693
3738
|
if (!trimmed) {
|
|
@@ -3701,8 +3746,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3701
3746
|
} catch {
|
|
3702
3747
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3703
3748
|
}
|
|
3704
|
-
const relPath =
|
|
3705
|
-
const dest =
|
|
3749
|
+
const relPath = join8(ingestDir, basename2(source));
|
|
3750
|
+
const dest = join8(worktreePath, relPath);
|
|
3706
3751
|
try {
|
|
3707
3752
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3708
3753
|
await copyFile(source, dest);
|
|
@@ -3720,7 +3765,7 @@ function hasEnvVar(content, name) {
|
|
|
3720
3765
|
async function readEnvVar(worktreePath, name) {
|
|
3721
3766
|
let content;
|
|
3722
3767
|
try {
|
|
3723
|
-
content = await
|
|
3768
|
+
content = await readFile6(join8(worktreePath, ".env"), "utf8");
|
|
3724
3769
|
} catch (err) {
|
|
3725
3770
|
if (err.code !== "ENOENT") throw err;
|
|
3726
3771
|
return void 0;
|
|
@@ -3735,10 +3780,10 @@ async function readEnvVar(worktreePath, name) {
|
|
|
3735
3780
|
return value;
|
|
3736
3781
|
}
|
|
3737
3782
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3738
|
-
const target =
|
|
3783
|
+
const target = join8(worktreePath, ".env");
|
|
3739
3784
|
let existing = "";
|
|
3740
3785
|
try {
|
|
3741
|
-
existing = await
|
|
3786
|
+
existing = await readFile6(target, "utf8");
|
|
3742
3787
|
} catch (err) {
|
|
3743
3788
|
if (err.code !== "ENOENT") throw err;
|
|
3744
3789
|
}
|
|
@@ -3807,15 +3852,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3807
3852
|
}
|
|
3808
3853
|
|
|
3809
3854
|
// src/lib/algoliaDocs.ts
|
|
3810
|
-
import { readFileSync, readdirSync, existsSync
|
|
3811
|
-
import { dirname as dirname8, join as
|
|
3855
|
+
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
3856
|
+
import { dirname as dirname8, join as join9 } from "node:path";
|
|
3812
3857
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3813
|
-
var DOCS_SUBPATH =
|
|
3858
|
+
var DOCS_SUBPATH = join9("docs", "algolia-sdk");
|
|
3814
3859
|
function findDocsDir() {
|
|
3815
3860
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3816
3861
|
for (; ; ) {
|
|
3817
|
-
const candidate =
|
|
3818
|
-
if (
|
|
3862
|
+
const candidate = join9(dir, DOCS_SUBPATH);
|
|
3863
|
+
if (existsSync(candidate)) return candidate;
|
|
3819
3864
|
const parent = dirname8(dir);
|
|
3820
3865
|
if (parent === dir) return void 0;
|
|
3821
3866
|
dir = parent;
|
|
@@ -3837,7 +3882,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3837
3882
|
);
|
|
3838
3883
|
return "";
|
|
3839
3884
|
}
|
|
3840
|
-
return readFileSync(
|
|
3885
|
+
return readFileSync(join9(docsDir, files[0]), "utf8").trim();
|
|
3841
3886
|
}
|
|
3842
3887
|
function getNamedDoc(name, language) {
|
|
3843
3888
|
const docsDir = findDocsDir();
|
|
@@ -3845,14 +3890,15 @@ function getNamedDoc(name, language) {
|
|
|
3845
3890
|
logger.warn("docs/algolia-sdk not found");
|
|
3846
3891
|
return "";
|
|
3847
3892
|
}
|
|
3848
|
-
const file =
|
|
3849
|
-
if (!
|
|
3893
|
+
const file = join9(docsDir, `${name}-${language}.md`);
|
|
3894
|
+
if (!existsSync(file)) {
|
|
3850
3895
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3851
3896
|
return "";
|
|
3852
3897
|
}
|
|
3853
3898
|
return readFileSync(file, "utf8").trim();
|
|
3854
3899
|
}
|
|
3855
3900
|
function getFrameworkSpecificDoc(frameworks) {
|
|
3901
|
+
if (frameworks.length === 0) return "";
|
|
3856
3902
|
const fw = frameworks.map((f) => f.toLowerCase());
|
|
3857
3903
|
if (fw.includes("vue") || fw.includes("nuxt")) {
|
|
3858
3904
|
return loadAlgoliaDoc("vue");
|
|
@@ -3860,9 +3906,6 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
3860
3906
|
if (fw.includes("react") || fw.includes("next.js")) {
|
|
3861
3907
|
return loadAlgoliaDoc("react");
|
|
3862
3908
|
}
|
|
3863
|
-
if (fw.includes("angular")) {
|
|
3864
|
-
return loadAlgoliaDoc("angular");
|
|
3865
|
-
}
|
|
3866
3909
|
return loadAlgoliaDoc("js");
|
|
3867
3910
|
}
|
|
3868
3911
|
|
|
@@ -3890,11 +3933,7 @@ var implementSchema = z26.object({
|
|
|
3890
3933
|
});
|
|
3891
3934
|
var implementationOutputSchema = z26.object({
|
|
3892
3935
|
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()
|
|
3936
|
+
ingestCommand: z26.string().optional()
|
|
3898
3937
|
});
|
|
3899
3938
|
var verificationOutputSchema = z26.object({
|
|
3900
3939
|
summary: z26.string(),
|
|
@@ -3904,30 +3943,35 @@ var verificationOutputSchema = z26.object({
|
|
|
3904
3943
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3905
3944
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
3906
3945
|
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
|
-
}
|
|
3946
|
+
var JS_LANGUAGES = ["javascript", "typescript", "jsx", "tsx", "node"];
|
|
3947
|
+
function lower(entries) {
|
|
3948
|
+
return entries.map((entry) => entry.name.toLowerCase());
|
|
3926
3949
|
}
|
|
3927
|
-
function
|
|
3928
|
-
|
|
3929
|
-
(
|
|
3950
|
+
function isJsProject(language) {
|
|
3951
|
+
return lower(language.languages).some(
|
|
3952
|
+
(name) => JS_LANGUAGES.some((js) => name.includes(js))
|
|
3930
3953
|
);
|
|
3954
|
+
}
|
|
3955
|
+
var UI_FRAMEWORKS = [
|
|
3956
|
+
{ match: ["vue", "nuxt"], target: "Vue", doc: "vue" },
|
|
3957
|
+
{ match: ["react", "next"], target: "React", doc: "react" },
|
|
3958
|
+
{ match: ["angular"], target: "Angular" }
|
|
3959
|
+
];
|
|
3960
|
+
function matchUiFramework(language) {
|
|
3961
|
+
const names = lower(language.frameworks);
|
|
3962
|
+
return UI_FRAMEWORKS.find(
|
|
3963
|
+
(ui) => ui.match.some((needle) => names.some((name) => name.includes(needle)))
|
|
3964
|
+
);
|
|
3965
|
+
}
|
|
3966
|
+
function searchUiTarget(language) {
|
|
3967
|
+
return matchUiFramework(language)?.target ?? language.frameworks[0]?.name ?? (isJsProject(language) ? "JavaScript" : "this project");
|
|
3968
|
+
}
|
|
3969
|
+
function frameworksForDoc(language) {
|
|
3970
|
+
if (!isJsProject(language)) return [];
|
|
3971
|
+
return [matchUiFramework(language)?.doc ?? "js"];
|
|
3972
|
+
}
|
|
3973
|
+
function publicEnvPrefix(language) {
|
|
3974
|
+
const frameworkNames = lower(language.frameworks);
|
|
3931
3975
|
if (frameworkNames.some((name) => name.includes("next"))) {
|
|
3932
3976
|
return "NEXT_PUBLIC_";
|
|
3933
3977
|
}
|
|
@@ -3940,7 +3984,7 @@ function publicEnvPrefix(language) {
|
|
|
3940
3984
|
if (frameworkNames.some((name) => name.includes("vite"))) {
|
|
3941
3985
|
return "VITE_";
|
|
3942
3986
|
}
|
|
3943
|
-
return "PUBLIC_";
|
|
3987
|
+
return isJsProject(language) ? "PUBLIC_" : "";
|
|
3944
3988
|
}
|
|
3945
3989
|
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
3946
3990
|
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
@@ -3979,7 +4023,10 @@ function baseInstructions(input) {
|
|
|
3979
4023
|
// index-scoped keys then reject with a 403.
|
|
3980
4024
|
`Target Algolia index, to be used exactly as written \u2014 never renamed, re-cased, prefixed, or suffixed: "${input.targetIndex}"`,
|
|
3981
4025
|
`Project languages and frameworks: ${JSON.stringify(input.language)}`,
|
|
3982
|
-
"Make minimal, idiomatic changes; do not touch unrelated code."
|
|
4026
|
+
"Make minimal, idiomatic changes; do not touch unrelated code.",
|
|
4027
|
+
`Commands run through a shell on ${process.platform}. Write commands that work there.`,
|
|
4028
|
+
"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.",
|
|
4029
|
+
"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
4030
|
];
|
|
3984
4031
|
}
|
|
3985
4032
|
function sourceSpecificInstructions(input) {
|
|
@@ -3999,12 +4046,21 @@ function sourceSpecificInstructions(input) {
|
|
|
3999
4046
|
generated: [
|
|
4000
4047
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
4001
4048
|
"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
|
|
4049
|
+
"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
4050
|
"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
4051
|
]
|
|
4005
4052
|
};
|
|
4006
4053
|
return byLine[input.ingestionSource];
|
|
4007
4054
|
}
|
|
4055
|
+
function algoliaClientDoc(input) {
|
|
4056
|
+
const doc = getNamedDoc("save-records", "js");
|
|
4057
|
+
if (!doc) return [];
|
|
4058
|
+
if (isJsProject(input.language)) return [doc];
|
|
4059
|
+
return [
|
|
4060
|
+
"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:",
|
|
4061
|
+
doc
|
|
4062
|
+
];
|
|
4063
|
+
}
|
|
4008
4064
|
function ingestionInstructions(input) {
|
|
4009
4065
|
return [
|
|
4010
4066
|
...input.confirmed && input.confirmed.length ? [
|
|
@@ -4012,25 +4068,30 @@ function ingestionInstructions(input) {
|
|
|
4012
4068
|
`Ingest only these confirmed entities (name, source paths, attributes): ${JSON.stringify(input.confirmed)}.`,
|
|
4013
4069
|
`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
4070
|
`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
|
-
"
|
|
4071
|
+
"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
4072
|
"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
|
-
'
|
|
4073
|
+
...algoliaClientDoc(input),
|
|
4074
|
+
"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.",
|
|
4075
|
+
'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
4076
|
"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
4077
|
...sourceSpecificInstructions(input)
|
|
4022
4078
|
] : []
|
|
4023
4079
|
];
|
|
4024
4080
|
}
|
|
4025
4081
|
function searchInstructions(input) {
|
|
4026
|
-
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.
|
|
4082
|
+
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.language));
|
|
4027
4083
|
return [
|
|
4028
4084
|
"Implement an in-app Algolia search experience.",
|
|
4029
|
-
`Build the search UI for ${input.
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4085
|
+
`Build the search UI for ${input.searchUiTarget}.`,
|
|
4086
|
+
...doc ? [
|
|
4087
|
+
"Follow the Algolia SDK reference below for client setup and search UI wiring; prefer it over prior knowledge:",
|
|
4088
|
+
doc
|
|
4089
|
+
] : [
|
|
4090
|
+
"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."
|
|
4091
|
+
],
|
|
4092
|
+
`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
4093
|
`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.`,
|
|
4094
|
+
"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
4095
|
// The key is provisioned only after verification passes, so the agent never
|
|
4035
4096
|
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
4036
4097
|
// decide whether a key already exists, and an agent-invented value there
|
|
@@ -4039,9 +4100,8 @@ function searchInstructions(input) {
|
|
|
4039
4100
|
// Not the agent's to rename: the wizard writes these exact names into
|
|
4040
4101
|
// ".env" right after this step, so a renamed prefix would leave the code
|
|
4041
4102
|
// 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.',
|
|
4103
|
+
`Use exactly these env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4104
|
+
"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
4105
|
"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
4106
|
];
|
|
4047
4107
|
}
|
|
@@ -4049,11 +4109,12 @@ function verificationInstructions(input) {
|
|
|
4049
4109
|
return [
|
|
4050
4110
|
"Verify the Algolia implementation changes in the current worktree.",
|
|
4051
4111
|
`Verification tools found in the codebase: ${JSON.stringify(input.findings.verification ?? [])}.`,
|
|
4052
|
-
"
|
|
4053
|
-
"
|
|
4054
|
-
"
|
|
4112
|
+
"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.",
|
|
4113
|
+
"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.",
|
|
4114
|
+
"For issues caused by the implementation, make minimal fixes with writeFile and re-run the checks.",
|
|
4115
|
+
"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
4116
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
4056
|
-
`Do not modify "${input.ingestDir}/" unless
|
|
4117
|
+
`Do not modify "${input.ingestDir}/" unless a check reports an actionable issue in its files.`,
|
|
4057
4118
|
"Always call reportStatus with status=success once verification has run, even when sufficient=false.",
|
|
4058
4119
|
"Set sufficient=true only when the implementation is complete and checks pass (or fail for a clearly unrelated reason).",
|
|
4059
4120
|
"Set sufficient=false when the implementation is incomplete or has implementation-caused failures; include concrete additionalInstructions for the next pass."
|
|
@@ -4074,14 +4135,15 @@ var IMPLEMENT_CONFIG = {
|
|
|
4074
4135
|
}
|
|
4075
4136
|
};
|
|
4076
4137
|
var useCaseToolMap = {
|
|
4077
|
-
ingestion: [
|
|
4078
|
-
search: [...FS_READ_TOOLS, "writeFile", "notifyUser"],
|
|
4079
|
-
verification: [
|
|
4138
|
+
ingestion: [
|
|
4080
4139
|
...FS_READ_TOOLS,
|
|
4081
4140
|
"writeFile",
|
|
4082
|
-
"
|
|
4141
|
+
"writeCredentials",
|
|
4142
|
+
"runShell",
|
|
4083
4143
|
"notifyUser"
|
|
4084
|
-
]
|
|
4144
|
+
],
|
|
4145
|
+
search: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"],
|
|
4146
|
+
verification: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"]
|
|
4085
4147
|
};
|
|
4086
4148
|
function toolsForUseCase(useCase, ingestionSource) {
|
|
4087
4149
|
const tools = useCaseToolMap[useCase];
|
|
@@ -4104,15 +4166,30 @@ function formatSummary(useCase, summary) {
|
|
|
4104
4166
|
const label = useCase === "ingestion" ? "Ingestion" : useCase === "search" ? "Search" : "Verification";
|
|
4105
4167
|
return `${label}: ${summary}`;
|
|
4106
4168
|
}
|
|
4107
|
-
function buildIngestCommand(worktree, runtime, entrypoint) {
|
|
4108
|
-
return `cd ${shellQuote(worktree)} && ${runtime} ${shellQuote(entrypoint)}`;
|
|
4109
|
-
}
|
|
4110
4169
|
function parseIngestRecordCount(output) {
|
|
4111
4170
|
const match = output.match(/ALGOLIA_WIZARD_RECORD_COUNT=(\d+)/);
|
|
4112
4171
|
if (!match) return void 0;
|
|
4113
4172
|
const count = Number(match[1]);
|
|
4114
4173
|
return Number.isFinite(count) ? count : void 0;
|
|
4115
4174
|
}
|
|
4175
|
+
function ingestOutcome(executions, ingestCommand) {
|
|
4176
|
+
const newestFirst = [...executions].reverse();
|
|
4177
|
+
const withCount = newestFirst.filter(
|
|
4178
|
+
(e) => parseIngestRecordCount(e.output ?? "") != null
|
|
4179
|
+
);
|
|
4180
|
+
const succeeded = newestFirst.filter((e) => e.approved && e.exitCode === 0);
|
|
4181
|
+
return {
|
|
4182
|
+
run: succeeded.find((e) => e.command === ingestCommand) ?? succeeded.find((e) => withCount.includes(e)),
|
|
4183
|
+
recordCount: parseIngestRecordCount(withCount[0]?.output ?? "")
|
|
4184
|
+
};
|
|
4185
|
+
}
|
|
4186
|
+
function makeToolContext(worktree, env = async () => ({})) {
|
|
4187
|
+
return createToolContext(
|
|
4188
|
+
DEFAULT_TOOL_LIMITS,
|
|
4189
|
+
worktree,
|
|
4190
|
+
createShellContext({ env, approve: storeApproval(worktree) })
|
|
4191
|
+
);
|
|
4192
|
+
}
|
|
4116
4193
|
function verificationRetryInstructions(verification) {
|
|
4117
4194
|
return [
|
|
4118
4195
|
`Implementation insufficient. Address these findings before reporting completion: ${verification.additionalInstructions ?? verification.summary}`
|
|
@@ -4191,9 +4268,13 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4191
4268
|
const confirmed2 = normalized.confirmedEntities;
|
|
4192
4269
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
4193
4270
|
let appId;
|
|
4271
|
+
let ingestAppId;
|
|
4194
4272
|
if (useCases.includes("search")) {
|
|
4195
4273
|
appId = (await requireApplication()).id;
|
|
4196
4274
|
}
|
|
4275
|
+
if (useCases.includes("ingestion")) {
|
|
4276
|
+
ingestAppId = appId ?? (await requireApplication()).id;
|
|
4277
|
+
}
|
|
4197
4278
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
4198
4279
|
try {
|
|
4199
4280
|
process.chdir(worktree);
|
|
@@ -4230,7 +4311,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4230
4311
|
ingestDir: INGEST_DIR,
|
|
4231
4312
|
ingestionSource,
|
|
4232
4313
|
uploadFilePath,
|
|
4233
|
-
|
|
4314
|
+
searchUiTarget: searchUiTarget(language)
|
|
4234
4315
|
};
|
|
4235
4316
|
const summaries = [];
|
|
4236
4317
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
@@ -4253,41 +4334,31 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4253
4334
|
}
|
|
4254
4335
|
let finalSearchEnvVars = input.searchEnvVars;
|
|
4255
4336
|
let agentRuns = 0;
|
|
4256
|
-
let
|
|
4257
|
-
let ingestEntrypoint;
|
|
4337
|
+
let ingestCommand;
|
|
4258
4338
|
let ingestScriptRan = false;
|
|
4259
4339
|
let ingestRecordCount;
|
|
4260
4340
|
let ingestDurationMs;
|
|
4261
|
-
let installFailed = false;
|
|
4262
4341
|
let ingestOutcomeMessage;
|
|
4342
|
+
const ingestKeyAppId = ingestAppId;
|
|
4343
|
+
const ingestionTools = ingestKeyAppId ? makeToolContext(worktree, async () => ({
|
|
4344
|
+
[APP_ID_VAR]: ingestKeyAppId,
|
|
4345
|
+
[API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
|
|
4346
|
+
[INDEX_NAME_VAR]: targetIndex
|
|
4347
|
+
})) : void 0;
|
|
4348
|
+
const searchTools = makeToolContext(worktree);
|
|
4263
4349
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4264
4350
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4265
4351
|
agentRuns += 1;
|
|
4266
|
-
|
|
4352
|
+
return runAgent({
|
|
4267
4353
|
instructions: buildAgentInstructions(
|
|
4268
4354
|
currentUseCase,
|
|
4269
4355
|
input,
|
|
4270
4356
|
extraInstructions
|
|
4271
4357
|
),
|
|
4272
4358
|
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4273
|
-
outputSchema: implementationOutputSchema
|
|
4274
|
-
|
|
4275
|
-
ctx.notify({
|
|
4276
|
-
messages: [`Installing dependencies for ${currentUseCase}\u2026`]
|
|
4359
|
+
outputSchema: implementationOutputSchema,
|
|
4360
|
+
toolContext: currentUseCase === "ingestion" ? ingestionTools ?? searchTools : searchTools
|
|
4277
4361
|
});
|
|
4278
|
-
const installLogId = ctx.logStart("installWorktreeDeps", {
|
|
4279
|
-
useCase: currentUseCase
|
|
4280
|
-
});
|
|
4281
|
-
const install = await installWorktreeDeps(worktree);
|
|
4282
|
-
ctx.logEnd(installLogId, install.ok ? "success" : "error");
|
|
4283
|
-
if (!install.ok) {
|
|
4284
|
-
installFailed = true;
|
|
4285
|
-
logger.warn(
|
|
4286
|
-
{ useCase: currentUseCase, output: install.output },
|
|
4287
|
-
"implement: dependency install in worktree failed; generated commands may not run until deps are installed"
|
|
4288
|
-
);
|
|
4289
|
-
}
|
|
4290
|
-
return result;
|
|
4291
4362
|
}
|
|
4292
4363
|
async function runVerificationUseCase() {
|
|
4293
4364
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
@@ -4295,111 +4366,55 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4295
4366
|
return runAgent({
|
|
4296
4367
|
instructions: buildAgentInstructions("verification", input),
|
|
4297
4368
|
tools: toolsForUseCase("verification"),
|
|
4298
|
-
outputSchema: verificationOutputSchema
|
|
4369
|
+
outputSchema: verificationOutputSchema,
|
|
4370
|
+
toolContext: searchTools
|
|
4299
4371
|
});
|
|
4300
4372
|
}
|
|
4301
4373
|
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
|
|
4374
|
+
const result = await runImplementationUseCase("ingestion");
|
|
4375
|
+
summaries.push(formatSummary("ingestion", result.summary));
|
|
4376
|
+
ingestCommand = result.ingestCommand;
|
|
4377
|
+
const executions = (ingestionTools ?? searchTools).shell.executions;
|
|
4378
|
+
const { run: ingestRun, recordCount } = ingestOutcome(
|
|
4379
|
+
executions,
|
|
4380
|
+
ingestCommand
|
|
4381
|
+
);
|
|
4382
|
+
ingestScriptRan = ingestRun != null;
|
|
4383
|
+
ingestRecordCount = recordCount;
|
|
4384
|
+
ingestDurationMs = ingestRun?.durationMs;
|
|
4385
|
+
if (ingestScriptRan) {
|
|
4386
|
+
ingestOutcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4387
|
+
if (ingestRecordCount != null) {
|
|
4388
|
+
track("AI Wizard Ingest Successful", {
|
|
4389
|
+
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4390
|
+
record_count: ingestRecordCount,
|
|
4391
|
+
duration_ms: ingestDurationMs ?? 0
|
|
4321
4392
|
});
|
|
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
4393
|
}
|
|
4394
|
+
} else {
|
|
4395
|
+
const rejected = executions.some((e) => !e.approved);
|
|
4396
|
+
const reason = rejected ? "you declined to run it" : "no successful run was recorded";
|
|
4397
|
+
ingestOutcomeMessage = `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.${ingestCommand ? " Run the command below when you are ready." : ""}`;
|
|
4398
|
+
summaries.push(`\u26A0\uFE0F The ingestion script did not run: ${reason}.`);
|
|
4399
|
+
logger.warn(
|
|
4400
|
+
{ ingestCommand, rejected, commandsRun: executions.length },
|
|
4401
|
+
"implement: ingestion script did not complete successfully"
|
|
4402
|
+
);
|
|
4403
|
+
track("Error", {
|
|
4404
|
+
step: "Push Data",
|
|
4405
|
+
error: `ingestion did not run: ${reason}`,
|
|
4406
|
+
product_area: "AI Wizard"
|
|
4407
|
+
});
|
|
4391
4408
|
}
|
|
4392
4409
|
const commandMessages = [`Open the worktree: cd ${shellQuote(worktree)}`];
|
|
4393
|
-
if (
|
|
4394
|
-
commandMessages.push(
|
|
4395
|
-
`Ingestion command: ${buildIngestCommand(worktree, ingestRuntime, ingestEntrypoint)}`
|
|
4396
|
-
);
|
|
4410
|
+
if (ingestCommand) {
|
|
4411
|
+
commandMessages.push(`Ingestion command: ${ingestCommand}`);
|
|
4397
4412
|
}
|
|
4398
4413
|
await ctx.requestUserInput({
|
|
4399
4414
|
prompt: "",
|
|
4400
4415
|
promptType: "enterToContinue",
|
|
4401
4416
|
options: [],
|
|
4402
|
-
messages:
|
|
4417
|
+
messages: [ingestOutcomeMessage, ...commandMessages]
|
|
4403
4418
|
});
|
|
4404
4419
|
}
|
|
4405
4420
|
if (useCases.includes("search")) {
|
|
@@ -4516,22 +4531,13 @@ ${run2.output}` : status;
|
|
|
4516
4531
|
"implement: agent reported success but no files changed in the worktree"
|
|
4517
4532
|
);
|
|
4518
4533
|
}
|
|
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
4534
|
return {
|
|
4525
4535
|
ingestionSource,
|
|
4526
4536
|
filesChanged,
|
|
4527
4537
|
summary: summaries.join("\n\n"),
|
|
4528
4538
|
worktreePath: worktree,
|
|
4529
|
-
...useCases.includes("ingestion") &&
|
|
4530
|
-
ingestCommand
|
|
4531
|
-
worktree,
|
|
4532
|
-
ingestRuntime,
|
|
4533
|
-
ingestEntrypoint
|
|
4534
|
-
),
|
|
4539
|
+
...useCases.includes("ingestion") && ingestCommand ? {
|
|
4540
|
+
ingestCommand,
|
|
4535
4541
|
ingestScriptRan,
|
|
4536
4542
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4537
4543
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
@@ -4859,7 +4865,7 @@ function parseCliArgs(argv) {
|
|
|
4859
4865
|
|
|
4860
4866
|
// src/lib/resetState.ts
|
|
4861
4867
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4862
|
-
import { join as
|
|
4868
|
+
import { join as join10 } from "node:path";
|
|
4863
4869
|
var KEEP = ["wizard.log"];
|
|
4864
4870
|
async function resetProjectState() {
|
|
4865
4871
|
const dir = stateDir();
|
|
@@ -4873,14 +4879,14 @@ async function resetProjectState() {
|
|
|
4873
4879
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4874
4880
|
await Promise.all(
|
|
4875
4881
|
targets.map(
|
|
4876
|
-
(name) => rm2(
|
|
4882
|
+
(name) => rm2(join10(dir, name), { recursive: true, force: true })
|
|
4877
4883
|
)
|
|
4878
4884
|
);
|
|
4879
4885
|
return { dir, removed: targets };
|
|
4880
4886
|
}
|
|
4881
4887
|
|
|
4882
4888
|
// src/main.tsx
|
|
4883
|
-
import { jsx as
|
|
4889
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4884
4890
|
async function startup() {
|
|
4885
4891
|
setProjectRoot(process.cwd());
|
|
4886
4892
|
let args;
|
|
@@ -4930,7 +4936,7 @@ ${formatStepList(workflow)}`);
|
|
|
4930
4936
|
}
|
|
4931
4937
|
async function run(workflow) {
|
|
4932
4938
|
const store = useWizard.getState();
|
|
4933
|
-
const instance = render(/* @__PURE__ */
|
|
4939
|
+
const instance = render(/* @__PURE__ */ jsx15(App, {}), { incrementalRendering: true });
|
|
4934
4940
|
await store.waitForStart();
|
|
4935
4941
|
let user = await getUser();
|
|
4936
4942
|
if (!user) {
|