@algolia/wizard 0.9.0-rc.87.86 → 0.9.0-rc.88.102
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,60 +13,10 @@ 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
|
}
|
|
64
19
|
var shell = process.platform === "win32";
|
|
65
|
-
function childEnv(withoutAdminKey) {
|
|
66
|
-
if (!withoutAdminKey) return void 0;
|
|
67
|
-
const { ALGOLIA_API_KEY: _adminKey, ...rest } = process.env;
|
|
68
|
-
return rest;
|
|
69
|
-
}
|
|
70
20
|
function lineSplitter(emit) {
|
|
71
21
|
let buffer = "";
|
|
72
22
|
return {
|
|
@@ -90,14 +40,11 @@ var stderrSink = (stream, line) => {
|
|
|
90
40
|
if (stream === "stdout") return;
|
|
91
41
|
wizardSink(stream, line);
|
|
92
42
|
};
|
|
93
|
-
function runAlgoliaCli(args, { onOutput
|
|
43
|
+
function runAlgoliaCli(args, { onOutput } = {}) {
|
|
94
44
|
const store = useWizard.getState();
|
|
95
45
|
const logId = store.logStart("tool", `algolia ${args.join(" ")}`);
|
|
96
46
|
return new Promise((resolve4, reject) => {
|
|
97
|
-
const child = spawn("npx", npxArgs(args), {
|
|
98
|
-
shell,
|
|
99
|
-
env: childEnv(withoutAdminKey)
|
|
100
|
-
});
|
|
47
|
+
const child = spawn("npx", npxArgs(args), { shell });
|
|
101
48
|
let stdout = "";
|
|
102
49
|
let stderr = "";
|
|
103
50
|
const splitters = {
|
|
@@ -142,7 +89,6 @@ function runAlgoliaCli(args, { onOutput, withoutAdminKey } = {}) {
|
|
|
142
89
|
},
|
|
143
90
|
(err) => {
|
|
144
91
|
useWizard.getState().logEnd(logId, "error");
|
|
145
|
-
logger.warn({ err, args }, "Algolia CLI command failed");
|
|
146
92
|
throw err;
|
|
147
93
|
}
|
|
148
94
|
);
|
|
@@ -200,6 +146,49 @@ function refreshAuthToken() {
|
|
|
200
146
|
return inFlightRefresh;
|
|
201
147
|
}
|
|
202
148
|
|
|
149
|
+
// src/lib/logger.ts
|
|
150
|
+
import pino from "pino";
|
|
151
|
+
import { join as join2, dirname } from "node:path";
|
|
152
|
+
import { devNull } from "node:os";
|
|
153
|
+
import { mkdirSync, openSync, closeSync } from "node:fs";
|
|
154
|
+
|
|
155
|
+
// src/core/constants.ts
|
|
156
|
+
import { homedir } from "node:os";
|
|
157
|
+
import { join, resolve } from "node:path";
|
|
158
|
+
function rootDir() {
|
|
159
|
+
return process.env.WIZARD_HOME ?? join(homedir(), ".algolia");
|
|
160
|
+
}
|
|
161
|
+
var pinnedRoot;
|
|
162
|
+
function setProjectRoot(cwd) {
|
|
163
|
+
pinnedRoot = resolve(cwd);
|
|
164
|
+
}
|
|
165
|
+
function projectSlug(cwd = pinnedRoot ?? process.cwd()) {
|
|
166
|
+
return resolve(cwd).replace(/[/\\:]+/g, "-").replace(/^-+/, "") || "root";
|
|
167
|
+
}
|
|
168
|
+
function stateDir(cwd = pinnedRoot ?? process.cwd()) {
|
|
169
|
+
return join(rootDir(), projectSlug(cwd));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/lib/logger.ts
|
|
173
|
+
var STDERR_FD = 2;
|
|
174
|
+
function resolveDest() {
|
|
175
|
+
const target = process.env.VITEST ? devNull : process.env.WIZARD_LOG ?? join2(stateDir(), "wizard.log");
|
|
176
|
+
try {
|
|
177
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
178
|
+
closeSync(openSync(target, "a"));
|
|
179
|
+
return target;
|
|
180
|
+
} catch {
|
|
181
|
+
return STDERR_FD;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function logDestination() {
|
|
185
|
+
return pino.destination({ dest: resolveDest(), sync: false });
|
|
186
|
+
}
|
|
187
|
+
var logger = pino(
|
|
188
|
+
{ level: process.env.LOG_LEVEL ?? "info" },
|
|
189
|
+
logDestination()
|
|
190
|
+
);
|
|
191
|
+
|
|
203
192
|
// src/lib/proxyFetch.ts
|
|
204
193
|
var PROXY_BASE_URL = process.env.PROXY_BASE_URL ?? "https://proxy-624203421261.us-east4.run.app";
|
|
205
194
|
var PROXY_AUTH_REJECTED_HEADER = "x-wizard-proxy-auth";
|
|
@@ -629,13 +618,10 @@ function Notices() {
|
|
|
629
618
|
}
|
|
630
619
|
|
|
631
620
|
// src/ui/PromptInput.tsx
|
|
632
|
-
import { Box as
|
|
621
|
+
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
633
622
|
import TextInput from "ink-text-input";
|
|
634
623
|
import { useState as useState5 } from "react";
|
|
635
624
|
|
|
636
|
-
// src/ui/CommandApproval.tsx
|
|
637
|
-
import { Box as Box5, Text as Text5, useInput } from "ink";
|
|
638
|
-
|
|
639
625
|
// src/ui/NextAction.tsx
|
|
640
626
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
641
627
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -659,69 +645,14 @@ function NextAction({
|
|
|
659
645
|
] });
|
|
660
646
|
}
|
|
661
647
|
|
|
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
648
|
// src/ui/SelectPrompt.tsx
|
|
718
|
-
import { Box as
|
|
649
|
+
import { Box as Box6, Text as Text6, useInput, useWindowSize as useWindowSize5 } from "ink";
|
|
719
650
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
720
651
|
|
|
721
652
|
// src/ui/ScrollView.tsx
|
|
722
|
-
import { Box as
|
|
653
|
+
import { Box as Box5, Text as Text5, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
|
|
723
654
|
import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
724
|
-
import { jsxs as
|
|
655
|
+
import { jsxs as jsxs4 } from "react/jsx-runtime";
|
|
725
656
|
var INDICATOR_ROWS = 2;
|
|
726
657
|
function fittedWidth(node, columns) {
|
|
727
658
|
let left = 0;
|
|
@@ -791,14 +722,14 @@ function useScrollWindow({
|
|
|
791
722
|
};
|
|
792
723
|
}
|
|
793
724
|
function ScrollView({ scroll, children }) {
|
|
794
|
-
return /* @__PURE__ */
|
|
795
|
-
scroll.hiddenAbove > 0 && /* @__PURE__ */
|
|
725
|
+
return /* @__PURE__ */ jsxs4(Box5, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
726
|
+
scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
796
727
|
"\u2191 ",
|
|
797
728
|
scroll.hiddenAbove,
|
|
798
729
|
" more"
|
|
799
730
|
] }),
|
|
800
731
|
children,
|
|
801
|
-
scroll.hiddenBelow > 0 && /* @__PURE__ */
|
|
732
|
+
scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
802
733
|
"\u2193 ",
|
|
803
734
|
scroll.hiddenBelow,
|
|
804
735
|
" more"
|
|
@@ -807,7 +738,7 @@ function ScrollView({ scroll, children }) {
|
|
|
807
738
|
}
|
|
808
739
|
|
|
809
740
|
// src/ui/SelectPrompt.tsx
|
|
810
|
-
import { jsx as
|
|
741
|
+
import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
811
742
|
var CANCEL = "cancel";
|
|
812
743
|
var ARROW_WIDTH = 4;
|
|
813
744
|
var COLUMN_GAP = 2;
|
|
@@ -869,7 +800,7 @@ function SelectPrompt({
|
|
|
869
800
|
revealIndex(index);
|
|
870
801
|
}, [index, revealIndex]);
|
|
871
802
|
const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
872
|
-
|
|
803
|
+
useInput((input, key) => {
|
|
873
804
|
if (rows.length === 0) return;
|
|
874
805
|
if (key.upArrow || input === "k") {
|
|
875
806
|
setIndex((i) => (i - 1 + rows.length) % rows.length);
|
|
@@ -892,56 +823,56 @@ function SelectPrompt({
|
|
|
892
823
|
}
|
|
893
824
|
}
|
|
894
825
|
});
|
|
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__ */
|
|
826
|
+
return /* @__PURE__ */ jsx4(Box6, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, width, children: [
|
|
827
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
|
|
828
|
+
error && /* @__PURE__ */ jsx4(Text6, { color: COLORS.danger, children: error }),
|
|
829
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
830
|
+
table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
|
|
831
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
832
|
+
question && /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: question }),
|
|
833
|
+
helpText && /* @__PURE__ */ jsx4(Text6, { color: COLORS.dim, children: helpText })
|
|
903
834
|
] })
|
|
904
835
|
] }),
|
|
905
|
-
/* @__PURE__ */
|
|
836
|
+
/* @__PURE__ */ jsx4(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
|
|
906
837
|
const i = scroll.offset + visibleIndex;
|
|
907
838
|
const highlighted = i === index;
|
|
908
839
|
const isCancel = i === cancelIndex;
|
|
909
840
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
910
841
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
911
842
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
912
|
-
const label = /* @__PURE__ */
|
|
843
|
+
const label = /* @__PURE__ */ jsxs5(Text6, { color: labelColor, wrap: "truncate", children: [
|
|
913
844
|
highlighted ? "\u276F " : " ",
|
|
914
845
|
bullet,
|
|
915
846
|
option
|
|
916
847
|
] });
|
|
917
848
|
const isText = sec?.kind === "text";
|
|
918
|
-
return /* @__PURE__ */
|
|
919
|
-
|
|
849
|
+
return /* @__PURE__ */ jsxs5(
|
|
850
|
+
Box6,
|
|
920
851
|
{
|
|
921
852
|
width: isText ? "100%" : barWidth,
|
|
922
853
|
paddingX: 1,
|
|
923
854
|
paddingY: 1,
|
|
924
855
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
925
856
|
children: [
|
|
926
|
-
/* @__PURE__ */
|
|
927
|
-
isText && textWidth > 0 && /* @__PURE__ */
|
|
928
|
-
|
|
857
|
+
/* @__PURE__ */ jsx4(Box6, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
858
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box6, { width: textWidth, children: /* @__PURE__ */ jsx4(
|
|
859
|
+
Text6,
|
|
929
860
|
{
|
|
930
861
|
wrap: "truncate",
|
|
931
862
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
932
863
|
children: sec.value
|
|
933
864
|
}
|
|
934
865
|
) }),
|
|
935
|
-
sec?.kind === "badge" && /* @__PURE__ */
|
|
866
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box6, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text6, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
936
867
|
]
|
|
937
868
|
},
|
|
938
869
|
`row-${i}`
|
|
939
870
|
);
|
|
940
871
|
}) }),
|
|
941
|
-
/* @__PURE__ */
|
|
872
|
+
/* @__PURE__ */ jsx4(Box6, { flexShrink: 0, children: /* @__PURE__ */ jsx4(Text6, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
942
873
|
i > 0 ? " " : "",
|
|
943
|
-
/* @__PURE__ */
|
|
944
|
-
/* @__PURE__ */
|
|
874
|
+
/* @__PURE__ */ jsx4(Text6, { color: COLORS.primary, children: key }),
|
|
875
|
+
/* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
945
876
|
" ",
|
|
946
877
|
label
|
|
947
878
|
] })
|
|
@@ -950,23 +881,23 @@ function SelectPrompt({
|
|
|
950
881
|
}
|
|
951
882
|
|
|
952
883
|
// src/ui/PromptInput.tsx
|
|
953
|
-
import { jsx as
|
|
884
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
954
885
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
955
886
|
function EnterToContinuePrompt({
|
|
956
887
|
question,
|
|
957
888
|
messages,
|
|
958
889
|
onDecide
|
|
959
890
|
}) {
|
|
960
|
-
|
|
891
|
+
useInput2((_input, key) => {
|
|
961
892
|
if (key.return) onDecide(true);
|
|
962
893
|
else if (key.escape) onDecide(false);
|
|
963
894
|
});
|
|
964
|
-
return /* @__PURE__ */
|
|
965
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
966
|
-
question && /* @__PURE__ */
|
|
967
|
-
/* @__PURE__ */
|
|
968
|
-
/* @__PURE__ */
|
|
969
|
-
/* @__PURE__ */
|
|
895
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, children: [
|
|
896
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
897
|
+
question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: question }),
|
|
898
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
|
|
899
|
+
/* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
|
|
900
|
+
/* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
970
901
|
] })
|
|
971
902
|
] });
|
|
972
903
|
}
|
|
@@ -974,11 +905,11 @@ function PromptInput() {
|
|
|
974
905
|
const { phase, inputReq, submitInput } = useWizard();
|
|
975
906
|
const [draft, setDraft] = useState5("");
|
|
976
907
|
if (phase === "done" || phase === "error") {
|
|
977
|
-
return /* @__PURE__ */
|
|
908
|
+
return /* @__PURE__ */ jsx5(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text7, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
978
909
|
}
|
|
979
910
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
980
911
|
if (inputReq.promptType === "multipleChoice") {
|
|
981
|
-
return /* @__PURE__ */
|
|
912
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
982
913
|
SelectPrompt,
|
|
983
914
|
{
|
|
984
915
|
question: inputReq.prompt,
|
|
@@ -995,7 +926,7 @@ function PromptInput() {
|
|
|
995
926
|
) });
|
|
996
927
|
}
|
|
997
928
|
if (inputReq.promptType === "multiSelect") {
|
|
998
|
-
return /* @__PURE__ */
|
|
929
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
999
930
|
SelectPrompt,
|
|
1000
931
|
{
|
|
1001
932
|
multi: true,
|
|
@@ -1010,7 +941,7 @@ function PromptInput() {
|
|
|
1010
941
|
) });
|
|
1011
942
|
}
|
|
1012
943
|
if (inputReq.promptType === "notice") {
|
|
1013
|
-
return /* @__PURE__ */
|
|
944
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
1014
945
|
SelectPrompt,
|
|
1015
946
|
{
|
|
1016
947
|
question: inputReq.prompt,
|
|
@@ -1021,7 +952,7 @@ function PromptInput() {
|
|
|
1021
952
|
) });
|
|
1022
953
|
}
|
|
1023
954
|
if (inputReq.promptType === "enterToContinue") {
|
|
1024
|
-
return /* @__PURE__ */
|
|
955
|
+
return /* @__PURE__ */ jsx5(
|
|
1025
956
|
EnterToContinuePrompt,
|
|
1026
957
|
{
|
|
1027
958
|
question: inputReq.prompt,
|
|
@@ -1030,12 +961,9 @@ function PromptInput() {
|
|
|
1030
961
|
}
|
|
1031
962
|
);
|
|
1032
963
|
}
|
|
1033
|
-
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1034
|
-
return /* @__PURE__ */ jsx6(CommandApproval, { command: inputReq.command, onDecide: submitInput });
|
|
1035
|
-
}
|
|
1036
964
|
if (inputReq.promptType === "acceptReject") {
|
|
1037
965
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
1038
|
-
return /* @__PURE__ */
|
|
966
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
1039
967
|
SelectPrompt,
|
|
1040
968
|
{
|
|
1041
969
|
question: inputReq.prompt,
|
|
@@ -1046,15 +974,15 @@ function PromptInput() {
|
|
|
1046
974
|
}
|
|
1047
975
|
) });
|
|
1048
976
|
}
|
|
1049
|
-
return /* @__PURE__ */
|
|
1050
|
-
inputReq.error && /* @__PURE__ */
|
|
1051
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1053
|
-
/* @__PURE__ */
|
|
977
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
978
|
+
inputReq.error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: inputReq.error }),
|
|
979
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
980
|
+
/* @__PURE__ */ jsxs6(Box7, { children: [
|
|
981
|
+
/* @__PURE__ */ jsxs6(Text7, { color: COLORS.primary, children: [
|
|
1054
982
|
inputReq.prompt,
|
|
1055
983
|
" "
|
|
1056
984
|
] }),
|
|
1057
|
-
/* @__PURE__ */
|
|
985
|
+
/* @__PURE__ */ jsx5(
|
|
1058
986
|
TextInput,
|
|
1059
987
|
{
|
|
1060
988
|
value: draft,
|
|
@@ -1072,7 +1000,7 @@ function PromptInput() {
|
|
|
1072
1000
|
// src/ui/Welcome.tsx
|
|
1073
1001
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1074
1002
|
import { fileURLToPath } from "node:url";
|
|
1075
|
-
import { Box as
|
|
1003
|
+
import { Box as Box8, Spacer, Text as Text8, useInput as useInput3, useWindowSize as useWindowSize6 } from "ink";
|
|
1076
1004
|
|
|
1077
1005
|
// src/ui/copy/welcome.ts
|
|
1078
1006
|
var sidebarItems = [
|
|
@@ -1085,12 +1013,12 @@ var sidebarItems = [
|
|
|
1085
1013
|
description: "push 100 records to Algolia in seconds"
|
|
1086
1014
|
},
|
|
1087
1015
|
{
|
|
1088
|
-
title: "detect your
|
|
1089
|
-
description: "
|
|
1016
|
+
title: "detect your framework",
|
|
1017
|
+
description: "React, Vue, Angular, Vanilla JS"
|
|
1090
1018
|
},
|
|
1091
1019
|
{
|
|
1092
1020
|
title: "scaffold a search UI",
|
|
1093
|
-
description: "a
|
|
1021
|
+
description: "a styled InstantSearch component, wired into your app"
|
|
1094
1022
|
},
|
|
1095
1023
|
{
|
|
1096
1024
|
title: "ship it",
|
|
@@ -1100,20 +1028,20 @@ var sidebarItems = [
|
|
|
1100
1028
|
|
|
1101
1029
|
// src/ui/Welcome.tsx
|
|
1102
1030
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
1103
|
-
import { jsx as
|
|
1031
|
+
import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1104
1032
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
1105
1033
|
function SidebarItem({
|
|
1106
1034
|
title,
|
|
1107
1035
|
description
|
|
1108
1036
|
}) {
|
|
1109
|
-
return /* @__PURE__ */
|
|
1110
|
-
/* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
/* @__PURE__ */
|
|
1037
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1038
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, children: [
|
|
1039
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.success, children: "\u2192" }),
|
|
1040
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.strong, bold: true, children: title })
|
|
1113
1041
|
] }),
|
|
1114
|
-
/* @__PURE__ */
|
|
1115
|
-
/* @__PURE__ */
|
|
1116
|
-
/* @__PURE__ */
|
|
1042
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 2, children: [
|
|
1043
|
+
/* @__PURE__ */ jsx6(Spacer, {}),
|
|
1044
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: description })
|
|
1117
1045
|
] })
|
|
1118
1046
|
] });
|
|
1119
1047
|
}
|
|
@@ -1121,7 +1049,7 @@ function Welcome() {
|
|
|
1121
1049
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
1122
1050
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
1123
1051
|
const { rows } = useWindowSize6();
|
|
1124
|
-
|
|
1052
|
+
useInput3((input, key) => {
|
|
1125
1053
|
if (key.return) confirmStart();
|
|
1126
1054
|
else if (input === "i") openLearnMore();
|
|
1127
1055
|
});
|
|
@@ -1139,16 +1067,16 @@ function Welcome() {
|
|
|
1139
1067
|
if (rows < 30) {
|
|
1140
1068
|
layout = scales["small"];
|
|
1141
1069
|
}
|
|
1142
|
-
return /* @__PURE__ */
|
|
1143
|
-
/* @__PURE__ */
|
|
1144
|
-
|
|
1070
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
1071
|
+
/* @__PURE__ */ jsx6(
|
|
1072
|
+
Box8,
|
|
1145
1073
|
{
|
|
1146
1074
|
paddingY: layout.main.padding.y,
|
|
1147
1075
|
paddingX: layout.main.padding.x,
|
|
1148
1076
|
flexDirection: "column",
|
|
1149
1077
|
justifyContent: "center",
|
|
1150
|
-
children: /* @__PURE__ */
|
|
1151
|
-
/* @__PURE__ */
|
|
1078
|
+
children: /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 2, children: [
|
|
1079
|
+
/* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
|
|
1152
1080
|
Image,
|
|
1153
1081
|
{
|
|
1154
1082
|
src: IMAGE_PATH,
|
|
@@ -1159,16 +1087,16 @@ function Welcome() {
|
|
|
1159
1087
|
protocol: "halfBlock"
|
|
1160
1088
|
}
|
|
1161
1089
|
) }),
|
|
1162
|
-
/* @__PURE__ */
|
|
1163
|
-
/* @__PURE__ */
|
|
1164
|
-
/* @__PURE__ */
|
|
1165
|
-
/* @__PURE__ */
|
|
1090
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1091
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
|
|
1092
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
1093
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
1166
1094
|
] })
|
|
1167
1095
|
] })
|
|
1168
1096
|
}
|
|
1169
1097
|
),
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
|
|
1098
|
+
/* @__PURE__ */ jsxs7(
|
|
1099
|
+
Box8,
|
|
1172
1100
|
{
|
|
1173
1101
|
backgroundColor: COLORS.bg.sidebar,
|
|
1174
1102
|
width: 40,
|
|
@@ -1178,8 +1106,8 @@ function Welcome() {
|
|
|
1178
1106
|
flexDirection: "column",
|
|
1179
1107
|
justifyContent: "center",
|
|
1180
1108
|
children: [
|
|
1181
|
-
/* @__PURE__ */
|
|
1182
|
-
sidebarItems.map((i, idx) => /* @__PURE__ */
|
|
1109
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
1110
|
+
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
1183
1111
|
]
|
|
1184
1112
|
}
|
|
1185
1113
|
)
|
|
@@ -1188,7 +1116,7 @@ function Welcome() {
|
|
|
1188
1116
|
|
|
1189
1117
|
// src/ui/LearnMore.tsx
|
|
1190
1118
|
import { Fragment as Fragment2 } from "react";
|
|
1191
|
-
import { Box as
|
|
1119
|
+
import { Box as Box9, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize7 } from "ink";
|
|
1192
1120
|
|
|
1193
1121
|
// src/ui/copy/learn-more.ts
|
|
1194
1122
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -1196,17 +1124,12 @@ var accessItems = [
|
|
|
1196
1124
|
{
|
|
1197
1125
|
tag: "READ",
|
|
1198
1126
|
title: "Project files",
|
|
1199
|
-
description: "reads
|
|
1127
|
+
description: "reads package.json, configs & source to detect your stack. Read-only; nothing is uploaded."
|
|
1200
1128
|
},
|
|
1201
1129
|
{
|
|
1202
1130
|
tag: "WRITE",
|
|
1203
1131
|
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."
|
|
1132
|
+
description: "creates & edits files (search UI, config). Shown as a diff first \u2014 nothing lands without your approval."
|
|
1210
1133
|
},
|
|
1211
1134
|
{
|
|
1212
1135
|
tag: "NET",
|
|
@@ -1216,13 +1139,13 @@ var accessItems = [
|
|
|
1216
1139
|
{
|
|
1217
1140
|
tag: "KEY",
|
|
1218
1141
|
title: "Credentials",
|
|
1219
|
-
description: "
|
|
1142
|
+
description: "saves your Admin API key to .env and adds it to .gitignore."
|
|
1220
1143
|
}
|
|
1221
1144
|
];
|
|
1222
1145
|
var neverItems = [
|
|
1223
1146
|
"Send your source code to a model or third party",
|
|
1224
1147
|
"Commit or push to git",
|
|
1225
|
-
"
|
|
1148
|
+
"Touch files outside your project directory"
|
|
1226
1149
|
];
|
|
1227
1150
|
var policyLinks = [
|
|
1228
1151
|
{ label: "Terms", url: "https://www.algolia.com/policies/terms" },
|
|
@@ -1230,11 +1153,10 @@ var policyLinks = [
|
|
|
1230
1153
|
];
|
|
1231
1154
|
|
|
1232
1155
|
// src/ui/LearnMore.tsx
|
|
1233
|
-
import { jsx as
|
|
1156
|
+
import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1234
1157
|
var TAG_COLORS = {
|
|
1235
1158
|
READ: COLORS.success,
|
|
1236
1159
|
WRITE: COLORS.badge,
|
|
1237
|
-
EXEC: COLORS.danger,
|
|
1238
1160
|
NET: COLORS.accent,
|
|
1239
1161
|
KEY: COLORS.muted
|
|
1240
1162
|
};
|
|
@@ -1247,12 +1169,12 @@ function NeverLine({
|
|
|
1247
1169
|
}) {
|
|
1248
1170
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
1249
1171
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
1250
|
-
return /* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1172
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1173
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" }),
|
|
1252
1174
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
1253
|
-
segments.map((s, i) => /* @__PURE__ */
|
|
1175
|
+
segments.map((s, i) => /* @__PURE__ */ jsx7(Text9, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
1254
1176
|
" ".repeat(rightPad),
|
|
1255
|
-
/* @__PURE__ */
|
|
1177
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" })
|
|
1256
1178
|
] });
|
|
1257
1179
|
}
|
|
1258
1180
|
function LearnMore() {
|
|
@@ -1260,12 +1182,12 @@ function LearnMore() {
|
|
|
1260
1182
|
const backToHome = useWizard((s) => s.backToHome);
|
|
1261
1183
|
const { columns } = useWindowSize7();
|
|
1262
1184
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
1263
|
-
|
|
1185
|
+
useInput4((_input, key) => {
|
|
1264
1186
|
if (key.escape) backToHome();
|
|
1265
1187
|
else if (key.return) confirmStart();
|
|
1266
1188
|
});
|
|
1267
|
-
return /* @__PURE__ */
|
|
1268
|
-
|
|
1189
|
+
return /* @__PURE__ */ jsxs8(
|
|
1190
|
+
Box9,
|
|
1269
1191
|
{
|
|
1270
1192
|
flexDirection: "column",
|
|
1271
1193
|
paddingX: PADDING_X,
|
|
@@ -1273,31 +1195,31 @@ function LearnMore() {
|
|
|
1273
1195
|
width: "100%",
|
|
1274
1196
|
gap: 1,
|
|
1275
1197
|
children: [
|
|
1276
|
-
/* @__PURE__ */
|
|
1277
|
-
/* @__PURE__ */
|
|
1278
|
-
/* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1280
|
-
/* @__PURE__ */
|
|
1281
|
-
/* @__PURE__ */
|
|
1282
|
-
/* @__PURE__ */
|
|
1283
|
-
/* @__PURE__ */
|
|
1284
|
-
/* @__PURE__ */
|
|
1198
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1199
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: accessIntro }),
|
|
1200
|
+
/* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, children: [
|
|
1201
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1202
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1203
|
+
/* @__PURE__ */ jsx7(Box9, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text9, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1204
|
+
/* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1205
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1206
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
1285
1207
|
] }) })
|
|
1286
1208
|
] })
|
|
1287
1209
|
] }, item.tag)) }),
|
|
1288
|
-
/* @__PURE__ */
|
|
1289
|
-
/* @__PURE__ */
|
|
1290
|
-
/* @__PURE__ */
|
|
1291
|
-
/* @__PURE__ */
|
|
1210
|
+
/* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
1211
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
1212
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1213
|
+
/* @__PURE__ */ jsx7(
|
|
1292
1214
|
NeverLine,
|
|
1293
1215
|
{
|
|
1294
1216
|
width: dividerWidth,
|
|
1295
1217
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1296
1218
|
}
|
|
1297
1219
|
),
|
|
1298
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1299
|
-
/* @__PURE__ */
|
|
1300
|
-
/* @__PURE__ */
|
|
1220
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1221
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1222
|
+
/* @__PURE__ */ jsx7(
|
|
1301
1223
|
NeverLine,
|
|
1302
1224
|
{
|
|
1303
1225
|
width: dividerWidth,
|
|
@@ -1309,24 +1231,24 @@ function LearnMore() {
|
|
|
1309
1231
|
}
|
|
1310
1232
|
)
|
|
1311
1233
|
] }, item)),
|
|
1312
|
-
/* @__PURE__ */
|
|
1313
|
-
/* @__PURE__ */
|
|
1234
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1235
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1314
1236
|
] }),
|
|
1315
|
-
/* @__PURE__ */
|
|
1316
|
-
/* @__PURE__ */
|
|
1317
|
-
/* @__PURE__ */
|
|
1237
|
+
/* @__PURE__ */ jsx7(Box9, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
|
|
1238
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1239
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.accent, children: link.url })
|
|
1318
1240
|
] }, link.label)) }),
|
|
1319
|
-
/* @__PURE__ */
|
|
1320
|
-
/* @__PURE__ */
|
|
1321
|
-
/* @__PURE__ */
|
|
1322
|
-
/* @__PURE__ */
|
|
1323
|
-
/* @__PURE__ */
|
|
1241
|
+
/* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1242
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
|
|
1243
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
|
|
1244
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "esc" }),
|
|
1245
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "] back" })
|
|
1324
1246
|
] }),
|
|
1325
|
-
/* @__PURE__ */
|
|
1326
|
-
/* @__PURE__ */
|
|
1327
|
-
/* @__PURE__ */
|
|
1328
|
-
/* @__PURE__ */
|
|
1329
|
-
/* @__PURE__ */
|
|
1247
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
|
|
1248
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
|
|
1249
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "enter" }),
|
|
1250
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "]" }),
|
|
1251
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1330
1252
|
] })
|
|
1331
1253
|
] })
|
|
1332
1254
|
]
|
|
@@ -1335,10 +1257,10 @@ function LearnMore() {
|
|
|
1335
1257
|
}
|
|
1336
1258
|
|
|
1337
1259
|
// src/ui/Sidebar.tsx
|
|
1338
|
-
import { Box as
|
|
1260
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1339
1261
|
|
|
1340
1262
|
// src/ui/Steps.tsx
|
|
1341
|
-
import { Box as
|
|
1263
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1342
1264
|
import Spinner from "ink-spinner";
|
|
1343
1265
|
|
|
1344
1266
|
// src/core/persistence.ts
|
|
@@ -1367,12 +1289,12 @@ async function clearWorkflowState(workflowId) {
|
|
|
1367
1289
|
}
|
|
1368
1290
|
|
|
1369
1291
|
// src/ui/Steps.tsx
|
|
1370
|
-
import { jsx as
|
|
1292
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1371
1293
|
function Steps() {
|
|
1372
1294
|
const { steps } = useWizard();
|
|
1373
1295
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1374
|
-
return /* @__PURE__ */
|
|
1375
|
-
s.status === "running" ? /* @__PURE__ */
|
|
1296
|
+
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: [
|
|
1297
|
+
s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1376
1298
|
" ",
|
|
1377
1299
|
s.title
|
|
1378
1300
|
] }) }, s.id)) });
|
|
@@ -1381,27 +1303,27 @@ function CurrentStep() {
|
|
|
1381
1303
|
const { steps } = useWizard();
|
|
1382
1304
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1383
1305
|
if (!currentStep) return null;
|
|
1384
|
-
return /* @__PURE__ */
|
|
1385
|
-
/* @__PURE__ */
|
|
1306
|
+
return /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status.running, children: [
|
|
1307
|
+
/* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
|
|
1386
1308
|
" ",
|
|
1387
1309
|
` ${currentStep.title}`
|
|
1388
1310
|
] });
|
|
1389
1311
|
}
|
|
1390
1312
|
|
|
1391
1313
|
// src/ui/Progress.tsx
|
|
1392
|
-
import { Box as
|
|
1393
|
-
import { jsx as
|
|
1314
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1315
|
+
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1394
1316
|
function Progress() {
|
|
1395
1317
|
const { steps, currentStepIndex } = useWizard();
|
|
1396
1318
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1397
1319
|
if (visibleSteps.length === 0) return null;
|
|
1398
1320
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1399
1321
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1400
|
-
return /* @__PURE__ */
|
|
1401
|
-
/* @__PURE__ */
|
|
1402
|
-
/* @__PURE__ */
|
|
1403
|
-
/* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1322
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1323
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "STEP" }),
|
|
1324
|
+
/* @__PURE__ */ jsx9(Text11, { bold: true, children: activeStepNumber }),
|
|
1325
|
+
/* @__PURE__ */ jsx9(Text11, { bold: true, children: "/" }),
|
|
1326
|
+
/* @__PURE__ */ jsx9(Text11, { bold: true, children: visibleSteps.length })
|
|
1405
1327
|
] });
|
|
1406
1328
|
}
|
|
1407
1329
|
|
|
@@ -1412,10 +1334,10 @@ var sidebarCommands = [
|
|
|
1412
1334
|
];
|
|
1413
1335
|
|
|
1414
1336
|
// src/ui/Sidebar.tsx
|
|
1415
|
-
import { jsx as
|
|
1337
|
+
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1416
1338
|
function Sidebar() {
|
|
1417
|
-
return /* @__PURE__ */
|
|
1418
|
-
|
|
1339
|
+
return /* @__PURE__ */ jsxs11(
|
|
1340
|
+
Box12,
|
|
1419
1341
|
{
|
|
1420
1342
|
backgroundColor: "#14171E",
|
|
1421
1343
|
width: 30,
|
|
@@ -1424,16 +1346,16 @@ function Sidebar() {
|
|
|
1424
1346
|
flexDirection: "column",
|
|
1425
1347
|
justifyContent: "space-between",
|
|
1426
1348
|
children: [
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
/* @__PURE__ */
|
|
1429
|
-
/* @__PURE__ */
|
|
1349
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
|
|
1350
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1351
|
+
/* @__PURE__ */ jsx10(Steps, {})
|
|
1430
1352
|
] }),
|
|
1431
|
-
/* @__PURE__ */
|
|
1432
|
-
/* @__PURE__ */
|
|
1433
|
-
/* @__PURE__ */
|
|
1434
|
-
return /* @__PURE__ */
|
|
1435
|
-
/* @__PURE__ */
|
|
1436
|
-
/* @__PURE__ */
|
|
1353
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
|
|
1354
|
+
/* @__PURE__ */ jsx10(Progress, {}),
|
|
1355
|
+
/* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1356
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1357
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1358
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: c.description })
|
|
1437
1359
|
] });
|
|
1438
1360
|
}) })
|
|
1439
1361
|
] })
|
|
@@ -1443,12 +1365,12 @@ function Sidebar() {
|
|
|
1443
1365
|
}
|
|
1444
1366
|
|
|
1445
1367
|
// src/ui/Ribbon.tsx
|
|
1446
|
-
import { Box as
|
|
1447
|
-
import { jsx as
|
|
1368
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1369
|
+
import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1448
1370
|
function Ribbon() {
|
|
1449
1371
|
const firstCommand = sidebarCommands[0];
|
|
1450
|
-
return /* @__PURE__ */
|
|
1451
|
-
|
|
1372
|
+
return /* @__PURE__ */ jsxs12(
|
|
1373
|
+
Box13,
|
|
1452
1374
|
{
|
|
1453
1375
|
backgroundColor: "#14171E",
|
|
1454
1376
|
flexDirection: "row",
|
|
@@ -1456,11 +1378,11 @@ function Ribbon() {
|
|
|
1456
1378
|
paddingX: 2,
|
|
1457
1379
|
paddingY: 1,
|
|
1458
1380
|
children: [
|
|
1459
|
-
/* @__PURE__ */
|
|
1460
|
-
/* @__PURE__ */
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
/* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
1381
|
+
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1382
|
+
/* @__PURE__ */ jsx11(CurrentStep, {}),
|
|
1383
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
|
|
1384
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1385
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: firstCommand.description })
|
|
1464
1386
|
] })
|
|
1465
1387
|
]
|
|
1466
1388
|
}
|
|
@@ -1471,8 +1393,8 @@ function Ribbon() {
|
|
|
1471
1393
|
import { useState as useState6 } from "react";
|
|
1472
1394
|
|
|
1473
1395
|
// src/ui/Logs.tsx
|
|
1474
|
-
import { Box as
|
|
1475
|
-
import { jsx as
|
|
1396
|
+
import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
|
|
1397
|
+
import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1476
1398
|
var KIND_COLOR = {
|
|
1477
1399
|
tool: COLORS.primary,
|
|
1478
1400
|
prompt: COLORS.badge
|
|
@@ -1503,14 +1425,14 @@ function formatTimestamp(ms) {
|
|
|
1503
1425
|
function Logs() {
|
|
1504
1426
|
const logs = useWizard((s) => s.logs);
|
|
1505
1427
|
const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
|
|
1506
|
-
|
|
1428
|
+
useInput5((_input, key) => {
|
|
1507
1429
|
if (key.upArrow) scroll.scrollBy(-1);
|
|
1508
1430
|
else if (key.downArrow) scroll.scrollBy(1);
|
|
1509
1431
|
});
|
|
1510
1432
|
const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
1511
|
-
return /* @__PURE__ */
|
|
1512
|
-
logs.length === 0 && /* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1433
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1434
|
+
logs.length === 0 && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "No logs yet." }),
|
|
1435
|
+
/* @__PURE__ */ jsx12(ScrollView, { scroll, children: visible.map((entry) => {
|
|
1514
1436
|
const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
|
|
1515
1437
|
const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
|
|
1516
1438
|
const rawPreview = rawInputText(entry.input);
|
|
@@ -1520,14 +1442,14 @@ function Logs() {
|
|
|
1520
1442
|
const name = truncate2(entry.name, budget);
|
|
1521
1443
|
budget -= name.length;
|
|
1522
1444
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1523
|
-
return /* @__PURE__ */
|
|
1524
|
-
/* @__PURE__ */
|
|
1525
|
-
/* @__PURE__ */
|
|
1526
|
-
preview && /* @__PURE__ */
|
|
1527
|
-
durationText && /* @__PURE__ */
|
|
1445
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1446
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: timestamp }),
|
|
1447
|
+
/* @__PURE__ */ jsx12(Text14, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1448
|
+
preview && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1449
|
+
durationText && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: durationText })
|
|
1528
1450
|
] }, entry.id);
|
|
1529
1451
|
}) }),
|
|
1530
|
-
/* @__PURE__ */
|
|
1452
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1531
1453
|
] });
|
|
1532
1454
|
}
|
|
1533
1455
|
|
|
@@ -1719,7 +1641,7 @@ function track(event, payload) {
|
|
|
1719
1641
|
}
|
|
1720
1642
|
|
|
1721
1643
|
// src/ui/App.tsx
|
|
1722
|
-
import { jsx as
|
|
1644
|
+
import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1723
1645
|
function App() {
|
|
1724
1646
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1725
1647
|
const { exit } = useApp();
|
|
@@ -1727,7 +1649,7 @@ function App() {
|
|
|
1727
1649
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1728
1650
|
const finished = phase === "done" || phase === "error";
|
|
1729
1651
|
const currentStep = steps[currentStepIndex];
|
|
1730
|
-
|
|
1652
|
+
useInput6(
|
|
1731
1653
|
(_input, key) => {
|
|
1732
1654
|
if (key.return) {
|
|
1733
1655
|
exit();
|
|
@@ -1735,7 +1657,7 @@ function App() {
|
|
|
1735
1657
|
},
|
|
1736
1658
|
{ isActive: finished }
|
|
1737
1659
|
);
|
|
1738
|
-
|
|
1660
|
+
useInput6((_input, key) => {
|
|
1739
1661
|
if (phase === "idle" || phase === "authenticating") return;
|
|
1740
1662
|
if (key.tab) {
|
|
1741
1663
|
setShowLogs(!showLogs);
|
|
@@ -1746,8 +1668,8 @@ function App() {
|
|
|
1746
1668
|
});
|
|
1747
1669
|
}
|
|
1748
1670
|
});
|
|
1749
|
-
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" &&
|
|
1750
|
-
|
|
1671
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1672
|
+
useInput6((_input, key) => {
|
|
1751
1673
|
if (escOwnedElsewhere) return;
|
|
1752
1674
|
if (key.escape) {
|
|
1753
1675
|
track("AI Wizard Interaction", {
|
|
@@ -1766,8 +1688,8 @@ function App() {
|
|
|
1766
1688
|
/* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
|
|
1767
1689
|
the whole screen, and the scrolling throws off its cursor arithmetic —
|
|
1768
1690
|
flicker and leftover rows. */
|
|
1769
|
-
/* @__PURE__ */
|
|
1770
|
-
|
|
1691
|
+
/* @__PURE__ */ jsxs14(
|
|
1692
|
+
Box15,
|
|
1771
1693
|
{
|
|
1772
1694
|
backgroundColor: COLORS.bg.main,
|
|
1773
1695
|
flexDirection: "row",
|
|
@@ -1775,16 +1697,16 @@ function App() {
|
|
|
1775
1697
|
height: scrollsPastViewport ? void 0 : rows,
|
|
1776
1698
|
overflow: scrollsPastViewport ? "visible" : "hidden",
|
|
1777
1699
|
children: [
|
|
1778
|
-
mainWindowVisible && /* @__PURE__ */
|
|
1779
|
-
|
|
1700
|
+
mainWindowVisible && /* @__PURE__ */ jsxs14(
|
|
1701
|
+
Box15,
|
|
1780
1702
|
{
|
|
1781
1703
|
flexDirection,
|
|
1782
1704
|
width: "100%",
|
|
1783
1705
|
maxHeight: rows,
|
|
1784
1706
|
justifyContent: "space-between",
|
|
1785
1707
|
children: [
|
|
1786
|
-
showLogs ? /* @__PURE__ */
|
|
1787
|
-
|
|
1708
|
+
showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : /* @__PURE__ */ jsxs14(
|
|
1709
|
+
Box15,
|
|
1788
1710
|
{
|
|
1789
1711
|
flexDirection: "column",
|
|
1790
1712
|
paddingX: 4,
|
|
@@ -1792,26 +1714,26 @@ function App() {
|
|
|
1792
1714
|
width: showSidebar ? 70 : "100%",
|
|
1793
1715
|
flexGrow: 1,
|
|
1794
1716
|
children: [
|
|
1795
|
-
phase === "authenticating" && /* @__PURE__ */
|
|
1796
|
-
/* @__PURE__ */
|
|
1797
|
-
/* @__PURE__ */
|
|
1717
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginBottom: 1, children: [
|
|
1718
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1719
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1798
1720
|
] }),
|
|
1799
|
-
/* @__PURE__ */
|
|
1800
|
-
/* @__PURE__ */
|
|
1801
|
-
/* @__PURE__ */
|
|
1802
|
-
phase === "running" && showSidebar && /* @__PURE__ */
|
|
1803
|
-
phase === "error" && error && /* @__PURE__ */
|
|
1721
|
+
/* @__PURE__ */ jsx13(CliOutput, {}),
|
|
1722
|
+
/* @__PURE__ */ jsx13(Notices, {}),
|
|
1723
|
+
/* @__PURE__ */ jsx13(PromptInput, {}),
|
|
1724
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
|
|
1725
|
+
phase === "error" && error && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { color: COLORS.status.error, children: [
|
|
1804
1726
|
"\u2716 ",
|
|
1805
1727
|
error
|
|
1806
1728
|
] }) })
|
|
1807
1729
|
]
|
|
1808
1730
|
}
|
|
1809
1731
|
),
|
|
1810
|
-
showSidebar ? /* @__PURE__ */
|
|
1732
|
+
showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
|
|
1811
1733
|
]
|
|
1812
1734
|
}
|
|
1813
1735
|
),
|
|
1814
|
-
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */
|
|
1736
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
|
|
1815
1737
|
]
|
|
1816
1738
|
}
|
|
1817
1739
|
)
|
|
@@ -1891,7 +1813,7 @@ async function ensureConsent() {
|
|
|
1891
1813
|
if (config.aiConsent) return;
|
|
1892
1814
|
const store = useWizard.getState();
|
|
1893
1815
|
const answer = await store.requestUserInput({
|
|
1894
|
-
prompt: "Wizard will make AI-authored changes to this repository
|
|
1816
|
+
prompt: "Wizard will make AI-authored changes to this repository.",
|
|
1895
1817
|
promptType: "enterToContinue",
|
|
1896
1818
|
options: []
|
|
1897
1819
|
});
|
|
@@ -2173,7 +2095,7 @@ async function ensureApplication() {
|
|
|
2173
2095
|
}
|
|
2174
2096
|
|
|
2175
2097
|
// src/workflows/default.ts
|
|
2176
|
-
import { z as
|
|
2098
|
+
import { z as z28 } from "zod";
|
|
2177
2099
|
|
|
2178
2100
|
// src/actions/listIndices.ts
|
|
2179
2101
|
import { z as z5 } from "zod";
|
|
@@ -2433,22 +2355,48 @@ function writeFileTool(ctx) {
|
|
|
2433
2355
|
|
|
2434
2356
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2435
2357
|
import { tool as tool6 } from "ai";
|
|
2436
|
-
import
|
|
2358
|
+
import z13 from "zod";
|
|
2437
2359
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2438
2360
|
import { dirname as dirname5 } from "node:path";
|
|
2439
2361
|
|
|
2440
2362
|
// src/lib/algoliaApiKey.ts
|
|
2441
|
-
import { z as
|
|
2363
|
+
import { z as z12 } from "zod";
|
|
2442
2364
|
|
|
2443
2365
|
// src/lib/keychain.ts
|
|
2444
|
-
import { getPassword, setPassword } from "cross-keychain";
|
|
2366
|
+
import { deletePassword, getPassword, setPassword } from "cross-keychain";
|
|
2367
|
+
import { z as z11 } from "zod";
|
|
2445
2368
|
var SERVICE = "algolia-wizard";
|
|
2446
|
-
|
|
2369
|
+
var ACCOUNT = "api-keys";
|
|
2370
|
+
var storedKeysSchema = z11.record(z11.string(), z11.string());
|
|
2371
|
+
function entryId(kind, index, appId) {
|
|
2447
2372
|
return `${kind}:${appId}:${index}`;
|
|
2448
2373
|
}
|
|
2374
|
+
async function loadKeys() {
|
|
2375
|
+
const raw = await getPassword(SERVICE, ACCOUNT);
|
|
2376
|
+
if (!raw) return {};
|
|
2377
|
+
let payload;
|
|
2378
|
+
try {
|
|
2379
|
+
payload = JSON.parse(raw);
|
|
2380
|
+
} catch {
|
|
2381
|
+
payload = null;
|
|
2382
|
+
}
|
|
2383
|
+
const keys = storedKeysSchema.safeParse(payload);
|
|
2384
|
+
if (!keys.success) {
|
|
2385
|
+
logger.warn("the stored API keys are unreadable; treating them as empty");
|
|
2386
|
+
return {};
|
|
2387
|
+
}
|
|
2388
|
+
return keys.data;
|
|
2389
|
+
}
|
|
2390
|
+
var queue = Promise.resolve();
|
|
2391
|
+
function serialized(op) {
|
|
2392
|
+
const next = queue.then(op);
|
|
2393
|
+
queue = next.catch(() => {
|
|
2394
|
+
});
|
|
2395
|
+
return next;
|
|
2396
|
+
}
|
|
2449
2397
|
async function readStoredKey(kind, index, appId) {
|
|
2450
2398
|
try {
|
|
2451
|
-
return await
|
|
2399
|
+
return (await loadKeys())[entryId(kind, index, appId)] ?? null;
|
|
2452
2400
|
} catch (err) {
|
|
2453
2401
|
logger.warn(
|
|
2454
2402
|
{ err: err.message, kind, index, appId },
|
|
@@ -2457,19 +2405,40 @@ async function readStoredKey(kind, index, appId) {
|
|
|
2457
2405
|
return null;
|
|
2458
2406
|
}
|
|
2459
2407
|
}
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2408
|
+
function storeKey(kind, index, appId, value) {
|
|
2409
|
+
return serialized(async () => {
|
|
2410
|
+
const id = entryId(kind, index, appId);
|
|
2411
|
+
try {
|
|
2412
|
+
const keys = await loadKeys();
|
|
2413
|
+
await setPassword(
|
|
2414
|
+
SERVICE,
|
|
2415
|
+
ACCOUNT,
|
|
2416
|
+
JSON.stringify({ ...keys, [id]: value })
|
|
2417
|
+
);
|
|
2418
|
+
if ((await loadKeys())[id] !== value) {
|
|
2419
|
+
throw new Error("the keychain did not store the value");
|
|
2420
|
+
}
|
|
2421
|
+
} catch (err) {
|
|
2422
|
+
logger.warn(
|
|
2423
|
+
{ err: err.message, kind, index, appId },
|
|
2424
|
+
"could not store the API key in the keychain; the next run will create another"
|
|
2425
|
+
);
|
|
2466
2426
|
}
|
|
2467
|
-
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
function deleteStoredKeys() {
|
|
2430
|
+
return serialized(async () => {
|
|
2431
|
+
try {
|
|
2432
|
+
await deletePassword(SERVICE, ACCOUNT);
|
|
2433
|
+
} catch (err) {
|
|
2434
|
+
const message = err.message;
|
|
2435
|
+
if (/not found/i.test(message)) return;
|
|
2436
|
+
logger.warn(
|
|
2437
|
+
{ err: message },
|
|
2438
|
+
"could not delete the API keys from the keychain"
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
2473
2442
|
}
|
|
2474
2443
|
|
|
2475
2444
|
// src/lib/algoliaApiKey.ts
|
|
@@ -2480,27 +2449,24 @@ var WRITE_ACLS = [
|
|
|
2480
2449
|
"editSettings",
|
|
2481
2450
|
"listIndexes"
|
|
2482
2451
|
];
|
|
2483
|
-
var createdKeySchema =
|
|
2484
|
-
key:
|
|
2485
|
-
value:
|
|
2452
|
+
var createdKeySchema = z12.object({
|
|
2453
|
+
key: z12.string().min(1).optional(),
|
|
2454
|
+
value: z12.string().min(1).optional()
|
|
2486
2455
|
}).transform((o) => o.key ?? o.value);
|
|
2487
2456
|
async function createKey(index, acls, description) {
|
|
2488
2457
|
logger.info({ index, acls }, "creating an API key");
|
|
2489
|
-
const stdout = await runAlgoliaCli(
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
],
|
|
2502
|
-
{ withoutAdminKey: true }
|
|
2503
|
-
);
|
|
2458
|
+
const stdout = await runAlgoliaCli([
|
|
2459
|
+
"apikeys",
|
|
2460
|
+
"create",
|
|
2461
|
+
"--acl",
|
|
2462
|
+
acls.join(","),
|
|
2463
|
+
"--indices",
|
|
2464
|
+
index,
|
|
2465
|
+
"--description",
|
|
2466
|
+
description,
|
|
2467
|
+
"-o",
|
|
2468
|
+
"json"
|
|
2469
|
+
]);
|
|
2504
2470
|
let payload;
|
|
2505
2471
|
try {
|
|
2506
2472
|
payload = JSON.parse(stdout);
|
|
@@ -2512,8 +2478,9 @@ async function createKey(index, acls, description) {
|
|
|
2512
2478
|
return created;
|
|
2513
2479
|
}
|
|
2514
2480
|
var resolved = /* @__PURE__ */ new Map();
|
|
2515
|
-
function forgetResolvedKeys() {
|
|
2481
|
+
async function forgetResolvedKeys() {
|
|
2516
2482
|
resolved.clear();
|
|
2483
|
+
await deleteStoredKeys();
|
|
2517
2484
|
}
|
|
2518
2485
|
function resolveKey(kind, index, appId, acls, description) {
|
|
2519
2486
|
const cacheKey = `${kind}:${appId}:${index}`;
|
|
@@ -2581,8 +2548,8 @@ function upsertEnv(content, name, value) {
|
|
|
2581
2548
|
function writeCredentialsTool(ctx) {
|
|
2582
2549
|
return tool6({
|
|
2583
2550
|
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) and the target index name (${INDEX_NAME_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file (e.g. ".env"). Any name the file already defines is left untouched.`,
|
|
2584
|
-
inputSchema:
|
|
2585
|
-
filePath:
|
|
2551
|
+
inputSchema: z13.object({
|
|
2552
|
+
filePath: z13.string().describe(
|
|
2586
2553
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
2587
2554
|
)
|
|
2588
2555
|
}),
|
|
@@ -2642,23 +2609,15 @@ function writeCredentialsTool(ctx) {
|
|
|
2642
2609
|
|
|
2643
2610
|
// src/lib/tools/searchFiles.ts
|
|
2644
2611
|
import { tool as tool7 } from "ai";
|
|
2645
|
-
import
|
|
2612
|
+
import z14 from "zod";
|
|
2646
2613
|
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2647
2614
|
import { join as join7 } from "node:path";
|
|
2648
2615
|
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
2616
|
async function walkFiles(dir) {
|
|
2617
|
+
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2659
2618
|
const out = [];
|
|
2660
2619
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2661
|
-
if (e.name.startsWith(".") ||
|
|
2620
|
+
if (e.name.startsWith(".") || skip.has(e.name)) continue;
|
|
2662
2621
|
const full = join7(dir, e.name);
|
|
2663
2622
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2664
2623
|
else if (e.isFile()) out.push(full);
|
|
@@ -2668,9 +2627,9 @@ async function walkFiles(dir) {
|
|
|
2668
2627
|
function searchFilesTool(ctx) {
|
|
2669
2628
|
return tool7({
|
|
2670
2629
|
description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
|
|
2671
|
-
inputSchema:
|
|
2672
|
-
query:
|
|
2673
|
-
path:
|
|
2630
|
+
inputSchema: z14.object({
|
|
2631
|
+
query: z14.string().describe("JavaScript RegExp pattern to search for"),
|
|
2632
|
+
path: z14.string().optional().describe("Directory to search in (default: cwd)")
|
|
2674
2633
|
}),
|
|
2675
2634
|
execute: async ({ query, path = "." }) => {
|
|
2676
2635
|
logger.info({ query, path }, "called searchFiles tool");
|
|
@@ -2712,194 +2671,92 @@ function searchFilesTool(ctx) {
|
|
|
2712
2671
|
});
|
|
2713
2672
|
}
|
|
2714
2673
|
|
|
2715
|
-
// src/lib/tools/
|
|
2674
|
+
// src/lib/tools/verifyImplementation.ts
|
|
2716
2675
|
import { tool as tool8 } from "ai";
|
|
2717
|
-
import
|
|
2718
|
-
import { relative as relative2 } from "node:path";
|
|
2676
|
+
import z15 from "zod";
|
|
2719
2677
|
|
|
2720
|
-
// src/lib/tools/utils/
|
|
2678
|
+
// src/lib/tools/utils/runCommand.ts
|
|
2721
2679
|
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();
|
|
2680
|
+
function runCommand(command, args, cwd) {
|
|
2773
2681
|
return new Promise((resolve4) => {
|
|
2774
2682
|
let output = "";
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
shell: true,
|
|
2779
|
-
cwd: opts.cwd,
|
|
2780
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2781
|
-
env: { ...process.env, ...opts.env }
|
|
2683
|
+
const child = spawn2(command, args, {
|
|
2684
|
+
cwd,
|
|
2685
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2782
2686
|
});
|
|
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
2687
|
child.stdout?.on("data", (d) => output += d);
|
|
2804
2688
|
child.stderr?.on("data", (d) => output += d);
|
|
2805
|
-
child.on(
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
child.on("close", (code) =>
|
|
2689
|
+
child.on(
|
|
2690
|
+
"error",
|
|
2691
|
+
(err) => resolve4({ code: 1, output: `Failed to run ${command}: ${err.message}` })
|
|
2692
|
+
);
|
|
2693
|
+
child.on("close", (code) => resolve4({ code: code ?? 1, output }));
|
|
2810
2694
|
});
|
|
2811
2695
|
}
|
|
2812
2696
|
|
|
2813
|
-
// src/lib/tools/
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
}
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2697
|
+
// src/lib/tools/utils/packageManager.ts
|
|
2698
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2699
|
+
import { existsSync } from "node:fs";
|
|
2700
|
+
import { join as join8 } from "node:path";
|
|
2701
|
+
var LOCKFILES = [
|
|
2702
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
2703
|
+
["yarn.lock", "yarn"],
|
|
2704
|
+
["bun.lockb", "bun"],
|
|
2705
|
+
["bun.lock", "bun"],
|
|
2706
|
+
["package-lock.json", "npm"]
|
|
2707
|
+
];
|
|
2708
|
+
async function readPackageJson(cwd = process.cwd()) {
|
|
2709
|
+
return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
|
|
2710
|
+
}
|
|
2711
|
+
function packageManagerFrom(pkg) {
|
|
2712
|
+
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2713
|
+
}
|
|
2714
|
+
function packageManagerFromLockfile(cwd) {
|
|
2715
|
+
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2716
|
+
}
|
|
2717
|
+
async function detectPackageManager(cwd) {
|
|
2718
|
+
try {
|
|
2719
|
+
const pkg = await readPackageJson(cwd);
|
|
2720
|
+
if (pkg.packageManager) return packageManagerFrom(pkg);
|
|
2721
|
+
} catch {
|
|
2722
|
+
}
|
|
2723
|
+
return packageManagerFromLockfile(cwd) ?? "npm";
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
// src/lib/tools/repoVerification.ts
|
|
2727
|
+
var VERIFICATION_SCRIPT_CANDIDATES = ["lint", "typecheck", "check"];
|
|
2728
|
+
async function runRepoVerificationCheck() {
|
|
2729
|
+
let pkg;
|
|
2730
|
+
try {
|
|
2731
|
+
pkg = await readPackageJson();
|
|
2732
|
+
} catch (err) {
|
|
2733
|
+
const limitation = `Could not read package.json to detect verification conventions: ${err.message}`;
|
|
2734
|
+
return { ok: false, checks: [], limitation };
|
|
2735
|
+
}
|
|
2736
|
+
const scripts = pkg.scripts ?? {};
|
|
2737
|
+
const present = VERIFICATION_SCRIPT_CANDIDATES.filter((s) => s in scripts);
|
|
2738
|
+
if (present.length === 0) {
|
|
2739
|
+
const limitation = `No verification script found in package.json (looked for: ${VERIFICATION_SCRIPT_CANDIDATES.join(", ")}).`;
|
|
2740
|
+
return { ok: false, checks: [], limitation };
|
|
2741
|
+
}
|
|
2742
|
+
const pm = await detectPackageManager(process.cwd());
|
|
2743
|
+
const checks = [];
|
|
2744
|
+
for (const script of present) {
|
|
2745
|
+
const command = `${pm} run ${script}`;
|
|
2746
|
+
const { code, output } = await runCommand(pm, ["run", script]);
|
|
2747
|
+
checks.push({ command, exitCode: code, ok: code === 0, output: output.trim() });
|
|
2748
|
+
}
|
|
2749
|
+
return { ok: checks.every((c) => c.ok), checks };
|
|
2831
2750
|
}
|
|
2832
|
-
|
|
2751
|
+
|
|
2752
|
+
// src/lib/tools/verifyImplementation.ts
|
|
2753
|
+
function verifyImplementationTool() {
|
|
2833
2754
|
return tool8({
|
|
2834
|
-
description: "Run
|
|
2835
|
-
inputSchema:
|
|
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
|
-
};
|
|
2755
|
+
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.",
|
|
2756
|
+
inputSchema: z15.object(),
|
|
2757
|
+
execute: async () => {
|
|
2758
|
+
logger.info("called verifyImplementation tool");
|
|
2759
|
+
return runRepoVerificationCheck();
|
|
2903
2760
|
}
|
|
2904
2761
|
});
|
|
2905
2762
|
}
|
|
@@ -2910,7 +2767,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
2910
2767
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2911
2768
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2912
2769
|
import { dirname as dirname6 } from "node:path";
|
|
2913
|
-
import
|
|
2770
|
+
import z16 from "zod";
|
|
2914
2771
|
var DATA_DIR = ".algolia-wizard/data";
|
|
2915
2772
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
2916
2773
|
var MAX_RECORDS = 100;
|
|
@@ -2922,17 +2779,17 @@ var anthropic = createAnthropic({
|
|
|
2922
2779
|
function generateRecordTool(ctx) {
|
|
2923
2780
|
return tool9({
|
|
2924
2781
|
description: "Generate realistic sample records for an entity and write them to a JSON file in the worktree. Provide the entity name and its attributes; this tool asks a model to invent varied, realistic values, each with a unique objectID, and returns the file path to read them from at runtime. Do not invent the record values or objectIDs yourself, and do not inline the returned records into the script \u2014 call this tool and read the file it writes.",
|
|
2925
|
-
inputSchema:
|
|
2926
|
-
entityName:
|
|
2927
|
-
attributes:
|
|
2928
|
-
count:
|
|
2929
|
-
hint:
|
|
2782
|
+
inputSchema: z16.object({
|
|
2783
|
+
entityName: z16.string().describe("Name of the entity to generate records for."),
|
|
2784
|
+
attributes: z16.array(z16.string()).describe("Attribute names each record must contain."),
|
|
2785
|
+
count: z16.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
|
|
2786
|
+
hint: z16.string().optional().describe("Optional context to steer realistic values.")
|
|
2930
2787
|
}),
|
|
2931
2788
|
execute: async ({ entityName, attributes, count, hint }) => {
|
|
2932
2789
|
logger.info({ entityName, count }, "called generateRecord tool");
|
|
2933
2790
|
try {
|
|
2934
|
-
const value =
|
|
2935
|
-
const recordSchema =
|
|
2791
|
+
const value = z16.union([z16.string(), z16.number(), z16.boolean(), z16.null()]);
|
|
2792
|
+
const recordSchema = z16.object(
|
|
2936
2793
|
Object.fromEntries(attributes.map((attr) => [attr, value]))
|
|
2937
2794
|
);
|
|
2938
2795
|
const generateBatch = async (batchCount) => {
|
|
@@ -2942,8 +2799,8 @@ function generateRecordTool(ctx) {
|
|
|
2942
2799
|
const { output } = await generateText({
|
|
2943
2800
|
model: anthropic(RECORD_MODEL),
|
|
2944
2801
|
output: Output.object({
|
|
2945
|
-
schema:
|
|
2946
|
-
records:
|
|
2802
|
+
schema: z16.object({
|
|
2803
|
+
records: z16.array(recordSchema).length(batchCount)
|
|
2947
2804
|
})
|
|
2948
2805
|
}),
|
|
2949
2806
|
prompt: [
|
|
@@ -2990,7 +2847,7 @@ function generateRecordTool(ctx) {
|
|
|
2990
2847
|
return {
|
|
2991
2848
|
filePath: relPath,
|
|
2992
2849
|
count: records.length,
|
|
2993
|
-
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime
|
|
2850
|
+
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
2851
|
};
|
|
2995
2852
|
} catch (err) {
|
|
2996
2853
|
return `Error generating records: ${err.message}`;
|
|
@@ -3001,12 +2858,12 @@ function generateRecordTool(ctx) {
|
|
|
3001
2858
|
|
|
3002
2859
|
// src/lib/tools/notifyUser.ts
|
|
3003
2860
|
import { tool as tool10 } from "ai";
|
|
3004
|
-
import
|
|
2861
|
+
import z17 from "zod";
|
|
3005
2862
|
function notifyUserTool() {
|
|
3006
2863
|
return tool10({
|
|
3007
2864
|
description: `Give the user a brief, high-level update on what you are currently doing or about to do next. This is for the big picture (e.g. "Reading through your data models", "Writing the search UI") \u2014 not granular detail like individual tool calls, which are already logged separately. Call it when you start a new phase of work or your focus shifts, just not on every step, enough to keep the user engaged. Don't say things like "starting", just describe what you are doing. Don't mention tool calls themselves, just general direction of the work.`,
|
|
3008
|
-
inputSchema:
|
|
3009
|
-
message:
|
|
2865
|
+
inputSchema: z17.object({
|
|
2866
|
+
message: z17.string().describe(
|
|
3010
2867
|
"Short, plain-language description of what you are doing now."
|
|
3011
2868
|
)
|
|
3012
2869
|
}),
|
|
@@ -3018,6 +2875,22 @@ function notifyUserTool() {
|
|
|
3018
2875
|
});
|
|
3019
2876
|
}
|
|
3020
2877
|
|
|
2878
|
+
// src/lib/tools/context.ts
|
|
2879
|
+
var DEFAULT_TOOL_LIMITS = {
|
|
2880
|
+
list: 10,
|
|
2881
|
+
search: 10,
|
|
2882
|
+
read: 20,
|
|
2883
|
+
match: 100
|
|
2884
|
+
};
|
|
2885
|
+
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd()) {
|
|
2886
|
+
return {
|
|
2887
|
+
root: cwd,
|
|
2888
|
+
cwd,
|
|
2889
|
+
limits,
|
|
2890
|
+
counts: { list: 0, search: 0, read: 0 }
|
|
2891
|
+
};
|
|
2892
|
+
}
|
|
2893
|
+
|
|
3021
2894
|
// src/lib/tools/index.ts
|
|
3022
2895
|
function withLogging(name, def) {
|
|
3023
2896
|
const execute = def.execute;
|
|
@@ -3049,7 +2922,10 @@ function createTools(ctx, { output, tools }) {
|
|
|
3049
2922
|
writeCredentialsTool(ctx)
|
|
3050
2923
|
),
|
|
3051
2924
|
searchFiles: withLogging("searchFiles", searchFilesTool(ctx)),
|
|
3052
|
-
|
|
2925
|
+
verifyImplementation: withLogging(
|
|
2926
|
+
"verifyImplementation",
|
|
2927
|
+
verifyImplementationTool()
|
|
2928
|
+
),
|
|
3053
2929
|
generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
|
|
3054
2930
|
notifyUser: withLogging("notifyUser", notifyUserTool())
|
|
3055
2931
|
};
|
|
@@ -3084,7 +2960,7 @@ async function runAgent(req) {
|
|
|
3084
2960
|
baseURL: PROXY_BASE_URL,
|
|
3085
2961
|
fetch: proxyFetch
|
|
3086
2962
|
});
|
|
3087
|
-
const toolContext =
|
|
2963
|
+
const toolContext = createToolContext();
|
|
3088
2964
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
3089
2965
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
3090
2966
|
const instructions = [
|
|
@@ -3149,11 +3025,7 @@ async function runAgent(req) {
|
|
|
3149
3025
|
"runAgent finished"
|
|
3150
3026
|
);
|
|
3151
3027
|
logger.info(
|
|
3152
|
-
{
|
|
3153
|
-
counts: toolContext.counts,
|
|
3154
|
-
limits: toolContext.limits,
|
|
3155
|
-
commandsRun: toolContext.shell.executions.length
|
|
3156
|
-
},
|
|
3028
|
+
{ counts: toolContext.counts, limits: toolContext.limits },
|
|
3157
3029
|
"tool usage"
|
|
3158
3030
|
);
|
|
3159
3031
|
const toolResults = await stream.toolResults;
|
|
@@ -3171,16 +3043,16 @@ async function runAgent(req) {
|
|
|
3171
3043
|
}
|
|
3172
3044
|
|
|
3173
3045
|
// src/actions/detectLanguage.ts
|
|
3174
|
-
import
|
|
3175
|
-
var detectLanguageSchema =
|
|
3176
|
-
languages:
|
|
3177
|
-
frameworks:
|
|
3046
|
+
import z20 from "zod";
|
|
3047
|
+
var detectLanguageSchema = z20.object({
|
|
3048
|
+
languages: z20.array(z20.object({ name: z20.string(), version: z20.string() })),
|
|
3049
|
+
frameworks: z20.array(z20.object({ name: z20.string(), version: z20.string() }))
|
|
3178
3050
|
});
|
|
3179
3051
|
var detectLanguage = () => runAgent({
|
|
3180
3052
|
instructions: [
|
|
3181
3053
|
"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
|
|
3054
|
+
"If a superset language is found, exclude the subset language. TS-over-JS.",
|
|
3055
|
+
"If a meta-framework is used, exclude the framework. Next-over-React.",
|
|
3184
3056
|
"Return the exact version",
|
|
3185
3057
|
"Exclude things like CSS frameworks, build tools, or testing frameworks",
|
|
3186
3058
|
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
@@ -3192,31 +3064,31 @@ var detectLanguage = () => runAgent({
|
|
|
3192
3064
|
});
|
|
3193
3065
|
|
|
3194
3066
|
// src/actions/analyzeCodebase.ts
|
|
3195
|
-
import
|
|
3067
|
+
import z21 from "zod";
|
|
3196
3068
|
var READONLY_TOOLS = [
|
|
3197
3069
|
"listFiles",
|
|
3198
3070
|
"changeDirectory",
|
|
3199
3071
|
"readFile",
|
|
3200
3072
|
"searchFiles"
|
|
3201
3073
|
];
|
|
3202
|
-
var ingestionAnalysisSchema =
|
|
3203
|
-
ingestionAnalysis:
|
|
3204
|
-
|
|
3205
|
-
name:
|
|
3206
|
-
paths:
|
|
3074
|
+
var ingestionAnalysisSchema = z21.object({
|
|
3075
|
+
ingestionAnalysis: z21.array(
|
|
3076
|
+
z21.object({
|
|
3077
|
+
name: z21.string(),
|
|
3078
|
+
paths: z21.array(z21.string()),
|
|
3207
3079
|
// indexable fields the agent found for this entity
|
|
3208
|
-
attributes:
|
|
3080
|
+
attributes: z21.array(z21.string())
|
|
3209
3081
|
})
|
|
3210
3082
|
)
|
|
3211
3083
|
});
|
|
3212
|
-
var searchImplementationAnalysisSchema =
|
|
3213
|
-
searchImplementationAnalysis:
|
|
3084
|
+
var searchImplementationAnalysisSchema = z21.object({
|
|
3085
|
+
searchImplementationAnalysis: z21.string()
|
|
3214
3086
|
});
|
|
3215
|
-
var verificationSchema =
|
|
3216
|
-
verification:
|
|
3087
|
+
var verificationSchema = z21.object({
|
|
3088
|
+
verification: z21.array(z21.string())
|
|
3217
3089
|
});
|
|
3218
3090
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
3219
|
-
var analyzeCodebaseSchema =
|
|
3091
|
+
var analyzeCodebaseSchema = z21.object({
|
|
3220
3092
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3221
3093
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
3222
3094
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -3278,7 +3150,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3278
3150
|
// package.json
|
|
3279
3151
|
var package_default = {
|
|
3280
3152
|
name: "@algolia/wizard",
|
|
3281
|
-
version: "0.9.0-rc.
|
|
3153
|
+
version: "0.9.0-rc.88.102",
|
|
3282
3154
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3283
3155
|
type: "module",
|
|
3284
3156
|
engines: {
|
|
@@ -3397,8 +3269,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
3397
3269
|
}
|
|
3398
3270
|
|
|
3399
3271
|
// src/actions/confirmLanguage.ts
|
|
3400
|
-
import
|
|
3401
|
-
var confirmLanguageSchema =
|
|
3272
|
+
import z23 from "zod";
|
|
3273
|
+
var confirmLanguageSchema = z23.object({
|
|
3402
3274
|
languages: detectLanguageSchema.shape.languages
|
|
3403
3275
|
});
|
|
3404
3276
|
async function confirmLanguage(ctx) {
|
|
@@ -3419,19 +3291,17 @@ async function confirmLanguage(ctx) {
|
|
|
3419
3291
|
}
|
|
3420
3292
|
|
|
3421
3293
|
// src/actions/confirmFramework.ts
|
|
3422
|
-
import
|
|
3423
|
-
var confirmFrameworkSchema =
|
|
3294
|
+
import z24 from "zod";
|
|
3295
|
+
var confirmFrameworkSchema = z24.object({
|
|
3424
3296
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
3425
3297
|
});
|
|
3426
3298
|
var CURATED_FRAMEWORKS = [
|
|
3427
3299
|
"Next.js",
|
|
3428
3300
|
"React",
|
|
3429
3301
|
"Vue",
|
|
3430
|
-
"
|
|
3431
|
-
"
|
|
3432
|
-
"
|
|
3433
|
-
"Rails",
|
|
3434
|
-
"Symfony"
|
|
3302
|
+
"Angular",
|
|
3303
|
+
"Svelte",
|
|
3304
|
+
"Vanilla JS"
|
|
3435
3305
|
];
|
|
3436
3306
|
var OTHER_OPTION = "Other";
|
|
3437
3307
|
var normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -3444,15 +3314,12 @@ var FRAMEWORK_ALIASES = {
|
|
|
3444
3314
|
vuejs: "vue",
|
|
3445
3315
|
angular: "angular",
|
|
3446
3316
|
angularjs: "angular",
|
|
3317
|
+
svelte: "svelte",
|
|
3318
|
+
sveltekit: "svelte",
|
|
3447
3319
|
vanillajs: "vanillajs",
|
|
3448
3320
|
vanilla: "vanillajs",
|
|
3449
3321
|
javascript: "vanillajs",
|
|
3450
|
-
js: "vanillajs"
|
|
3451
|
-
django: "django",
|
|
3452
|
-
laravel: "laravel",
|
|
3453
|
-
rails: "rails",
|
|
3454
|
-
rubyonrails: "rails",
|
|
3455
|
-
symfony: "symfony"
|
|
3322
|
+
js: "vanillajs"
|
|
3456
3323
|
};
|
|
3457
3324
|
var isSameFramework = (a, b) => {
|
|
3458
3325
|
const x = FRAMEWORK_ALIASES[normalize(a)] ?? normalize(a);
|
|
@@ -3553,8 +3420,8 @@ async function promptUser(ctx, params) {
|
|
|
3553
3420
|
}
|
|
3554
3421
|
|
|
3555
3422
|
// src/actions/confirmEntities.ts
|
|
3556
|
-
import
|
|
3557
|
-
var confirmEntitiesSchema =
|
|
3423
|
+
import z25 from "zod";
|
|
3424
|
+
var confirmEntitiesSchema = z25.object({
|
|
3558
3425
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
3559
3426
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3560
3427
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -3624,15 +3491,15 @@ async function confirmEntities(ctx) {
|
|
|
3624
3491
|
}
|
|
3625
3492
|
|
|
3626
3493
|
// src/actions/review.ts
|
|
3627
|
-
import { z as
|
|
3628
|
-
var reviewSchema =
|
|
3494
|
+
import { z as z26 } from "zod";
|
|
3495
|
+
var reviewSchema = z26.object({
|
|
3629
3496
|
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
3630
3497
|
// not one entry per workflow step — a step's raw output can be a long,
|
|
3631
3498
|
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
3632
3499
|
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
3633
|
-
summaryPoints:
|
|
3634
|
-
reviewPrompt:
|
|
3635
|
-
nextSteps:
|
|
3500
|
+
summaryPoints: z26.array(z26.string()),
|
|
3501
|
+
reviewPrompt: z26.string(),
|
|
3502
|
+
nextSteps: z26.array(z26.string())
|
|
3636
3503
|
});
|
|
3637
3504
|
function formatCompletedSteps(steps) {
|
|
3638
3505
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -3683,12 +3550,19 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3683
3550
|
};
|
|
3684
3551
|
|
|
3685
3552
|
// src/actions/implement.ts
|
|
3686
|
-
import
|
|
3553
|
+
import z27 from "zod";
|
|
3687
3554
|
|
|
3688
3555
|
// src/lib/worktree.ts
|
|
3689
|
-
import { execFile } from "node:child_process";
|
|
3690
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3691
|
-
import {
|
|
3556
|
+
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3557
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3558
|
+
import {
|
|
3559
|
+
basename as basename2,
|
|
3560
|
+
dirname as dirname7,
|
|
3561
|
+
isAbsolute as isAbsolute2,
|
|
3562
|
+
join as join9,
|
|
3563
|
+
relative as relative2,
|
|
3564
|
+
resolve as resolve3
|
|
3565
|
+
} from "node:path";
|
|
3692
3566
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
3693
3567
|
var MAX_WIZARD_WORKTREES = 3;
|
|
3694
3568
|
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
@@ -3719,7 +3593,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3719
3593
|
return out.trim().length > 0;
|
|
3720
3594
|
}
|
|
3721
3595
|
async function pruneOldWorktrees(repoRoot) {
|
|
3722
|
-
const dir =
|
|
3596
|
+
const dir = join9(stateDir(repoRoot), "worktrees");
|
|
3723
3597
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3724
3598
|
for (const slug of stale) {
|
|
3725
3599
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3730,7 +3604,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3730
3604
|
"worktree",
|
|
3731
3605
|
"remove",
|
|
3732
3606
|
"--force",
|
|
3733
|
-
|
|
3607
|
+
join9(dir, slug)
|
|
3734
3608
|
]);
|
|
3735
3609
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3736
3610
|
} catch (err) {
|
|
@@ -3744,13 +3618,113 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3744
3618
|
async function createWorktree(repoRoot) {
|
|
3745
3619
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3746
3620
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3747
|
-
const path =
|
|
3621
|
+
const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3748
3622
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3749
3623
|
await pruneOldWorktrees(repoRoot);
|
|
3750
3624
|
await mkdir6(dirname7(path), { recursive: true });
|
|
3751
3625
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3752
3626
|
return { path, branch };
|
|
3753
3627
|
}
|
|
3628
|
+
async function installWorktreeDeps(worktreePath) {
|
|
3629
|
+
try {
|
|
3630
|
+
await readPackageJson(worktreePath);
|
|
3631
|
+
} catch {
|
|
3632
|
+
return { ok: true, output: "no package.json; skipped install" };
|
|
3633
|
+
}
|
|
3634
|
+
const pm = await detectPackageManager(worktreePath);
|
|
3635
|
+
return new Promise((resolve4) => {
|
|
3636
|
+
let output = "";
|
|
3637
|
+
const child = spawn3(pm, ["install"], {
|
|
3638
|
+
cwd: worktreePath,
|
|
3639
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3640
|
+
});
|
|
3641
|
+
child.stdout?.on("data", (d) => output += d);
|
|
3642
|
+
child.stderr?.on("data", (d) => output += d);
|
|
3643
|
+
child.on(
|
|
3644
|
+
"error",
|
|
3645
|
+
(err) => resolve4({
|
|
3646
|
+
ok: false,
|
|
3647
|
+
output: `Failed to run ${pm} install: ${err.message}`
|
|
3648
|
+
})
|
|
3649
|
+
);
|
|
3650
|
+
child.on(
|
|
3651
|
+
"close",
|
|
3652
|
+
(code) => resolve4({ ok: code === 0, output: output.trim() })
|
|
3653
|
+
);
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3656
|
+
var INGEST_RUNTIMES = ["node", "python", "python3", "bun"];
|
|
3657
|
+
function validateIngestEntrypoint(worktreePath, entrypoint) {
|
|
3658
|
+
if (!entrypoint || entrypoint.startsWith("-")) {
|
|
3659
|
+
return {
|
|
3660
|
+
ok: false,
|
|
3661
|
+
reason: `entrypoint "${entrypoint}" is not a plain file path`
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
const target = resolve3(worktreePath, entrypoint);
|
|
3665
|
+
const rel = relative2(worktreePath, target);
|
|
3666
|
+
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
3667
|
+
return {
|
|
3668
|
+
ok: false,
|
|
3669
|
+
reason: `entrypoint "${entrypoint}" resolves outside the worktree`
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
return { ok: true, target };
|
|
3673
|
+
}
|
|
3674
|
+
async function runIngestScript(worktreePath, runtime, entrypoint, env = {}) {
|
|
3675
|
+
if (!INGEST_RUNTIMES.includes(runtime)) {
|
|
3676
|
+
return {
|
|
3677
|
+
ran: false,
|
|
3678
|
+
ok: false,
|
|
3679
|
+
output: "",
|
|
3680
|
+
reason: `runtime "${runtime}" is not an allowed interpreter (${INGEST_RUNTIMES.join(", ")})`
|
|
3681
|
+
};
|
|
3682
|
+
}
|
|
3683
|
+
const validated = validateIngestEntrypoint(worktreePath, entrypoint);
|
|
3684
|
+
if (!validated.ok) {
|
|
3685
|
+
return { ran: false, ok: false, output: "", reason: validated.reason };
|
|
3686
|
+
}
|
|
3687
|
+
try {
|
|
3688
|
+
if (!(await stat2(validated.target)).isFile()) {
|
|
3689
|
+
return {
|
|
3690
|
+
ran: false,
|
|
3691
|
+
ok: false,
|
|
3692
|
+
output: "",
|
|
3693
|
+
reason: `entrypoint "${entrypoint}" is not a file`
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
} catch {
|
|
3697
|
+
return {
|
|
3698
|
+
ran: false,
|
|
3699
|
+
ok: false,
|
|
3700
|
+
output: "",
|
|
3701
|
+
reason: `entrypoint "${entrypoint}" does not exist`
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
return new Promise((resolveRun) => {
|
|
3705
|
+
let output = "";
|
|
3706
|
+
const child = spawn3(runtime, [entrypoint], {
|
|
3707
|
+
cwd: worktreePath,
|
|
3708
|
+
shell: false,
|
|
3709
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3710
|
+
env: { ...process.env, ...env }
|
|
3711
|
+
});
|
|
3712
|
+
child.stdout?.on("data", (d) => output += d);
|
|
3713
|
+
child.stderr?.on("data", (d) => output += d);
|
|
3714
|
+
child.on(
|
|
3715
|
+
"error",
|
|
3716
|
+
(err) => resolveRun({
|
|
3717
|
+
ran: true,
|
|
3718
|
+
ok: false,
|
|
3719
|
+
output: `Failed to run ${runtime} ${entrypoint}: ${err.message}`
|
|
3720
|
+
})
|
|
3721
|
+
);
|
|
3722
|
+
child.on(
|
|
3723
|
+
"close",
|
|
3724
|
+
(code) => resolveRun({ ran: true, ok: code === 0, output: output.trim() })
|
|
3725
|
+
);
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3754
3728
|
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
3755
3729
|
const trimmed = sourcePath.trim();
|
|
3756
3730
|
if (!trimmed) {
|
|
@@ -3764,8 +3738,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3764
3738
|
} catch {
|
|
3765
3739
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3766
3740
|
}
|
|
3767
|
-
const relPath =
|
|
3768
|
-
const dest =
|
|
3741
|
+
const relPath = join9(ingestDir, basename2(source));
|
|
3742
|
+
const dest = join9(worktreePath, relPath);
|
|
3769
3743
|
try {
|
|
3770
3744
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3771
3745
|
await copyFile(source, dest);
|
|
@@ -3783,7 +3757,7 @@ function hasEnvVar(content, name) {
|
|
|
3783
3757
|
async function readEnvVar(worktreePath, name) {
|
|
3784
3758
|
let content;
|
|
3785
3759
|
try {
|
|
3786
|
-
content = await
|
|
3760
|
+
content = await readFile7(join9(worktreePath, ".env"), "utf8");
|
|
3787
3761
|
} catch (err) {
|
|
3788
3762
|
if (err.code !== "ENOENT") throw err;
|
|
3789
3763
|
return void 0;
|
|
@@ -3798,10 +3772,10 @@ async function readEnvVar(worktreePath, name) {
|
|
|
3798
3772
|
return value;
|
|
3799
3773
|
}
|
|
3800
3774
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3801
|
-
const target =
|
|
3775
|
+
const target = join9(worktreePath, ".env");
|
|
3802
3776
|
let existing = "";
|
|
3803
3777
|
try {
|
|
3804
|
-
existing = await
|
|
3778
|
+
existing = await readFile7(target, "utf8");
|
|
3805
3779
|
} catch (err) {
|
|
3806
3780
|
if (err.code !== "ENOENT") throw err;
|
|
3807
3781
|
}
|
|
@@ -3870,15 +3844,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3870
3844
|
}
|
|
3871
3845
|
|
|
3872
3846
|
// src/lib/algoliaDocs.ts
|
|
3873
|
-
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
3874
|
-
import { dirname as dirname8, join as
|
|
3847
|
+
import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
|
|
3848
|
+
import { dirname as dirname8, join as join10 } from "node:path";
|
|
3875
3849
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3876
|
-
var DOCS_SUBPATH =
|
|
3850
|
+
var DOCS_SUBPATH = join10("docs", "algolia-sdk");
|
|
3877
3851
|
function findDocsDir() {
|
|
3878
3852
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3879
3853
|
for (; ; ) {
|
|
3880
|
-
const candidate =
|
|
3881
|
-
if (
|
|
3854
|
+
const candidate = join10(dir, DOCS_SUBPATH);
|
|
3855
|
+
if (existsSync2(candidate)) return candidate;
|
|
3882
3856
|
const parent = dirname8(dir);
|
|
3883
3857
|
if (parent === dir) return void 0;
|
|
3884
3858
|
dir = parent;
|
|
@@ -3900,7 +3874,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3900
3874
|
);
|
|
3901
3875
|
return "";
|
|
3902
3876
|
}
|
|
3903
|
-
return readFileSync(
|
|
3877
|
+
return readFileSync(join10(docsDir, files[0]), "utf8").trim();
|
|
3904
3878
|
}
|
|
3905
3879
|
function getNamedDoc(name, language) {
|
|
3906
3880
|
const docsDir = findDocsDir();
|
|
@@ -3908,15 +3882,14 @@ function getNamedDoc(name, language) {
|
|
|
3908
3882
|
logger.warn("docs/algolia-sdk not found");
|
|
3909
3883
|
return "";
|
|
3910
3884
|
}
|
|
3911
|
-
const file =
|
|
3912
|
-
if (!
|
|
3885
|
+
const file = join10(docsDir, `${name}-${language}.md`);
|
|
3886
|
+
if (!existsSync2(file)) {
|
|
3913
3887
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3914
3888
|
return "";
|
|
3915
3889
|
}
|
|
3916
3890
|
return readFileSync(file, "utf8").trim();
|
|
3917
3891
|
}
|
|
3918
3892
|
function getFrameworkSpecificDoc(frameworks) {
|
|
3919
|
-
if (frameworks.length === 0) return "";
|
|
3920
3893
|
const fw = frameworks.map((f) => f.toLowerCase());
|
|
3921
3894
|
if (fw.includes("vue") || fw.includes("nuxt")) {
|
|
3922
3895
|
return loadAlgoliaDoc("vue");
|
|
@@ -3924,6 +3897,9 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
3924
3897
|
if (fw.includes("react") || fw.includes("next.js")) {
|
|
3925
3898
|
return loadAlgoliaDoc("react");
|
|
3926
3899
|
}
|
|
3900
|
+
if (fw.includes("angular")) {
|
|
3901
|
+
return loadAlgoliaDoc("angular");
|
|
3902
|
+
}
|
|
3927
3903
|
return loadAlgoliaDoc("js");
|
|
3928
3904
|
}
|
|
3929
3905
|
|
|
@@ -3933,63 +3909,62 @@ function shellQuote(value) {
|
|
|
3933
3909
|
}
|
|
3934
3910
|
|
|
3935
3911
|
// src/actions/implement.ts
|
|
3936
|
-
var implementSchema =
|
|
3937
|
-
filesChanged:
|
|
3938
|
-
summary:
|
|
3939
|
-
worktreePath:
|
|
3940
|
-
ingestCommand:
|
|
3941
|
-
ingestScriptRan:
|
|
3942
|
-
ingestRecordCount:
|
|
3943
|
-
ingestDurationMs:
|
|
3944
|
-
ingestionSource:
|
|
3945
|
-
searchEnvVars:
|
|
3946
|
-
|
|
3947
|
-
name:
|
|
3948
|
-
value:
|
|
3912
|
+
var implementSchema = z27.object({
|
|
3913
|
+
filesChanged: z27.array(z27.string()),
|
|
3914
|
+
summary: z27.string(),
|
|
3915
|
+
worktreePath: z27.string().optional(),
|
|
3916
|
+
ingestCommand: z27.string().optional(),
|
|
3917
|
+
ingestScriptRan: z27.boolean().optional(),
|
|
3918
|
+
ingestRecordCount: z27.number().optional(),
|
|
3919
|
+
ingestDurationMs: z27.number().optional(),
|
|
3920
|
+
ingestionSource: z27.enum(["local", "fileUpload", "generated"]),
|
|
3921
|
+
searchEnvVars: z27.array(
|
|
3922
|
+
z27.object({
|
|
3923
|
+
name: z27.string(),
|
|
3924
|
+
value: z27.string()
|
|
3949
3925
|
})
|
|
3950
3926
|
).optional()
|
|
3951
3927
|
});
|
|
3952
|
-
var implementationOutputSchema =
|
|
3953
|
-
summary:
|
|
3954
|
-
|
|
3928
|
+
var implementationOutputSchema = z27.object({
|
|
3929
|
+
summary: z27.string(),
|
|
3930
|
+
// Ingestion only: a structured pair the wizard turns into an argv, never a
|
|
3931
|
+
// free-form command string. `runtime` is allowlisted and `entrypoint` is
|
|
3932
|
+
// validated worktree-relative, so the agent cannot inject extra commands.
|
|
3933
|
+
runtime: z27.enum(INGEST_RUNTIMES).optional(),
|
|
3934
|
+
entrypoint: z27.string().optional()
|
|
3955
3935
|
});
|
|
3956
|
-
var verificationOutputSchema =
|
|
3957
|
-
summary:
|
|
3958
|
-
sufficient:
|
|
3959
|
-
additionalInstructions:
|
|
3936
|
+
var verificationOutputSchema = z27.object({
|
|
3937
|
+
summary: z27.string(),
|
|
3938
|
+
sufficient: z27.boolean(),
|
|
3939
|
+
additionalInstructions: z27.string().optional()
|
|
3960
3940
|
});
|
|
3961
3941
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3962
3942
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
3963
3943
|
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"];
|
|
3944
|
+
function detectUiFramework(language) {
|
|
3945
|
+
const names = language.frameworks.map((f) => f.name.toLowerCase());
|
|
3946
|
+
if (names.some((n) => n.includes("vue") || n.includes("nuxt"))) return "Vue";
|
|
3947
|
+
if (names.some((n) => n.includes("react") || n.includes("next")))
|
|
3948
|
+
return "React";
|
|
3949
|
+
if (names.some((n) => n.includes("angular"))) return "Angular";
|
|
3950
|
+
return "JavaScript";
|
|
3951
|
+
}
|
|
3952
|
+
function frameworksForDoc(framework) {
|
|
3953
|
+
switch (framework) {
|
|
3954
|
+
case "React":
|
|
3955
|
+
return ["react"];
|
|
3956
|
+
case "Vue":
|
|
3957
|
+
return ["vue"];
|
|
3958
|
+
case "Angular":
|
|
3959
|
+
return ["angular"];
|
|
3960
|
+
case "JavaScript":
|
|
3961
|
+
return [];
|
|
3962
|
+
}
|
|
3990
3963
|
}
|
|
3991
3964
|
function publicEnvPrefix(language) {
|
|
3992
|
-
const frameworkNames =
|
|
3965
|
+
const frameworkNames = language.frameworks.map(
|
|
3966
|
+
(framework) => framework.name.toLowerCase()
|
|
3967
|
+
);
|
|
3993
3968
|
if (frameworkNames.some((name) => name.includes("next"))) {
|
|
3994
3969
|
return "NEXT_PUBLIC_";
|
|
3995
3970
|
}
|
|
@@ -4002,7 +3977,7 @@ function publicEnvPrefix(language) {
|
|
|
4002
3977
|
if (frameworkNames.some((name) => name.includes("vite"))) {
|
|
4003
3978
|
return "VITE_";
|
|
4004
3979
|
}
|
|
4005
|
-
return
|
|
3980
|
+
return "PUBLIC_";
|
|
4006
3981
|
}
|
|
4007
3982
|
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
4008
3983
|
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
@@ -4041,10 +4016,7 @@ function baseInstructions(input) {
|
|
|
4041
4016
|
// index-scoped keys then reject with a 403.
|
|
4042
4017
|
`Target Algolia index, to be used exactly as written \u2014 never renamed, re-cased, prefixed, or suffixed: "${input.targetIndex}"`,
|
|
4043
4018
|
`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."
|
|
4019
|
+
"Make minimal, idiomatic changes; do not touch unrelated code."
|
|
4048
4020
|
];
|
|
4049
4021
|
}
|
|
4050
4022
|
function sourceSpecificInstructions(input) {
|
|
@@ -4064,21 +4036,12 @@ function sourceSpecificInstructions(input) {
|
|
|
4064
4036
|
generated: [
|
|
4065
4037
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
4066
4038
|
"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
|
|
4039
|
+
"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
4040
|
"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
4041
|
]
|
|
4070
4042
|
};
|
|
4071
4043
|
return byLine[input.ingestionSource];
|
|
4072
4044
|
}
|
|
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
4045
|
function ingestionInstructions(input) {
|
|
4083
4046
|
return [
|
|
4084
4047
|
...input.confirmed && input.confirmed.length ? [
|
|
@@ -4086,30 +4049,25 @@ function ingestionInstructions(input) {
|
|
|
4086
4049
|
`Ingest only these confirmed entities (name, source paths, attributes): ${JSON.stringify(input.confirmed)}.`,
|
|
4087
4050
|
`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
4051
|
`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
|
-
"
|
|
4052
|
+
"Use the appropriate Algolia package in the ingestion script. Do not use the raw HTTP API.",
|
|
4090
4053
|
"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.',
|
|
4054
|
+
getNamedDoc("save-records", "js"),
|
|
4055
|
+
'Add algoliasearch to package.json "dependencies" with a valid version range; the wizard installs the worktree deps after you finish.',
|
|
4094
4056
|
"The summary should be extremely concise.",
|
|
4057
|
+
`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
4058
|
...sourceSpecificInstructions(input)
|
|
4096
4059
|
] : []
|
|
4097
4060
|
];
|
|
4098
4061
|
}
|
|
4099
4062
|
function searchInstructions(input) {
|
|
4100
|
-
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.
|
|
4063
|
+
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.uiFramework));
|
|
4101
4064
|
return [
|
|
4102
4065
|
"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.`,
|
|
4066
|
+
`Build the search UI for ${input.uiFramework}.`,
|
|
4067
|
+
"Follow the Algolia JS SDK reference below for client setup and InstantSearch wiring; prefer it over prior knowledge:",
|
|
4068
|
+
doc,
|
|
4069
|
+
`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
4070
|
`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
4071
|
// The key is provisioned only after verification passes, so the agent never
|
|
4114
4072
|
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
4115
4073
|
// decide whether a key already exists, and an agent-invented value there
|
|
@@ -4118,8 +4076,9 @@ function searchInstructions(input) {
|
|
|
4118
4076
|
// Not the agent's to rename: the wizard writes these exact names into
|
|
4119
4077
|
// ".env" right after this step, so a renamed prefix would leave the code
|
|
4120
4078
|
// reading a var the wizard never wrote.
|
|
4121
|
-
`Use exactly these env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4122
|
-
"
|
|
4079
|
+
`Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4080
|
+
"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.",
|
|
4081
|
+
'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
4082
|
"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
4083
|
];
|
|
4125
4084
|
}
|
|
@@ -4127,12 +4086,11 @@ function verificationInstructions(input) {
|
|
|
4127
4086
|
return [
|
|
4128
4087
|
"Verify the Algolia implementation changes in the current worktree.",
|
|
4129
4088
|
`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.",
|
|
4089
|
+
"Call verifyImplementation at least once; it runs every repo-defined lint/typecheck/check script and returns per-check results plus an aggregate ok.",
|
|
4090
|
+
"For issues caused by the implementation, make minimal fixes with writeFile and re-run verifyImplementation.",
|
|
4091
|
+
"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
4092
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
4135
|
-
`Do not modify "${input.ingestDir}/" unless
|
|
4093
|
+
`Do not modify "${input.ingestDir}/" unless verifyImplementation reports an actionable issue in its files.`,
|
|
4136
4094
|
"Always call reportStatus with status=success once verification has run, even when sufficient=false.",
|
|
4137
4095
|
"Set sufficient=true only when the implementation is complete and checks pass (or fail for a clearly unrelated reason).",
|
|
4138
4096
|
"Set sufficient=false when the implementation is incomplete or has implementation-caused failures; include concrete additionalInstructions for the next pass."
|
|
@@ -4153,15 +4111,14 @@ var IMPLEMENT_CONFIG = {
|
|
|
4153
4111
|
}
|
|
4154
4112
|
};
|
|
4155
4113
|
var useCaseToolMap = {
|
|
4156
|
-
ingestion: [
|
|
4114
|
+
ingestion: [...FS_READ_TOOLS, "writeFile", "writeCredentials", "notifyUser"],
|
|
4115
|
+
search: [...FS_READ_TOOLS, "writeFile", "notifyUser"],
|
|
4116
|
+
verification: [
|
|
4157
4117
|
...FS_READ_TOOLS,
|
|
4158
4118
|
"writeFile",
|
|
4159
|
-
"
|
|
4160
|
-
"runShell",
|
|
4119
|
+
"verifyImplementation",
|
|
4161
4120
|
"notifyUser"
|
|
4162
|
-
]
|
|
4163
|
-
search: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"],
|
|
4164
|
-
verification: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"]
|
|
4121
|
+
]
|
|
4165
4122
|
};
|
|
4166
4123
|
function toolsForUseCase(useCase, ingestionSource) {
|
|
4167
4124
|
const tools = useCaseToolMap[useCase];
|
|
@@ -4184,30 +4141,15 @@ function formatSummary(useCase, summary) {
|
|
|
4184
4141
|
const label = useCase === "ingestion" ? "Ingestion" : useCase === "search" ? "Search" : "Verification";
|
|
4185
4142
|
return `${label}: ${summary}`;
|
|
4186
4143
|
}
|
|
4144
|
+
function buildIngestCommand(worktree, runtime, entrypoint) {
|
|
4145
|
+
return `cd ${shellQuote(worktree)} && ${runtime} ${shellQuote(entrypoint)}`;
|
|
4146
|
+
}
|
|
4187
4147
|
function parseIngestRecordCount(output) {
|
|
4188
4148
|
const match = output.match(/ALGOLIA_WIZARD_RECORD_COUNT=(\d+)/);
|
|
4189
4149
|
if (!match) return void 0;
|
|
4190
4150
|
const count = Number(match[1]);
|
|
4191
4151
|
return Number.isFinite(count) ? count : void 0;
|
|
4192
4152
|
}
|
|
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
4153
|
function verificationRetryInstructions(verification) {
|
|
4212
4154
|
return [
|
|
4213
4155
|
`Implementation insufficient. Address these findings before reporting completion: ${verification.additionalInstructions ?? verification.summary}`
|
|
@@ -4286,13 +4228,9 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4286
4228
|
const confirmed2 = normalized.confirmedEntities;
|
|
4287
4229
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
4288
4230
|
let appId;
|
|
4289
|
-
let ingestAppId;
|
|
4290
4231
|
if (useCases.includes("search")) {
|
|
4291
4232
|
appId = (await requireApplication()).id;
|
|
4292
4233
|
}
|
|
4293
|
-
if (useCases.includes("ingestion")) {
|
|
4294
|
-
ingestAppId = appId ?? (await requireApplication()).id;
|
|
4295
|
-
}
|
|
4296
4234
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
4297
4235
|
try {
|
|
4298
4236
|
process.chdir(worktree);
|
|
@@ -4329,7 +4267,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4329
4267
|
ingestDir: INGEST_DIR,
|
|
4330
4268
|
ingestionSource,
|
|
4331
4269
|
uploadFilePath,
|
|
4332
|
-
|
|
4270
|
+
uiFramework: detectUiFramework(language)
|
|
4333
4271
|
};
|
|
4334
4272
|
const summaries = [];
|
|
4335
4273
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
@@ -4352,31 +4290,41 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4352
4290
|
}
|
|
4353
4291
|
let finalSearchEnvVars = input.searchEnvVars;
|
|
4354
4292
|
let agentRuns = 0;
|
|
4355
|
-
let
|
|
4293
|
+
let ingestRuntime;
|
|
4294
|
+
let ingestEntrypoint;
|
|
4356
4295
|
let ingestScriptRan = false;
|
|
4357
4296
|
let ingestRecordCount;
|
|
4358
4297
|
let ingestDurationMs;
|
|
4298
|
+
let installFailed = false;
|
|
4359
4299
|
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
4300
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4368
4301
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4369
4302
|
agentRuns += 1;
|
|
4370
|
-
|
|
4303
|
+
const result = await runAgent({
|
|
4371
4304
|
instructions: buildAgentInstructions(
|
|
4372
4305
|
currentUseCase,
|
|
4373
4306
|
input,
|
|
4374
4307
|
extraInstructions
|
|
4375
4308
|
),
|
|
4376
4309
|
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4377
|
-
outputSchema: implementationOutputSchema
|
|
4378
|
-
|
|
4310
|
+
outputSchema: implementationOutputSchema
|
|
4311
|
+
});
|
|
4312
|
+
ctx.notify({
|
|
4313
|
+
messages: [`Installing dependencies for ${currentUseCase}\u2026`]
|
|
4379
4314
|
});
|
|
4315
|
+
const installLogId = ctx.logStart("installWorktreeDeps", {
|
|
4316
|
+
useCase: currentUseCase
|
|
4317
|
+
});
|
|
4318
|
+
const install = await installWorktreeDeps(worktree);
|
|
4319
|
+
ctx.logEnd(installLogId, install.ok ? "success" : "error");
|
|
4320
|
+
if (!install.ok) {
|
|
4321
|
+
installFailed = true;
|
|
4322
|
+
logger.warn(
|
|
4323
|
+
{ useCase: currentUseCase, output: install.output },
|
|
4324
|
+
"implement: dependency install in worktree failed; generated commands may not run until deps are installed"
|
|
4325
|
+
);
|
|
4326
|
+
}
|
|
4327
|
+
return result;
|
|
4380
4328
|
}
|
|
4381
4329
|
async function runVerificationUseCase() {
|
|
4382
4330
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
@@ -4384,55 +4332,111 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4384
4332
|
return runAgent({
|
|
4385
4333
|
instructions: buildAgentInstructions("verification", input),
|
|
4386
4334
|
tools: toolsForUseCase("verification"),
|
|
4387
|
-
outputSchema: verificationOutputSchema
|
|
4388
|
-
toolContext: searchTools
|
|
4335
|
+
outputSchema: verificationOutputSchema
|
|
4389
4336
|
});
|
|
4390
4337
|
}
|
|
4391
4338
|
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
|
-
|
|
4339
|
+
const { summary, runtime, entrypoint } = await runImplementationUseCase("ingestion");
|
|
4340
|
+
summaries.push(formatSummary("ingestion", summary));
|
|
4341
|
+
ingestRuntime = runtime;
|
|
4342
|
+
ingestEntrypoint = entrypoint;
|
|
4343
|
+
if (ingestRuntime && ingestEntrypoint && !installFailed) {
|
|
4344
|
+
ctx.clearNotices();
|
|
4345
|
+
const runNow = await ctx.requestUserInput({
|
|
4346
|
+
prompt: `Run the ingestion script now? This writes records to the "${targetIndex}" index.`,
|
|
4347
|
+
promptType: "acceptReject",
|
|
4348
|
+
options: ["Yes", "No"],
|
|
4349
|
+
messages: []
|
|
4350
|
+
}) === true;
|
|
4351
|
+
if (runNow) {
|
|
4352
|
+
const ingestApp = await requireApplication();
|
|
4353
|
+
const writeKey = (await resolveWriteKey(targetIndex, ingestApp.id)).key;
|
|
4354
|
+
ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
|
|
4355
|
+
const scriptLogId = ctx.logStart("runIngestScript", {
|
|
4356
|
+
runtime: ingestRuntime,
|
|
4357
|
+
entrypoint: ingestEntrypoint
|
|
4410
4358
|
});
|
|
4359
|
+
const startedAt = Date.now();
|
|
4360
|
+
const run2 = await runIngestScript(
|
|
4361
|
+
worktree,
|
|
4362
|
+
ingestRuntime,
|
|
4363
|
+
ingestEntrypoint,
|
|
4364
|
+
{
|
|
4365
|
+
[APP_ID_VAR]: ingestApp.id,
|
|
4366
|
+
[API_KEY_VAR]: writeKey,
|
|
4367
|
+
[INDEX_NAME_VAR]: targetIndex
|
|
4368
|
+
}
|
|
4369
|
+
);
|
|
4370
|
+
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
4371
|
+
ingestScriptRan = run2.ran && run2.ok;
|
|
4372
|
+
if (ingestScriptRan) {
|
|
4373
|
+
ingestDurationMs = Date.now() - startedAt;
|
|
4374
|
+
ingestRecordCount = parseIngestRecordCount(run2.output);
|
|
4375
|
+
if (ingestRecordCount != null) {
|
|
4376
|
+
track("AI Wizard Ingest Successful", {
|
|
4377
|
+
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4378
|
+
record_count: ingestRecordCount,
|
|
4379
|
+
duration_ms: ingestDurationMs
|
|
4380
|
+
});
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
let summaryLine;
|
|
4384
|
+
let outcomeMessage;
|
|
4385
|
+
if (!run2.ran) {
|
|
4386
|
+
summaryLine = `\u26A0\uFE0F Skipped running the ingestion script: ${run2.reason}`;
|
|
4387
|
+
outcomeMessage = `\u26A0\uFE0F The ingestion script did not run: ${run2.reason}`;
|
|
4388
|
+
logger.warn(
|
|
4389
|
+
{
|
|
4390
|
+
runtime: ingestRuntime,
|
|
4391
|
+
entrypoint: ingestEntrypoint,
|
|
4392
|
+
reason: run2.reason
|
|
4393
|
+
},
|
|
4394
|
+
"implement: refused to auto-run ingestion script"
|
|
4395
|
+
);
|
|
4396
|
+
track("Error", {
|
|
4397
|
+
step: "Push Data",
|
|
4398
|
+
error: `ingestion script skipped: ${run2.reason}`,
|
|
4399
|
+
product_area: "AI Wizard"
|
|
4400
|
+
});
|
|
4401
|
+
} else if (run2.ok) {
|
|
4402
|
+
const status = "Ingestion run: succeeded.";
|
|
4403
|
+
summaryLine = run2.output ? `${status}
|
|
4404
|
+
${run2.output}` : status;
|
|
4405
|
+
outcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4406
|
+
} else {
|
|
4407
|
+
const status = "\u26A0\uFE0F Ingestion run failed:";
|
|
4408
|
+
summaryLine = run2.output ? `${status}
|
|
4409
|
+
${run2.output}` : status;
|
|
4410
|
+
outcomeMessage = `\u274C Ingestion failed.${run2.output ? ` ${run2.output}` : ""}`;
|
|
4411
|
+
logger.warn(
|
|
4412
|
+
{
|
|
4413
|
+
runtime: ingestRuntime,
|
|
4414
|
+
entrypoint: ingestEntrypoint,
|
|
4415
|
+
output: run2.output
|
|
4416
|
+
},
|
|
4417
|
+
"implement: ingestion script run failed"
|
|
4418
|
+
);
|
|
4419
|
+
track("Error", {
|
|
4420
|
+
step: "Push Data",
|
|
4421
|
+
error: run2.output || "ingestion script exited non-zero",
|
|
4422
|
+
product_area: "AI Wizard"
|
|
4423
|
+
});
|
|
4424
|
+
}
|
|
4425
|
+
summaries.push(summaryLine);
|
|
4426
|
+
ingestOutcomeMessage = outcomeMessage;
|
|
4411
4427
|
}
|
|
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
4428
|
}
|
|
4427
4429
|
const commandMessages = [`Open the worktree: cd ${shellQuote(worktree)}`];
|
|
4428
|
-
if (
|
|
4429
|
-
commandMessages.push(
|
|
4430
|
+
if (ingestRuntime && ingestEntrypoint) {
|
|
4431
|
+
commandMessages.push(
|
|
4432
|
+
`Ingestion command: ${buildIngestCommand(worktree, ingestRuntime, ingestEntrypoint)}`
|
|
4433
|
+
);
|
|
4430
4434
|
}
|
|
4431
4435
|
await ctx.requestUserInput({
|
|
4432
4436
|
prompt: "",
|
|
4433
4437
|
promptType: "enterToContinue",
|
|
4434
4438
|
options: [],
|
|
4435
|
-
messages: [ingestOutcomeMessage, ...commandMessages]
|
|
4439
|
+
messages: ingestOutcomeMessage ? [ingestOutcomeMessage, ...commandMessages] : commandMessages
|
|
4436
4440
|
});
|
|
4437
4441
|
}
|
|
4438
4442
|
if (useCases.includes("search")) {
|
|
@@ -4549,13 +4553,22 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4549
4553
|
"implement: agent reported success but no files changed in the worktree"
|
|
4550
4554
|
);
|
|
4551
4555
|
}
|
|
4556
|
+
if (installFailed) {
|
|
4557
|
+
summaries.push(
|
|
4558
|
+
'\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".'
|
|
4559
|
+
);
|
|
4560
|
+
}
|
|
4552
4561
|
return {
|
|
4553
4562
|
ingestionSource,
|
|
4554
4563
|
filesChanged,
|
|
4555
4564
|
summary: summaries.join("\n\n"),
|
|
4556
4565
|
worktreePath: worktree,
|
|
4557
|
-
...useCases.includes("ingestion") &&
|
|
4558
|
-
ingestCommand
|
|
4566
|
+
...useCases.includes("ingestion") && ingestRuntime && ingestEntrypoint ? {
|
|
4567
|
+
ingestCommand: buildIngestCommand(
|
|
4568
|
+
worktree,
|
|
4569
|
+
ingestRuntime,
|
|
4570
|
+
ingestEntrypoint
|
|
4571
|
+
),
|
|
4559
4572
|
ingestScriptRan,
|
|
4560
4573
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4561
4574
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
@@ -4603,8 +4616,8 @@ var defaultWorkflow = {
|
|
|
4603
4616
|
defineStep({
|
|
4604
4617
|
id: "select-index",
|
|
4605
4618
|
title: "Set up index",
|
|
4606
|
-
outputSchema:
|
|
4607
|
-
selection:
|
|
4619
|
+
outputSchema: z28.object({
|
|
4620
|
+
selection: z28.string()
|
|
4608
4621
|
}),
|
|
4609
4622
|
run: (ctx) => selectIndexStep(ctx)
|
|
4610
4623
|
}),
|
|
@@ -4846,7 +4859,10 @@ Options:
|
|
|
4846
4859
|
--no-telemetry Send no telemetry or analytics for this run.
|
|
4847
4860
|
--reset-on-run Wipe this project's wizard state (run state, AI consent,
|
|
4848
4861
|
worktrees) before starting, so the run behaves like a
|
|
4849
|
-
first-ever run.
|
|
4862
|
+
first-ever run. Also drops every API key the wizard has
|
|
4863
|
+
stored in your keychain, for this project and any other, so
|
|
4864
|
+
later runs create new ones. Your Algolia login is not
|
|
4865
|
+
touched.
|
|
4850
4866
|
-h, --help Print this message.`;
|
|
4851
4867
|
function parseCliArgs(argv) {
|
|
4852
4868
|
const positionals = [];
|
|
@@ -4883,11 +4899,11 @@ function parseCliArgs(argv) {
|
|
|
4883
4899
|
|
|
4884
4900
|
// src/lib/resetState.ts
|
|
4885
4901
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4886
|
-
import { join as
|
|
4902
|
+
import { join as join11 } from "node:path";
|
|
4887
4903
|
var KEEP = ["wizard.log"];
|
|
4888
4904
|
async function resetProjectState() {
|
|
4889
4905
|
const dir = stateDir();
|
|
4890
|
-
forgetResolvedKeys();
|
|
4906
|
+
await forgetResolvedKeys();
|
|
4891
4907
|
let entries;
|
|
4892
4908
|
try {
|
|
4893
4909
|
entries = await readdir4(dir);
|
|
@@ -4897,14 +4913,14 @@ async function resetProjectState() {
|
|
|
4897
4913
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4898
4914
|
await Promise.all(
|
|
4899
4915
|
targets.map(
|
|
4900
|
-
(name) => rm2(
|
|
4916
|
+
(name) => rm2(join11(dir, name), { recursive: true, force: true })
|
|
4901
4917
|
)
|
|
4902
4918
|
);
|
|
4903
4919
|
return { dir, removed: targets };
|
|
4904
4920
|
}
|
|
4905
4921
|
|
|
4906
4922
|
// src/main.tsx
|
|
4907
|
-
import { jsx as
|
|
4923
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
4908
4924
|
async function startup() {
|
|
4909
4925
|
setProjectRoot(process.cwd());
|
|
4910
4926
|
let args;
|
|
@@ -4954,7 +4970,7 @@ ${formatStepList(workflow)}`);
|
|
|
4954
4970
|
}
|
|
4955
4971
|
async function run(workflow) {
|
|
4956
4972
|
const store = useWizard.getState();
|
|
4957
|
-
const instance = render(/* @__PURE__ */
|
|
4973
|
+
const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4958
4974
|
await store.waitForStart();
|
|
4959
4975
|
let user = await getUser();
|
|
4960
4976
|
if (!user) {
|