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