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