@algolia/wizard 0.9.0-rc.87.83 → 0.9.0-rc.88.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/ui/App.tsx
|
|
7
|
-
import { Box as
|
|
7
|
+
import { Box as Box15, Text as Text15, useApp, useInput as useInput6, useWindowSize as useWindowSize8 } from "ink";
|
|
8
8
|
|
|
9
9
|
// src/core/store.ts
|
|
10
10
|
import { create } from "zustand";
|
|
@@ -13,51 +13,15 @@ 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
|
-
function projectSlug(cwd = process.cwd()) {
|
|
30
|
-
return resolve(cwd).replace(/[/\\:]+/g, "-").replace(/^-+/, "") || "root";
|
|
31
|
-
}
|
|
32
|
-
function stateDir(cwd = process.cwd()) {
|
|
33
|
-
return join(rootDir(), projectSlug(cwd));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/lib/logger.ts
|
|
37
|
-
var STDERR_FD = 2;
|
|
38
|
-
function resolveDest() {
|
|
39
|
-
const target = process.env.VITEST ? devNull : process.env.WIZARD_LOG ?? join2(stateDir(), "wizard.log");
|
|
40
|
-
try {
|
|
41
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
42
|
-
closeSync(openSync(target, "a"));
|
|
43
|
-
return target;
|
|
44
|
-
} catch {
|
|
45
|
-
return STDERR_FD;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function logDestination() {
|
|
49
|
-
return pino.destination({ dest: resolveDest(), sync: false });
|
|
50
|
-
}
|
|
51
|
-
var logger = pino(
|
|
52
|
-
{ level: process.env.LOG_LEVEL ?? "info" },
|
|
53
|
-
logDestination()
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
// src/lib/algoliaCli.ts
|
|
57
16
|
function npxArgs(args) {
|
|
58
17
|
return ["--yes", "@algolia/cli@latest", ...args];
|
|
59
18
|
}
|
|
60
19
|
var shell = process.platform === "win32";
|
|
20
|
+
function childEnv(withoutAdminKey) {
|
|
21
|
+
if (!withoutAdminKey) return void 0;
|
|
22
|
+
const { ALGOLIA_API_KEY: _adminKey, ...rest } = process.env;
|
|
23
|
+
return rest;
|
|
24
|
+
}
|
|
61
25
|
function lineSplitter(emit) {
|
|
62
26
|
let buffer = "";
|
|
63
27
|
return {
|
|
@@ -81,11 +45,14 @@ var stderrSink = (stream, line) => {
|
|
|
81
45
|
if (stream === "stdout") return;
|
|
82
46
|
wizardSink(stream, line);
|
|
83
47
|
};
|
|
84
|
-
function runAlgoliaCli(args, { onOutput } = {}) {
|
|
48
|
+
function runAlgoliaCli(args, { onOutput, withoutAdminKey } = {}) {
|
|
85
49
|
const store = useWizard.getState();
|
|
86
50
|
const logId = store.logStart("tool", `algolia ${args.join(" ")}`);
|
|
87
51
|
return new Promise((resolve4, reject) => {
|
|
88
|
-
const child = spawn("npx", npxArgs(args), {
|
|
52
|
+
const child = spawn("npx", npxArgs(args), {
|
|
53
|
+
shell,
|
|
54
|
+
env: childEnv(withoutAdminKey)
|
|
55
|
+
});
|
|
89
56
|
let stdout = "";
|
|
90
57
|
let stderr = "";
|
|
91
58
|
const splitters = {
|
|
@@ -130,7 +97,6 @@ function runAlgoliaCli(args, { onOutput } = {}) {
|
|
|
130
97
|
},
|
|
131
98
|
(err) => {
|
|
132
99
|
useWizard.getState().logEnd(logId, "error");
|
|
133
|
-
logger.warn({ err, args }, "Algolia CLI command failed");
|
|
134
100
|
throw err;
|
|
135
101
|
}
|
|
136
102
|
);
|
|
@@ -188,6 +154,49 @@ function refreshAuthToken() {
|
|
|
188
154
|
return inFlightRefresh;
|
|
189
155
|
}
|
|
190
156
|
|
|
157
|
+
// src/lib/logger.ts
|
|
158
|
+
import pino from "pino";
|
|
159
|
+
import { join as join2, dirname } from "node:path";
|
|
160
|
+
import { devNull } from "node:os";
|
|
161
|
+
import { mkdirSync, openSync, closeSync } from "node:fs";
|
|
162
|
+
|
|
163
|
+
// src/core/constants.ts
|
|
164
|
+
import { homedir } from "node:os";
|
|
165
|
+
import { join, resolve } from "node:path";
|
|
166
|
+
function rootDir() {
|
|
167
|
+
return process.env.WIZARD_HOME ?? join(homedir(), ".algolia");
|
|
168
|
+
}
|
|
169
|
+
var pinnedRoot;
|
|
170
|
+
function setProjectRoot(cwd) {
|
|
171
|
+
pinnedRoot = resolve(cwd);
|
|
172
|
+
}
|
|
173
|
+
function projectSlug(cwd = pinnedRoot ?? process.cwd()) {
|
|
174
|
+
return resolve(cwd).replace(/[/\\:]+/g, "-").replace(/^-+/, "") || "root";
|
|
175
|
+
}
|
|
176
|
+
function stateDir(cwd = pinnedRoot ?? process.cwd()) {
|
|
177
|
+
return join(rootDir(), projectSlug(cwd));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/lib/logger.ts
|
|
181
|
+
var STDERR_FD = 2;
|
|
182
|
+
function resolveDest() {
|
|
183
|
+
const target = process.env.VITEST ? devNull : process.env.WIZARD_LOG ?? join2(stateDir(), "wizard.log");
|
|
184
|
+
try {
|
|
185
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
186
|
+
closeSync(openSync(target, "a"));
|
|
187
|
+
return target;
|
|
188
|
+
} catch {
|
|
189
|
+
return STDERR_FD;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function logDestination() {
|
|
193
|
+
return pino.destination({ dest: resolveDest(), sync: false });
|
|
194
|
+
}
|
|
195
|
+
var logger = pino(
|
|
196
|
+
{ level: process.env.LOG_LEVEL ?? "info" },
|
|
197
|
+
logDestination()
|
|
198
|
+
);
|
|
199
|
+
|
|
191
200
|
// src/lib/proxyFetch.ts
|
|
192
201
|
var PROXY_BASE_URL = process.env.PROXY_BASE_URL ?? "https://proxy-624203421261.us-east4.run.app";
|
|
193
202
|
var PROXY_AUTH_REJECTED_HEADER = "x-wizard-proxy-auth";
|
|
@@ -617,13 +626,10 @@ function Notices() {
|
|
|
617
626
|
}
|
|
618
627
|
|
|
619
628
|
// src/ui/PromptInput.tsx
|
|
620
|
-
import { Box as
|
|
629
|
+
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
621
630
|
import TextInput from "ink-text-input";
|
|
622
631
|
import { useState as useState5 } from "react";
|
|
623
632
|
|
|
624
|
-
// src/ui/CommandApproval.tsx
|
|
625
|
-
import { Box as Box5, Text as Text5, useInput } from "ink";
|
|
626
|
-
|
|
627
633
|
// src/ui/NextAction.tsx
|
|
628
634
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
629
635
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -647,69 +653,14 @@ function NextAction({
|
|
|
647
653
|
] });
|
|
648
654
|
}
|
|
649
655
|
|
|
650
|
-
// src/ui/CommandApproval.tsx
|
|
651
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
652
|
-
function CommandApproval({
|
|
653
|
-
command,
|
|
654
|
-
onDecide
|
|
655
|
-
}) {
|
|
656
|
-
useInput((input, key) => {
|
|
657
|
-
if (key.return) onDecide("approve");
|
|
658
|
-
else if (key.escape) onDecide("reject");
|
|
659
|
-
else if (input.toLowerCase() === "a") onDecide("always");
|
|
660
|
-
});
|
|
661
|
-
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
|
|
662
|
-
/* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, bold: true, children: "Run this command?" }),
|
|
663
|
-
/* @__PURE__ */ jsxs4(
|
|
664
|
-
Box5,
|
|
665
|
-
{
|
|
666
|
-
flexDirection: "column",
|
|
667
|
-
paddingLeft: 2,
|
|
668
|
-
borderStyle: "single",
|
|
669
|
-
borderColor: COLORS.success,
|
|
670
|
-
borderTop: false,
|
|
671
|
-
borderBottom: false,
|
|
672
|
-
borderRight: false,
|
|
673
|
-
gap: 1,
|
|
674
|
-
children: [
|
|
675
|
-
/* @__PURE__ */ jsxs4(Box5, { children: [
|
|
676
|
-
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "$ " }),
|
|
677
|
-
/* @__PURE__ */ jsx4(Text5, { color: COLORS.strong, wrap: "wrap", children: command.command })
|
|
678
|
-
] }),
|
|
679
|
-
/* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
680
|
-
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "in:" }),
|
|
681
|
-
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
|
|
682
|
-
] }),
|
|
683
|
-
command.explanation && /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
684
|
-
/* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: "why:" }),
|
|
685
|
-
/* @__PURE__ */ jsx4(Text5, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
|
|
686
|
-
] })
|
|
687
|
-
]
|
|
688
|
-
}
|
|
689
|
-
),
|
|
690
|
-
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
691
|
-
/* @__PURE__ */ jsx4(NextAction, { action: "approve", keyHint: "enter" }),
|
|
692
|
-
/* @__PURE__ */ jsx4(NextAction, { action: "reject", keyHint: "esc", hierarchy: "secondary" }),
|
|
693
|
-
/* @__PURE__ */ jsx4(
|
|
694
|
-
NextAction,
|
|
695
|
-
{
|
|
696
|
-
action: "approve, and don't ask again for this command",
|
|
697
|
-
keyHint: "a",
|
|
698
|
-
hierarchy: "secondary"
|
|
699
|
-
}
|
|
700
|
-
)
|
|
701
|
-
] })
|
|
702
|
-
] });
|
|
703
|
-
}
|
|
704
|
-
|
|
705
656
|
// src/ui/SelectPrompt.tsx
|
|
706
|
-
import { Box as
|
|
657
|
+
import { Box as Box6, Text as Text6, useInput, useWindowSize as useWindowSize5 } from "ink";
|
|
707
658
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
708
659
|
|
|
709
660
|
// src/ui/ScrollView.tsx
|
|
710
|
-
import { Box as
|
|
661
|
+
import { Box as Box5, Text as Text5, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
|
|
711
662
|
import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
712
|
-
import { jsxs as
|
|
663
|
+
import { jsxs as jsxs4 } from "react/jsx-runtime";
|
|
713
664
|
var INDICATOR_ROWS = 2;
|
|
714
665
|
function fittedWidth(node, columns) {
|
|
715
666
|
let left = 0;
|
|
@@ -779,14 +730,14 @@ function useScrollWindow({
|
|
|
779
730
|
};
|
|
780
731
|
}
|
|
781
732
|
function ScrollView({ scroll, children }) {
|
|
782
|
-
return /* @__PURE__ */
|
|
783
|
-
scroll.hiddenAbove > 0 && /* @__PURE__ */
|
|
733
|
+
return /* @__PURE__ */ jsxs4(Box5, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
734
|
+
scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
784
735
|
"\u2191 ",
|
|
785
736
|
scroll.hiddenAbove,
|
|
786
737
|
" more"
|
|
787
738
|
] }),
|
|
788
739
|
children,
|
|
789
|
-
scroll.hiddenBelow > 0 && /* @__PURE__ */
|
|
740
|
+
scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
790
741
|
"\u2193 ",
|
|
791
742
|
scroll.hiddenBelow,
|
|
792
743
|
" more"
|
|
@@ -795,7 +746,7 @@ function ScrollView({ scroll, children }) {
|
|
|
795
746
|
}
|
|
796
747
|
|
|
797
748
|
// src/ui/SelectPrompt.tsx
|
|
798
|
-
import { jsx as
|
|
749
|
+
import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
799
750
|
var CANCEL = "cancel";
|
|
800
751
|
var ARROW_WIDTH = 4;
|
|
801
752
|
var COLUMN_GAP = 2;
|
|
@@ -857,7 +808,7 @@ function SelectPrompt({
|
|
|
857
808
|
revealIndex(index);
|
|
858
809
|
}, [index, revealIndex]);
|
|
859
810
|
const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
860
|
-
|
|
811
|
+
useInput((input, key) => {
|
|
861
812
|
if (rows.length === 0) return;
|
|
862
813
|
if (key.upArrow || input === "k") {
|
|
863
814
|
setIndex((i) => (i - 1 + rows.length) % rows.length);
|
|
@@ -880,56 +831,56 @@ function SelectPrompt({
|
|
|
880
831
|
}
|
|
881
832
|
}
|
|
882
833
|
});
|
|
883
|
-
return /* @__PURE__ */
|
|
884
|
-
/* @__PURE__ */
|
|
885
|
-
error && /* @__PURE__ */
|
|
886
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
887
|
-
table && /* @__PURE__ */
|
|
888
|
-
/* @__PURE__ */
|
|
889
|
-
question && /* @__PURE__ */
|
|
890
|
-
helpText && /* @__PURE__ */
|
|
834
|
+
return /* @__PURE__ */ jsx4(Box6, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, width, children: [
|
|
835
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
|
|
836
|
+
error && /* @__PURE__ */ jsx4(Text6, { color: COLORS.danger, children: error }),
|
|
837
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
838
|
+
table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
|
|
839
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
840
|
+
question && /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: question }),
|
|
841
|
+
helpText && /* @__PURE__ */ jsx4(Text6, { color: COLORS.dim, children: helpText })
|
|
891
842
|
] })
|
|
892
843
|
] }),
|
|
893
|
-
/* @__PURE__ */
|
|
844
|
+
/* @__PURE__ */ jsx4(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
|
|
894
845
|
const i = scroll.offset + visibleIndex;
|
|
895
846
|
const highlighted = i === index;
|
|
896
847
|
const isCancel = i === cancelIndex;
|
|
897
848
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
898
849
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
899
850
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
900
|
-
const label = /* @__PURE__ */
|
|
851
|
+
const label = /* @__PURE__ */ jsxs5(Text6, { color: labelColor, wrap: "truncate", children: [
|
|
901
852
|
highlighted ? "\u276F " : " ",
|
|
902
853
|
bullet,
|
|
903
854
|
option
|
|
904
855
|
] });
|
|
905
856
|
const isText = sec?.kind === "text";
|
|
906
|
-
return /* @__PURE__ */
|
|
907
|
-
|
|
857
|
+
return /* @__PURE__ */ jsxs5(
|
|
858
|
+
Box6,
|
|
908
859
|
{
|
|
909
860
|
width: isText ? "100%" : barWidth,
|
|
910
861
|
paddingX: 1,
|
|
911
862
|
paddingY: 1,
|
|
912
863
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
913
864
|
children: [
|
|
914
|
-
/* @__PURE__ */
|
|
915
|
-
isText && textWidth > 0 && /* @__PURE__ */
|
|
916
|
-
|
|
865
|
+
/* @__PURE__ */ jsx4(Box6, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
866
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box6, { width: textWidth, children: /* @__PURE__ */ jsx4(
|
|
867
|
+
Text6,
|
|
917
868
|
{
|
|
918
869
|
wrap: "truncate",
|
|
919
870
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
920
871
|
children: sec.value
|
|
921
872
|
}
|
|
922
873
|
) }),
|
|
923
|
-
sec?.kind === "badge" && /* @__PURE__ */
|
|
874
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box6, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text6, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
924
875
|
]
|
|
925
876
|
},
|
|
926
877
|
`row-${i}`
|
|
927
878
|
);
|
|
928
879
|
}) }),
|
|
929
|
-
/* @__PURE__ */
|
|
880
|
+
/* @__PURE__ */ jsx4(Box6, { flexShrink: 0, children: /* @__PURE__ */ jsx4(Text6, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
930
881
|
i > 0 ? " " : "",
|
|
931
|
-
/* @__PURE__ */
|
|
932
|
-
/* @__PURE__ */
|
|
882
|
+
/* @__PURE__ */ jsx4(Text6, { color: COLORS.primary, children: key }),
|
|
883
|
+
/* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
|
|
933
884
|
" ",
|
|
934
885
|
label
|
|
935
886
|
] })
|
|
@@ -938,23 +889,23 @@ function SelectPrompt({
|
|
|
938
889
|
}
|
|
939
890
|
|
|
940
891
|
// src/ui/PromptInput.tsx
|
|
941
|
-
import { jsx as
|
|
892
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
942
893
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
943
894
|
function EnterToContinuePrompt({
|
|
944
895
|
question,
|
|
945
896
|
messages,
|
|
946
897
|
onDecide
|
|
947
898
|
}) {
|
|
948
|
-
|
|
899
|
+
useInput2((_input, key) => {
|
|
949
900
|
if (key.return) onDecide(true);
|
|
950
901
|
else if (key.escape) onDecide(false);
|
|
951
902
|
});
|
|
952
|
-
return /* @__PURE__ */
|
|
953
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
954
|
-
question && /* @__PURE__ */
|
|
955
|
-
/* @__PURE__ */
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
/* @__PURE__ */
|
|
903
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, children: [
|
|
904
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
905
|
+
question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: question }),
|
|
906
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
|
|
907
|
+
/* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
|
|
908
|
+
/* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
958
909
|
] })
|
|
959
910
|
] });
|
|
960
911
|
}
|
|
@@ -962,11 +913,11 @@ function PromptInput() {
|
|
|
962
913
|
const { phase, inputReq, submitInput } = useWizard();
|
|
963
914
|
const [draft, setDraft] = useState5("");
|
|
964
915
|
if (phase === "done" || phase === "error") {
|
|
965
|
-
return /* @__PURE__ */
|
|
916
|
+
return /* @__PURE__ */ jsx5(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text7, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
966
917
|
}
|
|
967
918
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
968
919
|
if (inputReq.promptType === "multipleChoice") {
|
|
969
|
-
return /* @__PURE__ */
|
|
920
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
970
921
|
SelectPrompt,
|
|
971
922
|
{
|
|
972
923
|
question: inputReq.prompt,
|
|
@@ -983,7 +934,7 @@ function PromptInput() {
|
|
|
983
934
|
) });
|
|
984
935
|
}
|
|
985
936
|
if (inputReq.promptType === "multiSelect") {
|
|
986
|
-
return /* @__PURE__ */
|
|
937
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
987
938
|
SelectPrompt,
|
|
988
939
|
{
|
|
989
940
|
multi: true,
|
|
@@ -998,7 +949,7 @@ function PromptInput() {
|
|
|
998
949
|
) });
|
|
999
950
|
}
|
|
1000
951
|
if (inputReq.promptType === "notice") {
|
|
1001
|
-
return /* @__PURE__ */
|
|
952
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
1002
953
|
SelectPrompt,
|
|
1003
954
|
{
|
|
1004
955
|
question: inputReq.prompt,
|
|
@@ -1009,7 +960,7 @@ function PromptInput() {
|
|
|
1009
960
|
) });
|
|
1010
961
|
}
|
|
1011
962
|
if (inputReq.promptType === "enterToContinue") {
|
|
1012
|
-
return /* @__PURE__ */
|
|
963
|
+
return /* @__PURE__ */ jsx5(
|
|
1013
964
|
EnterToContinuePrompt,
|
|
1014
965
|
{
|
|
1015
966
|
question: inputReq.prompt,
|
|
@@ -1018,12 +969,9 @@ function PromptInput() {
|
|
|
1018
969
|
}
|
|
1019
970
|
);
|
|
1020
971
|
}
|
|
1021
|
-
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1022
|
-
return /* @__PURE__ */ jsx6(CommandApproval, { command: inputReq.command, onDecide: submitInput });
|
|
1023
|
-
}
|
|
1024
972
|
if (inputReq.promptType === "acceptReject") {
|
|
1025
973
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
1026
|
-
return /* @__PURE__ */
|
|
974
|
+
return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
1027
975
|
SelectPrompt,
|
|
1028
976
|
{
|
|
1029
977
|
question: inputReq.prompt,
|
|
@@ -1034,15 +982,15 @@ function PromptInput() {
|
|
|
1034
982
|
}
|
|
1035
983
|
) });
|
|
1036
984
|
}
|
|
1037
|
-
return /* @__PURE__ */
|
|
1038
|
-
inputReq.error && /* @__PURE__ */
|
|
1039
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
1040
|
-
/* @__PURE__ */
|
|
1041
|
-
/* @__PURE__ */
|
|
985
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
986
|
+
inputReq.error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: inputReq.error }),
|
|
987
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
988
|
+
/* @__PURE__ */ jsxs6(Box7, { children: [
|
|
989
|
+
/* @__PURE__ */ jsxs6(Text7, { color: COLORS.primary, children: [
|
|
1042
990
|
inputReq.prompt,
|
|
1043
991
|
" "
|
|
1044
992
|
] }),
|
|
1045
|
-
/* @__PURE__ */
|
|
993
|
+
/* @__PURE__ */ jsx5(
|
|
1046
994
|
TextInput,
|
|
1047
995
|
{
|
|
1048
996
|
value: draft,
|
|
@@ -1060,7 +1008,7 @@ function PromptInput() {
|
|
|
1060
1008
|
// src/ui/Welcome.tsx
|
|
1061
1009
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1062
1010
|
import { fileURLToPath } from "node:url";
|
|
1063
|
-
import { Box as
|
|
1011
|
+
import { Box as Box8, Spacer, Text as Text8, useInput as useInput3, useWindowSize as useWindowSize6 } from "ink";
|
|
1064
1012
|
|
|
1065
1013
|
// src/ui/copy/welcome.ts
|
|
1066
1014
|
var sidebarItems = [
|
|
@@ -1073,12 +1021,12 @@ var sidebarItems = [
|
|
|
1073
1021
|
description: "push 100 records to Algolia in seconds"
|
|
1074
1022
|
},
|
|
1075
1023
|
{
|
|
1076
|
-
title: "detect your
|
|
1077
|
-
description: "
|
|
1024
|
+
title: "detect your framework",
|
|
1025
|
+
description: "React, Vue, Angular, Vanilla JS"
|
|
1078
1026
|
},
|
|
1079
1027
|
{
|
|
1080
1028
|
title: "scaffold a search UI",
|
|
1081
|
-
description: "a
|
|
1029
|
+
description: "a styled InstantSearch component, wired into your app"
|
|
1082
1030
|
},
|
|
1083
1031
|
{
|
|
1084
1032
|
title: "ship it",
|
|
@@ -1088,20 +1036,20 @@ var sidebarItems = [
|
|
|
1088
1036
|
|
|
1089
1037
|
// src/ui/Welcome.tsx
|
|
1090
1038
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
1091
|
-
import { jsx as
|
|
1039
|
+
import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1092
1040
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
1093
1041
|
function SidebarItem({
|
|
1094
1042
|
title,
|
|
1095
1043
|
description
|
|
1096
1044
|
}) {
|
|
1097
|
-
return /* @__PURE__ */
|
|
1098
|
-
/* @__PURE__ */
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1045
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1046
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, children: [
|
|
1047
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.success, children: "\u2192" }),
|
|
1048
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.strong, bold: true, children: title })
|
|
1101
1049
|
] }),
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
/* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1050
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 2, children: [
|
|
1051
|
+
/* @__PURE__ */ jsx6(Spacer, {}),
|
|
1052
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: description })
|
|
1105
1053
|
] })
|
|
1106
1054
|
] });
|
|
1107
1055
|
}
|
|
@@ -1109,7 +1057,7 @@ function Welcome() {
|
|
|
1109
1057
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
1110
1058
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
1111
1059
|
const { rows } = useWindowSize6();
|
|
1112
|
-
|
|
1060
|
+
useInput3((input, key) => {
|
|
1113
1061
|
if (key.return) confirmStart();
|
|
1114
1062
|
else if (input === "i") openLearnMore();
|
|
1115
1063
|
});
|
|
@@ -1127,16 +1075,16 @@ function Welcome() {
|
|
|
1127
1075
|
if (rows < 30) {
|
|
1128
1076
|
layout = scales["small"];
|
|
1129
1077
|
}
|
|
1130
|
-
return /* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1132
|
-
|
|
1078
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx6(
|
|
1080
|
+
Box8,
|
|
1133
1081
|
{
|
|
1134
1082
|
paddingY: layout.main.padding.y,
|
|
1135
1083
|
paddingX: layout.main.padding.x,
|
|
1136
1084
|
flexDirection: "column",
|
|
1137
1085
|
justifyContent: "center",
|
|
1138
|
-
children: /* @__PURE__ */
|
|
1139
|
-
/* @__PURE__ */
|
|
1086
|
+
children: /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 2, children: [
|
|
1087
|
+
/* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
|
|
1140
1088
|
Image,
|
|
1141
1089
|
{
|
|
1142
1090
|
src: IMAGE_PATH,
|
|
@@ -1147,16 +1095,16 @@ function Welcome() {
|
|
|
1147
1095
|
protocol: "halfBlock"
|
|
1148
1096
|
}
|
|
1149
1097
|
) }),
|
|
1150
|
-
/* @__PURE__ */
|
|
1151
|
-
/* @__PURE__ */
|
|
1152
|
-
/* @__PURE__ */
|
|
1153
|
-
/* @__PURE__ */
|
|
1098
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1099
|
+
/* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
|
|
1100
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
1101
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
1154
1102
|
] })
|
|
1155
1103
|
] })
|
|
1156
1104
|
}
|
|
1157
1105
|
),
|
|
1158
|
-
/* @__PURE__ */
|
|
1159
|
-
|
|
1106
|
+
/* @__PURE__ */ jsxs7(
|
|
1107
|
+
Box8,
|
|
1160
1108
|
{
|
|
1161
1109
|
backgroundColor: COLORS.bg.sidebar,
|
|
1162
1110
|
width: 40,
|
|
@@ -1166,8 +1114,8 @@ function Welcome() {
|
|
|
1166
1114
|
flexDirection: "column",
|
|
1167
1115
|
justifyContent: "center",
|
|
1168
1116
|
children: [
|
|
1169
|
-
/* @__PURE__ */
|
|
1170
|
-
sidebarItems.map((i, idx) => /* @__PURE__ */
|
|
1117
|
+
/* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
1118
|
+
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
1171
1119
|
]
|
|
1172
1120
|
}
|
|
1173
1121
|
)
|
|
@@ -1176,7 +1124,7 @@ function Welcome() {
|
|
|
1176
1124
|
|
|
1177
1125
|
// src/ui/LearnMore.tsx
|
|
1178
1126
|
import { Fragment as Fragment2 } from "react";
|
|
1179
|
-
import { Box as
|
|
1127
|
+
import { Box as Box9, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize7 } from "ink";
|
|
1180
1128
|
|
|
1181
1129
|
// src/ui/copy/learn-more.ts
|
|
1182
1130
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -1184,17 +1132,12 @@ var accessItems = [
|
|
|
1184
1132
|
{
|
|
1185
1133
|
tag: "READ",
|
|
1186
1134
|
title: "Project files",
|
|
1187
|
-
description: "reads
|
|
1135
|
+
description: "reads package.json, configs & source to detect your stack. Read-only; nothing is uploaded."
|
|
1188
1136
|
},
|
|
1189
1137
|
{
|
|
1190
1138
|
tag: "WRITE",
|
|
1191
1139
|
title: "Code changes",
|
|
1192
|
-
description: "creates & edits files (search UI, config)
|
|
1193
|
-
},
|
|
1194
|
-
{
|
|
1195
|
-
tag: "EXEC",
|
|
1196
|
-
title: "Setup commands",
|
|
1197
|
-
description: "runs dependency installs, the ingestion script & your own checks. Every command is shown in full and needs your OK; its output is shown as-is, so a command that prints a secret will display it."
|
|
1140
|
+
description: "creates & edits files (search UI, config). Shown as a diff first \u2014 nothing lands without your approval."
|
|
1198
1141
|
},
|
|
1199
1142
|
{
|
|
1200
1143
|
tag: "NET",
|
|
@@ -1204,13 +1147,13 @@ var accessItems = [
|
|
|
1204
1147
|
{
|
|
1205
1148
|
tag: "KEY",
|
|
1206
1149
|
title: "Credentials",
|
|
1207
|
-
description: "
|
|
1150
|
+
description: "saves your Admin API key to .env and adds it to .gitignore."
|
|
1208
1151
|
}
|
|
1209
1152
|
];
|
|
1210
1153
|
var neverItems = [
|
|
1211
1154
|
"Send your source code to a model or third party",
|
|
1212
1155
|
"Commit or push to git",
|
|
1213
|
-
"
|
|
1156
|
+
"Touch files outside your project directory"
|
|
1214
1157
|
];
|
|
1215
1158
|
var policyLinks = [
|
|
1216
1159
|
{ label: "Terms", url: "https://www.algolia.com/policies/terms" },
|
|
@@ -1218,11 +1161,10 @@ var policyLinks = [
|
|
|
1218
1161
|
];
|
|
1219
1162
|
|
|
1220
1163
|
// src/ui/LearnMore.tsx
|
|
1221
|
-
import { jsx as
|
|
1164
|
+
import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1222
1165
|
var TAG_COLORS = {
|
|
1223
1166
|
READ: COLORS.success,
|
|
1224
1167
|
WRITE: COLORS.badge,
|
|
1225
|
-
EXEC: COLORS.danger,
|
|
1226
1168
|
NET: COLORS.accent,
|
|
1227
1169
|
KEY: COLORS.muted
|
|
1228
1170
|
};
|
|
@@ -1235,12 +1177,12 @@ function NeverLine({
|
|
|
1235
1177
|
}) {
|
|
1236
1178
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
1237
1179
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
1238
|
-
return /* @__PURE__ */
|
|
1239
|
-
/* @__PURE__ */
|
|
1180
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1181
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" }),
|
|
1240
1182
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
1241
|
-
segments.map((s, i) => /* @__PURE__ */
|
|
1183
|
+
segments.map((s, i) => /* @__PURE__ */ jsx7(Text9, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
1242
1184
|
" ".repeat(rightPad),
|
|
1243
|
-
/* @__PURE__ */
|
|
1185
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" })
|
|
1244
1186
|
] });
|
|
1245
1187
|
}
|
|
1246
1188
|
function LearnMore() {
|
|
@@ -1248,12 +1190,12 @@ function LearnMore() {
|
|
|
1248
1190
|
const backToHome = useWizard((s) => s.backToHome);
|
|
1249
1191
|
const { columns } = useWindowSize7();
|
|
1250
1192
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
1251
|
-
|
|
1193
|
+
useInput4((_input, key) => {
|
|
1252
1194
|
if (key.escape) backToHome();
|
|
1253
1195
|
else if (key.return) confirmStart();
|
|
1254
1196
|
});
|
|
1255
|
-
return /* @__PURE__ */
|
|
1256
|
-
|
|
1197
|
+
return /* @__PURE__ */ jsxs8(
|
|
1198
|
+
Box9,
|
|
1257
1199
|
{
|
|
1258
1200
|
flexDirection: "column",
|
|
1259
1201
|
paddingX: PADDING_X,
|
|
@@ -1261,31 +1203,31 @@ function LearnMore() {
|
|
|
1261
1203
|
width: "100%",
|
|
1262
1204
|
gap: 1,
|
|
1263
1205
|
children: [
|
|
1264
|
-
/* @__PURE__ */
|
|
1265
|
-
/* @__PURE__ */
|
|
1266
|
-
/* @__PURE__ */
|
|
1267
|
-
/* @__PURE__ */
|
|
1268
|
-
/* @__PURE__ */
|
|
1269
|
-
/* @__PURE__ */
|
|
1270
|
-
/* @__PURE__ */
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
/* @__PURE__ */
|
|
1206
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1207
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: accessIntro }),
|
|
1208
|
+
/* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, children: [
|
|
1209
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1210
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1211
|
+
/* @__PURE__ */ jsx7(Box9, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text9, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1212
|
+
/* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
1213
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1214
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
1273
1215
|
] }) })
|
|
1274
1216
|
] })
|
|
1275
1217
|
] }, item.tag)) }),
|
|
1276
|
-
/* @__PURE__ */
|
|
1277
|
-
/* @__PURE__ */
|
|
1278
|
-
/* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1218
|
+
/* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
1219
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
1220
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1221
|
+
/* @__PURE__ */ jsx7(
|
|
1280
1222
|
NeverLine,
|
|
1281
1223
|
{
|
|
1282
1224
|
width: dividerWidth,
|
|
1283
1225
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1284
1226
|
}
|
|
1285
1227
|
),
|
|
1286
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1287
|
-
/* @__PURE__ */
|
|
1288
|
-
/* @__PURE__ */
|
|
1228
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1229
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1230
|
+
/* @__PURE__ */ jsx7(
|
|
1289
1231
|
NeverLine,
|
|
1290
1232
|
{
|
|
1291
1233
|
width: dividerWidth,
|
|
@@ -1297,24 +1239,24 @@ function LearnMore() {
|
|
|
1297
1239
|
}
|
|
1298
1240
|
)
|
|
1299
1241
|
] }, item)),
|
|
1300
|
-
/* @__PURE__ */
|
|
1301
|
-
/* @__PURE__ */
|
|
1242
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1243
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1302
1244
|
] }),
|
|
1303
|
-
/* @__PURE__ */
|
|
1304
|
-
/* @__PURE__ */
|
|
1305
|
-
/* @__PURE__ */
|
|
1245
|
+
/* @__PURE__ */ jsx7(Box9, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
|
|
1246
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1247
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.accent, children: link.url })
|
|
1306
1248
|
] }, link.label)) }),
|
|
1307
|
-
/* @__PURE__ */
|
|
1308
|
-
/* @__PURE__ */
|
|
1309
|
-
/* @__PURE__ */
|
|
1310
|
-
/* @__PURE__ */
|
|
1311
|
-
/* @__PURE__ */
|
|
1249
|
+
/* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1250
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
|
|
1251
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
|
|
1252
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "esc" }),
|
|
1253
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "] back" })
|
|
1312
1254
|
] }),
|
|
1313
|
-
/* @__PURE__ */
|
|
1314
|
-
/* @__PURE__ */
|
|
1315
|
-
/* @__PURE__ */
|
|
1316
|
-
/* @__PURE__ */
|
|
1317
|
-
/* @__PURE__ */
|
|
1255
|
+
/* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
|
|
1256
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
|
|
1257
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "enter" }),
|
|
1258
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "]" }),
|
|
1259
|
+
/* @__PURE__ */ jsx7(Text9, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1318
1260
|
] })
|
|
1319
1261
|
] })
|
|
1320
1262
|
]
|
|
@@ -1323,10 +1265,10 @@ function LearnMore() {
|
|
|
1323
1265
|
}
|
|
1324
1266
|
|
|
1325
1267
|
// src/ui/Sidebar.tsx
|
|
1326
|
-
import { Box as
|
|
1268
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1327
1269
|
|
|
1328
1270
|
// src/ui/Steps.tsx
|
|
1329
|
-
import { Box as
|
|
1271
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1330
1272
|
import Spinner from "ink-spinner";
|
|
1331
1273
|
|
|
1332
1274
|
// src/core/persistence.ts
|
|
@@ -1355,12 +1297,12 @@ async function clearWorkflowState(workflowId) {
|
|
|
1355
1297
|
}
|
|
1356
1298
|
|
|
1357
1299
|
// src/ui/Steps.tsx
|
|
1358
|
-
import { jsx as
|
|
1300
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1359
1301
|
function Steps() {
|
|
1360
1302
|
const { steps } = useWizard();
|
|
1361
1303
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1362
|
-
return /* @__PURE__ */
|
|
1363
|
-
s.status === "running" ? /* @__PURE__ */
|
|
1304
|
+
return /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status[s.status], children: [
|
|
1305
|
+
s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1364
1306
|
" ",
|
|
1365
1307
|
s.title
|
|
1366
1308
|
] }) }, s.id)) });
|
|
@@ -1369,27 +1311,27 @@ function CurrentStep() {
|
|
|
1369
1311
|
const { steps } = useWizard();
|
|
1370
1312
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1371
1313
|
if (!currentStep) return null;
|
|
1372
|
-
return /* @__PURE__ */
|
|
1373
|
-
/* @__PURE__ */
|
|
1314
|
+
return /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status.running, children: [
|
|
1315
|
+
/* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
|
|
1374
1316
|
" ",
|
|
1375
1317
|
` ${currentStep.title}`
|
|
1376
1318
|
] });
|
|
1377
1319
|
}
|
|
1378
1320
|
|
|
1379
1321
|
// src/ui/Progress.tsx
|
|
1380
|
-
import { Box as
|
|
1381
|
-
import { jsx as
|
|
1322
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1323
|
+
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1382
1324
|
function Progress() {
|
|
1383
1325
|
const { steps, currentStepIndex } = useWizard();
|
|
1384
1326
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1385
1327
|
if (visibleSteps.length === 0) return null;
|
|
1386
1328
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1387
1329
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1388
|
-
return /* @__PURE__ */
|
|
1389
|
-
/* @__PURE__ */
|
|
1390
|
-
/* @__PURE__ */
|
|
1391
|
-
/* @__PURE__ */
|
|
1392
|
-
/* @__PURE__ */
|
|
1330
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1331
|
+
/* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "STEP" }),
|
|
1332
|
+
/* @__PURE__ */ jsx9(Text11, { bold: true, children: activeStepNumber }),
|
|
1333
|
+
/* @__PURE__ */ jsx9(Text11, { bold: true, children: "/" }),
|
|
1334
|
+
/* @__PURE__ */ jsx9(Text11, { bold: true, children: visibleSteps.length })
|
|
1393
1335
|
] });
|
|
1394
1336
|
}
|
|
1395
1337
|
|
|
@@ -1400,10 +1342,10 @@ var sidebarCommands = [
|
|
|
1400
1342
|
];
|
|
1401
1343
|
|
|
1402
1344
|
// src/ui/Sidebar.tsx
|
|
1403
|
-
import { jsx as
|
|
1345
|
+
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1404
1346
|
function Sidebar() {
|
|
1405
|
-
return /* @__PURE__ */
|
|
1406
|
-
|
|
1347
|
+
return /* @__PURE__ */ jsxs11(
|
|
1348
|
+
Box12,
|
|
1407
1349
|
{
|
|
1408
1350
|
backgroundColor: "#14171E",
|
|
1409
1351
|
width: 30,
|
|
@@ -1412,16 +1354,16 @@ function Sidebar() {
|
|
|
1412
1354
|
flexDirection: "column",
|
|
1413
1355
|
justifyContent: "space-between",
|
|
1414
1356
|
children: [
|
|
1415
|
-
/* @__PURE__ */
|
|
1416
|
-
/* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
|
|
1358
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1359
|
+
/* @__PURE__ */ jsx10(Steps, {})
|
|
1418
1360
|
] }),
|
|
1419
|
-
/* @__PURE__ */
|
|
1420
|
-
/* @__PURE__ */
|
|
1421
|
-
/* @__PURE__ */
|
|
1422
|
-
return /* @__PURE__ */
|
|
1423
|
-
/* @__PURE__ */
|
|
1424
|
-
/* @__PURE__ */
|
|
1361
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
|
|
1362
|
+
/* @__PURE__ */ jsx10(Progress, {}),
|
|
1363
|
+
/* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1364
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1365
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1366
|
+
/* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: c.description })
|
|
1425
1367
|
] });
|
|
1426
1368
|
}) })
|
|
1427
1369
|
] })
|
|
@@ -1431,12 +1373,12 @@ function Sidebar() {
|
|
|
1431
1373
|
}
|
|
1432
1374
|
|
|
1433
1375
|
// src/ui/Ribbon.tsx
|
|
1434
|
-
import { Box as
|
|
1435
|
-
import { jsx as
|
|
1376
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1377
|
+
import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1436
1378
|
function Ribbon() {
|
|
1437
1379
|
const firstCommand = sidebarCommands[0];
|
|
1438
|
-
return /* @__PURE__ */
|
|
1439
|
-
|
|
1380
|
+
return /* @__PURE__ */ jsxs12(
|
|
1381
|
+
Box13,
|
|
1440
1382
|
{
|
|
1441
1383
|
backgroundColor: "#14171E",
|
|
1442
1384
|
flexDirection: "row",
|
|
@@ -1444,11 +1386,11 @@ function Ribbon() {
|
|
|
1444
1386
|
paddingX: 2,
|
|
1445
1387
|
paddingY: 1,
|
|
1446
1388
|
children: [
|
|
1447
|
-
/* @__PURE__ */
|
|
1448
|
-
/* @__PURE__ */
|
|
1449
|
-
/* @__PURE__ */
|
|
1450
|
-
/* @__PURE__ */
|
|
1451
|
-
/* @__PURE__ */
|
|
1389
|
+
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1390
|
+
/* @__PURE__ */ jsx11(CurrentStep, {}),
|
|
1391
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
|
|
1392
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1393
|
+
/* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: firstCommand.description })
|
|
1452
1394
|
] })
|
|
1453
1395
|
]
|
|
1454
1396
|
}
|
|
@@ -1459,8 +1401,8 @@ function Ribbon() {
|
|
|
1459
1401
|
import { useState as useState6 } from "react";
|
|
1460
1402
|
|
|
1461
1403
|
// src/ui/Logs.tsx
|
|
1462
|
-
import { Box as
|
|
1463
|
-
import { jsx as
|
|
1404
|
+
import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
|
|
1405
|
+
import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1464
1406
|
var KIND_COLOR = {
|
|
1465
1407
|
tool: COLORS.primary,
|
|
1466
1408
|
prompt: COLORS.badge
|
|
@@ -1491,14 +1433,14 @@ function formatTimestamp(ms) {
|
|
|
1491
1433
|
function Logs() {
|
|
1492
1434
|
const logs = useWizard((s) => s.logs);
|
|
1493
1435
|
const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
|
|
1494
|
-
|
|
1436
|
+
useInput5((_input, key) => {
|
|
1495
1437
|
if (key.upArrow) scroll.scrollBy(-1);
|
|
1496
1438
|
else if (key.downArrow) scroll.scrollBy(1);
|
|
1497
1439
|
});
|
|
1498
1440
|
const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
|
|
1499
|
-
return /* @__PURE__ */
|
|
1500
|
-
logs.length === 0 && /* @__PURE__ */
|
|
1501
|
-
/* @__PURE__ */
|
|
1441
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1442
|
+
logs.length === 0 && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "No logs yet." }),
|
|
1443
|
+
/* @__PURE__ */ jsx12(ScrollView, { scroll, children: visible.map((entry) => {
|
|
1502
1444
|
const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
|
|
1503
1445
|
const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
|
|
1504
1446
|
const rawPreview = rawInputText(entry.input);
|
|
@@ -1508,14 +1450,14 @@ function Logs() {
|
|
|
1508
1450
|
const name = truncate2(entry.name, budget);
|
|
1509
1451
|
budget -= name.length;
|
|
1510
1452
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1511
|
-
return /* @__PURE__ */
|
|
1512
|
-
/* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
preview && /* @__PURE__ */
|
|
1515
|
-
durationText && /* @__PURE__ */
|
|
1453
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1454
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: timestamp }),
|
|
1455
|
+
/* @__PURE__ */ jsx12(Text14, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1456
|
+
preview && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1457
|
+
durationText && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: durationText })
|
|
1516
1458
|
] }, entry.id);
|
|
1517
1459
|
}) }),
|
|
1518
|
-
/* @__PURE__ */
|
|
1460
|
+
/* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1519
1461
|
] });
|
|
1520
1462
|
}
|
|
1521
1463
|
|
|
@@ -1707,7 +1649,7 @@ function track(event, payload) {
|
|
|
1707
1649
|
}
|
|
1708
1650
|
|
|
1709
1651
|
// src/ui/App.tsx
|
|
1710
|
-
import { jsx as
|
|
1652
|
+
import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1711
1653
|
function App() {
|
|
1712
1654
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1713
1655
|
const { exit } = useApp();
|
|
@@ -1715,7 +1657,7 @@ function App() {
|
|
|
1715
1657
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1716
1658
|
const finished = phase === "done" || phase === "error";
|
|
1717
1659
|
const currentStep = steps[currentStepIndex];
|
|
1718
|
-
|
|
1660
|
+
useInput6(
|
|
1719
1661
|
(_input, key) => {
|
|
1720
1662
|
if (key.return) {
|
|
1721
1663
|
exit();
|
|
@@ -1723,7 +1665,7 @@ function App() {
|
|
|
1723
1665
|
},
|
|
1724
1666
|
{ isActive: finished }
|
|
1725
1667
|
);
|
|
1726
|
-
|
|
1668
|
+
useInput6((_input, key) => {
|
|
1727
1669
|
if (phase === "idle" || phase === "authenticating") return;
|
|
1728
1670
|
if (key.tab) {
|
|
1729
1671
|
setShowLogs(!showLogs);
|
|
@@ -1734,8 +1676,8 @@ function App() {
|
|
|
1734
1676
|
});
|
|
1735
1677
|
}
|
|
1736
1678
|
});
|
|
1737
|
-
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" &&
|
|
1738
|
-
|
|
1679
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1680
|
+
useInput6((_input, key) => {
|
|
1739
1681
|
if (escOwnedElsewhere) return;
|
|
1740
1682
|
if (key.escape) {
|
|
1741
1683
|
track("AI Wizard Interaction", {
|
|
@@ -1754,8 +1696,8 @@ function App() {
|
|
|
1754
1696
|
/* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
|
|
1755
1697
|
the whole screen, and the scrolling throws off its cursor arithmetic —
|
|
1756
1698
|
flicker and leftover rows. */
|
|
1757
|
-
/* @__PURE__ */
|
|
1758
|
-
|
|
1699
|
+
/* @__PURE__ */ jsxs14(
|
|
1700
|
+
Box15,
|
|
1759
1701
|
{
|
|
1760
1702
|
backgroundColor: COLORS.bg.main,
|
|
1761
1703
|
flexDirection: "row",
|
|
@@ -1763,16 +1705,16 @@ function App() {
|
|
|
1763
1705
|
height: scrollsPastViewport ? void 0 : rows,
|
|
1764
1706
|
overflow: scrollsPastViewport ? "visible" : "hidden",
|
|
1765
1707
|
children: [
|
|
1766
|
-
mainWindowVisible && /* @__PURE__ */
|
|
1767
|
-
|
|
1708
|
+
mainWindowVisible && /* @__PURE__ */ jsxs14(
|
|
1709
|
+
Box15,
|
|
1768
1710
|
{
|
|
1769
1711
|
flexDirection,
|
|
1770
1712
|
width: "100%",
|
|
1771
1713
|
maxHeight: rows,
|
|
1772
1714
|
justifyContent: "space-between",
|
|
1773
1715
|
children: [
|
|
1774
|
-
showLogs ? /* @__PURE__ */
|
|
1775
|
-
|
|
1716
|
+
showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : /* @__PURE__ */ jsxs14(
|
|
1717
|
+
Box15,
|
|
1776
1718
|
{
|
|
1777
1719
|
flexDirection: "column",
|
|
1778
1720
|
paddingX: 4,
|
|
@@ -1780,26 +1722,26 @@ function App() {
|
|
|
1780
1722
|
width: showSidebar ? 70 : "100%",
|
|
1781
1723
|
flexGrow: 1,
|
|
1782
1724
|
children: [
|
|
1783
|
-
phase === "authenticating" && /* @__PURE__ */
|
|
1784
|
-
/* @__PURE__ */
|
|
1785
|
-
/* @__PURE__ */
|
|
1725
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginBottom: 1, children: [
|
|
1726
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1727
|
+
/* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1786
1728
|
] }),
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
/* @__PURE__ */
|
|
1790
|
-
phase === "running" && showSidebar && /* @__PURE__ */
|
|
1791
|
-
phase === "error" && error && /* @__PURE__ */
|
|
1729
|
+
/* @__PURE__ */ jsx13(CliOutput, {}),
|
|
1730
|
+
/* @__PURE__ */ jsx13(Notices, {}),
|
|
1731
|
+
/* @__PURE__ */ jsx13(PromptInput, {}),
|
|
1732
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
|
|
1733
|
+
phase === "error" && error && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { color: COLORS.status.error, children: [
|
|
1792
1734
|
"\u2716 ",
|
|
1793
1735
|
error
|
|
1794
1736
|
] }) })
|
|
1795
1737
|
]
|
|
1796
1738
|
}
|
|
1797
1739
|
),
|
|
1798
|
-
showSidebar ? /* @__PURE__ */
|
|
1740
|
+
showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
|
|
1799
1741
|
]
|
|
1800
1742
|
}
|
|
1801
1743
|
),
|
|
1802
|
-
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */
|
|
1744
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
|
|
1803
1745
|
]
|
|
1804
1746
|
}
|
|
1805
1747
|
)
|
|
@@ -1813,18 +1755,17 @@ import "zod";
|
|
|
1813
1755
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1814
1756
|
import { join as join5 } from "node:path";
|
|
1815
1757
|
var configFile = () => join5(stateDir(), "config.json");
|
|
1816
|
-
var
|
|
1758
|
+
var defaultConfig = () => ({
|
|
1817
1759
|
version: 1,
|
|
1818
1760
|
aiConsent: false,
|
|
1819
|
-
workflowsRun: []
|
|
1820
|
-
|
|
1821
|
-
};
|
|
1761
|
+
workflowsRun: []
|
|
1762
|
+
});
|
|
1822
1763
|
async function loadConfig() {
|
|
1823
1764
|
try {
|
|
1824
1765
|
const raw = await readFile2(configFile(), "utf8");
|
|
1825
|
-
return { ...
|
|
1766
|
+
return { ...defaultConfig(), ...JSON.parse(raw) };
|
|
1826
1767
|
} catch {
|
|
1827
|
-
return
|
|
1768
|
+
return defaultConfig();
|
|
1828
1769
|
}
|
|
1829
1770
|
}
|
|
1830
1771
|
async function saveConfig(config) {
|
|
@@ -1836,38 +1777,6 @@ async function recordWorkflowRun(workflowId, completedAt) {
|
|
|
1836
1777
|
config.workflowsRun.push({ workflowId, completedAt });
|
|
1837
1778
|
await saveConfig(config);
|
|
1838
1779
|
}
|
|
1839
|
-
function isStoredSearchKey(value) {
|
|
1840
|
-
if (typeof value !== "object" || value === null) return false;
|
|
1841
|
-
const { appId, key } = value;
|
|
1842
|
-
return typeof appId === "string" && !!appId && typeof key === "string" && !!key;
|
|
1843
|
-
}
|
|
1844
|
-
function storedSearchKeys(config) {
|
|
1845
|
-
const stored = config.searchApiKeys;
|
|
1846
|
-
if (typeof stored !== "object" || stored === null || Array.isArray(stored)) {
|
|
1847
|
-
return {};
|
|
1848
|
-
}
|
|
1849
|
-
return stored;
|
|
1850
|
-
}
|
|
1851
|
-
async function getStoredSearchKey(index, appId) {
|
|
1852
|
-
const entry = storedSearchKeys(await loadConfig())[index];
|
|
1853
|
-
if (!isStoredSearchKey(entry) || entry.appId !== appId) return void 0;
|
|
1854
|
-
return entry.key;
|
|
1855
|
-
}
|
|
1856
|
-
async function storeSearchKey(index, appId, key) {
|
|
1857
|
-
const config = await loadConfig();
|
|
1858
|
-
config.searchApiKeys = {
|
|
1859
|
-
...storedSearchKeys(config),
|
|
1860
|
-
[index]: { appId, key }
|
|
1861
|
-
};
|
|
1862
|
-
await saveConfig(config);
|
|
1863
|
-
}
|
|
1864
|
-
async function forgetSearchKey(index) {
|
|
1865
|
-
const config = await loadConfig();
|
|
1866
|
-
const remaining = { ...storedSearchKeys(config) };
|
|
1867
|
-
delete remaining[index];
|
|
1868
|
-
config.searchApiKeys = remaining;
|
|
1869
|
-
await saveConfig(config);
|
|
1870
|
-
}
|
|
1871
1780
|
|
|
1872
1781
|
// src/core/orchestrator.ts
|
|
1873
1782
|
function defineStep(step) {
|
|
@@ -1912,7 +1821,7 @@ async function ensureConsent() {
|
|
|
1912
1821
|
if (config.aiConsent) return;
|
|
1913
1822
|
const store = useWizard.getState();
|
|
1914
1823
|
const answer = await store.requestUserInput({
|
|
1915
|
-
prompt: "Wizard will make AI-authored changes to this repository
|
|
1824
|
+
prompt: "Wizard will make AI-authored changes to this repository.",
|
|
1916
1825
|
promptType: "enterToContinue",
|
|
1917
1826
|
options: []
|
|
1918
1827
|
});
|
|
@@ -2315,9 +2224,9 @@ function listFilesTool(ctx) {
|
|
|
2315
2224
|
if (++ctx.counts.list > ctx.limits.list) {
|
|
2316
2225
|
return `Refused: list limit (${ctx.limits.list}) reached. Stop listing and proceed with the information you already have.`;
|
|
2317
2226
|
}
|
|
2318
|
-
const
|
|
2319
|
-
if (!
|
|
2320
|
-
const entries = await readdir(
|
|
2227
|
+
const resolved2 = resolveInRoot(ctx, ".");
|
|
2228
|
+
if (!resolved2.ok) return resolved2.error;
|
|
2229
|
+
const entries = await readdir(resolved2.target, { withFileTypes: true });
|
|
2321
2230
|
return entries.map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
2322
2231
|
}
|
|
2323
2232
|
});
|
|
@@ -2335,14 +2244,14 @@ function changeDirectoryTool(ctx) {
|
|
|
2335
2244
|
}),
|
|
2336
2245
|
execute: async ({ path }) => {
|
|
2337
2246
|
logger.info({ path }, "called changeDirectory tool");
|
|
2338
|
-
const
|
|
2339
|
-
if (!
|
|
2247
|
+
const resolved2 = resolveInRoot(ctx, path);
|
|
2248
|
+
if (!resolved2.ok) return resolved2.error;
|
|
2340
2249
|
try {
|
|
2341
|
-
const info = await stat(
|
|
2250
|
+
const info = await stat(resolved2.target);
|
|
2342
2251
|
if (!info.isDirectory()) {
|
|
2343
2252
|
return `Error changing directory to ${path}: not a directory`;
|
|
2344
2253
|
}
|
|
2345
|
-
ctx.cwd =
|
|
2254
|
+
ctx.cwd = resolved2.target;
|
|
2346
2255
|
return `Changed working directory to ${ctx.cwd}`;
|
|
2347
2256
|
} catch (err) {
|
|
2348
2257
|
return `Error changing directory to ${path}: ${err.message}`;
|
|
@@ -2407,11 +2316,11 @@ function readFileTool(ctx) {
|
|
|
2407
2316
|
return `Refused: read limit (${ctx.limits.read}) reached. Stop reading and proceed with the information you already have.`;
|
|
2408
2317
|
}
|
|
2409
2318
|
logger.info({ filePath }, "called readFile tool");
|
|
2410
|
-
const
|
|
2411
|
-
if (!
|
|
2319
|
+
const resolved2 = resolveInRoot(ctx, filePath);
|
|
2320
|
+
if (!resolved2.ok) return resolved2.error;
|
|
2412
2321
|
try {
|
|
2413
|
-
const content = await readFile3(
|
|
2414
|
-
return isEnvFile(
|
|
2322
|
+
const content = await readFile3(resolved2.target, "utf8");
|
|
2323
|
+
return isEnvFile(resolved2.target) ? redactEnvValues(content) : content;
|
|
2415
2324
|
} catch (err) {
|
|
2416
2325
|
return `Error reading ${filePath}: ${err.message}`;
|
|
2417
2326
|
}
|
|
@@ -2433,17 +2342,17 @@ function writeFileTool(ctx) {
|
|
|
2433
2342
|
}),
|
|
2434
2343
|
execute: async ({ filePath, content }) => {
|
|
2435
2344
|
logger.info({ filePath }, "called writeFile tool");
|
|
2436
|
-
const
|
|
2437
|
-
if (
|
|
2438
|
-
if (isSecretEnvFile(
|
|
2345
|
+
const resolved2 = resolveInRoot(ctx, filePath);
|
|
2346
|
+
if (resolved2.ok === false) return resolved2.error;
|
|
2347
|
+
if (isSecretEnvFile(resolved2.target)) {
|
|
2439
2348
|
return `Refused: ${filePath} holds secrets. Use the writeCredentials tool to set Algolia environment variables, passing this file path.`;
|
|
2440
2349
|
}
|
|
2441
2350
|
try {
|
|
2442
|
-
if (await hasSymlinkParent(ctx,
|
|
2443
|
-
return `Refused: ${
|
|
2351
|
+
if (await hasSymlinkParent(ctx, resolved2.target)) {
|
|
2352
|
+
return `Refused: ${resolved2.target} is outside the repo root (${ctx.root}).`;
|
|
2444
2353
|
}
|
|
2445
|
-
await mkdir3(dirname4(
|
|
2446
|
-
await writeFile3(
|
|
2354
|
+
await mkdir3(dirname4(resolved2.target), { recursive: true });
|
|
2355
|
+
await writeFile3(resolved2.target, content, "utf8");
|
|
2447
2356
|
return `Wrote to ${filePath}`;
|
|
2448
2357
|
} catch (err) {
|
|
2449
2358
|
return `Error writing ${filePath}: ${err.message}`;
|
|
@@ -2460,6 +2369,40 @@ import { dirname as dirname5 } from "node:path";
|
|
|
2460
2369
|
|
|
2461
2370
|
// src/lib/algoliaApiKey.ts
|
|
2462
2371
|
import { z as z11 } from "zod";
|
|
2372
|
+
|
|
2373
|
+
// src/lib/keychain.ts
|
|
2374
|
+
import { getPassword, setPassword } from "cross-keychain";
|
|
2375
|
+
var SERVICE = "algolia-wizard";
|
|
2376
|
+
function account(kind, index, appId) {
|
|
2377
|
+
return `${kind}:${appId}:${index}`;
|
|
2378
|
+
}
|
|
2379
|
+
async function readStoredKey(kind, index, appId) {
|
|
2380
|
+
try {
|
|
2381
|
+
return await getPassword(SERVICE, account(kind, index, appId));
|
|
2382
|
+
} catch (err) {
|
|
2383
|
+
logger.warn(
|
|
2384
|
+
{ err: err.message, kind, index, appId },
|
|
2385
|
+
"could not read the API key from the keychain"
|
|
2386
|
+
);
|
|
2387
|
+
return null;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
async function storeKey(kind, index, appId, value) {
|
|
2391
|
+
const name = account(kind, index, appId);
|
|
2392
|
+
try {
|
|
2393
|
+
await setPassword(SERVICE, name, value);
|
|
2394
|
+
if (await getPassword(SERVICE, name) !== value) {
|
|
2395
|
+
throw new Error("the keychain did not store the value");
|
|
2396
|
+
}
|
|
2397
|
+
} catch (err) {
|
|
2398
|
+
logger.warn(
|
|
2399
|
+
{ err: err.message, kind, index, appId },
|
|
2400
|
+
"could not store the API key in the keychain; the next run will create another"
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
// src/lib/algoliaApiKey.ts
|
|
2463
2406
|
var WRITE_ACLS = [
|
|
2464
2407
|
"addObject",
|
|
2465
2408
|
"deleteObject",
|
|
@@ -2467,81 +2410,27 @@ var WRITE_ACLS = [
|
|
|
2467
2410
|
"editSettings",
|
|
2468
2411
|
"listIndexes"
|
|
2469
2412
|
];
|
|
2470
|
-
var WRITE_ACL_SET = new Set(WRITE_ACLS);
|
|
2471
|
-
var apiKeySchema = z11.object({
|
|
2472
|
-
value: z11.string().min(1),
|
|
2473
|
-
acl: z11.array(z11.string()).default([]),
|
|
2474
|
-
indexes: z11.array(z11.string()).default([])
|
|
2475
|
-
});
|
|
2476
|
-
var apiKeyListSchema = z11.union([
|
|
2477
|
-
z11.array(apiKeySchema),
|
|
2478
|
-
z11.object({
|
|
2479
|
-
items: z11.array(apiKeySchema).optional(),
|
|
2480
|
-
keys: z11.array(apiKeySchema).optional()
|
|
2481
|
-
})
|
|
2482
|
-
]).transform((o) => Array.isArray(o) ? o : o.items ?? o.keys ?? []);
|
|
2483
|
-
function parseCliJson(schema, stdout, command) {
|
|
2484
|
-
let raw;
|
|
2485
|
-
try {
|
|
2486
|
-
raw = JSON.parse(stdout);
|
|
2487
|
-
} catch {
|
|
2488
|
-
throw new Error(`Algolia CLI \`${command}\` returned unreadable JSON.`);
|
|
2489
|
-
}
|
|
2490
|
-
const parsed = schema.safeParse(raw);
|
|
2491
|
-
if (!parsed.success) {
|
|
2492
|
-
throw new Error(
|
|
2493
|
-
`Algolia CLI \`${command}\` returned JSON in an unexpected shape.`
|
|
2494
|
-
);
|
|
2495
|
-
}
|
|
2496
|
-
return parsed.data;
|
|
2497
|
-
}
|
|
2498
2413
|
var createdKeySchema = z11.object({
|
|
2499
2414
|
key: z11.string().min(1).optional(),
|
|
2500
2415
|
value: z11.string().min(1).optional()
|
|
2501
2416
|
}).transform((o) => o.key ?? o.value);
|
|
2502
|
-
function
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
index,
|
|
2520
|
-
"--acl",
|
|
2521
|
-
WRITE_ACLS.join(","),
|
|
2522
|
-
"--description",
|
|
2523
|
-
`wizard write key for ${index}`,
|
|
2524
|
-
"-o",
|
|
2525
|
-
"json"
|
|
2526
|
-
]);
|
|
2527
|
-
const writeKey = parseCliJson(createdKeySchema, created, "apikeys create");
|
|
2528
|
-
if (!writeKey) throw new Error("apikeys create returned no key value");
|
|
2529
|
-
return writeKey;
|
|
2530
|
-
}
|
|
2531
|
-
async function createSearchOnlyKey(index) {
|
|
2532
|
-
logger.info({ index }, "creating a search-only API key");
|
|
2533
|
-
const stdout = await runAlgoliaCli([
|
|
2534
|
-
"apikeys",
|
|
2535
|
-
"create",
|
|
2536
|
-
"--acl",
|
|
2537
|
-
"search",
|
|
2538
|
-
"--indices",
|
|
2539
|
-
index,
|
|
2540
|
-
"--description",
|
|
2541
|
-
`Algolia Wizard search-only key for ${index}`,
|
|
2542
|
-
"-o",
|
|
2543
|
-
"json"
|
|
2544
|
-
]);
|
|
2417
|
+
async function createKey(index, acls, description) {
|
|
2418
|
+
logger.info({ index, acls }, "creating an API key");
|
|
2419
|
+
const stdout = await runAlgoliaCli(
|
|
2420
|
+
[
|
|
2421
|
+
"apikeys",
|
|
2422
|
+
"create",
|
|
2423
|
+
"--acl",
|
|
2424
|
+
acls.join(","),
|
|
2425
|
+
"--indices",
|
|
2426
|
+
index,
|
|
2427
|
+
"--description",
|
|
2428
|
+
description,
|
|
2429
|
+
"-o",
|
|
2430
|
+
"json"
|
|
2431
|
+
],
|
|
2432
|
+
{ withoutAdminKey: true }
|
|
2433
|
+
);
|
|
2545
2434
|
let payload;
|
|
2546
2435
|
try {
|
|
2547
2436
|
payload = JSON.parse(stdout);
|
|
@@ -2552,49 +2441,57 @@ async function createSearchOnlyKey(index) {
|
|
|
2552
2441
|
if (!created) throw new Error("apikeys create returned no key value");
|
|
2553
2442
|
return created;
|
|
2554
2443
|
}
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
return true;
|
|
2559
|
-
} catch (err) {
|
|
2560
|
-
return !/does not exist|not found|404/i.test(err.message);
|
|
2561
|
-
}
|
|
2444
|
+
var resolved = /* @__PURE__ */ new Map();
|
|
2445
|
+
function forgetResolvedKeys() {
|
|
2446
|
+
resolved.clear();
|
|
2562
2447
|
}
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
logger.info({ index, appId }, "reusing the stored search-only API key");
|
|
2572
|
-
return { key: stored, source: "config" };
|
|
2448
|
+
function resolveKey(kind, index, appId, acls, description) {
|
|
2449
|
+
const cacheKey = `${kind}:${appId}:${index}`;
|
|
2450
|
+
const cached = resolved.get(cacheKey);
|
|
2451
|
+
if (cached) return cached;
|
|
2452
|
+
const pending = provisionKey(kind, index, appId, acls, description).catch(
|
|
2453
|
+
(err) => {
|
|
2454
|
+
resolved.delete(cacheKey);
|
|
2455
|
+
throw err;
|
|
2573
2456
|
}
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2457
|
+
);
|
|
2458
|
+
resolved.set(cacheKey, pending);
|
|
2459
|
+
return pending;
|
|
2460
|
+
}
|
|
2461
|
+
async function provisionKey(kind, index, appId, acls, description) {
|
|
2462
|
+
const stored = await readStoredKey(kind, index, appId);
|
|
2463
|
+
if (stored) {
|
|
2464
|
+
logger.info({ kind, index, appId }, "reusing the stored API key");
|
|
2465
|
+
return { key: stored, source: "keychain" };
|
|
2579
2466
|
}
|
|
2580
|
-
const key = await
|
|
2581
|
-
await
|
|
2467
|
+
const key = await createKey(index, acls, description);
|
|
2468
|
+
await storeKey(kind, index, appId, key);
|
|
2582
2469
|
return { key, source: "created" };
|
|
2583
2470
|
}
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2471
|
+
function resolveWriteKey(index, appId) {
|
|
2472
|
+
return resolveKey(
|
|
2473
|
+
"write",
|
|
2474
|
+
index,
|
|
2475
|
+
appId,
|
|
2476
|
+
WRITE_ACLS,
|
|
2477
|
+
`Algolia Wizard write key for ${index} index`
|
|
2478
|
+
);
|
|
2479
|
+
}
|
|
2480
|
+
async function resolveSearchOnlyKey(index, appId, envKey) {
|
|
2481
|
+
if (envKey) return { key: envKey, source: "env" };
|
|
2482
|
+
return resolveKey(
|
|
2483
|
+
"search",
|
|
2484
|
+
index,
|
|
2485
|
+
appId,
|
|
2486
|
+
["search"],
|
|
2487
|
+
`Algolia Wizard search-only key for ${index} index`
|
|
2488
|
+
);
|
|
2593
2489
|
}
|
|
2594
2490
|
|
|
2595
2491
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2596
2492
|
var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
|
|
2597
2493
|
var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
|
|
2494
|
+
var INDEX_NAME_VAR = "ALGOLIA_INDEX_NAME";
|
|
2598
2495
|
function appendEnv(content, entries) {
|
|
2599
2496
|
const prefix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
|
|
2600
2497
|
const lines = entries.map(([name, value]) => `${name}=${value}
|
|
@@ -2604,9 +2501,16 @@ function appendEnv(content, entries) {
|
|
|
2604
2501
|
function hasEnv(content, name) {
|
|
2605
2502
|
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
2606
2503
|
}
|
|
2504
|
+
function upsertEnv(content, name, value) {
|
|
2505
|
+
if (!hasEnv(content, name)) return appendEnv(content, [[name, value]]);
|
|
2506
|
+
return content.replace(
|
|
2507
|
+
new RegExp(`^\\s*(?:export\\s+)?${name}\\s*=.*$`, "m"),
|
|
2508
|
+
`${name}=${value}`
|
|
2509
|
+
);
|
|
2510
|
+
}
|
|
2607
2511
|
function writeCredentialsTool(ctx) {
|
|
2608
2512
|
return tool6({
|
|
2609
|
-
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_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").
|
|
2513
|
+
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.`,
|
|
2610
2514
|
inputSchema: z12.object({
|
|
2611
2515
|
filePath: z12.string().describe(
|
|
2612
2516
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
@@ -2614,43 +2518,51 @@ function writeCredentialsTool(ctx) {
|
|
|
2614
2518
|
}),
|
|
2615
2519
|
execute: async ({ filePath }) => {
|
|
2616
2520
|
logger.info({ filePath }, "called writeCredentials tool");
|
|
2617
|
-
const
|
|
2618
|
-
if (
|
|
2521
|
+
const resolved2 = resolveInRoot(ctx, filePath);
|
|
2522
|
+
if (resolved2.ok === false) return resolved2.error;
|
|
2619
2523
|
const targetIndex = useWizard.getState().targetIndex;
|
|
2620
2524
|
if (!targetIndex) {
|
|
2621
2525
|
return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
|
|
2622
2526
|
}
|
|
2623
|
-
let
|
|
2624
|
-
let
|
|
2625
|
-
try {
|
|
2626
|
-
appId = (await requireApplication()).id;
|
|
2627
|
-
writeKey = await resolveWriteKey(targetIndex);
|
|
2628
|
-
} catch (err) {
|
|
2629
|
-
return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
|
|
2630
|
-
}
|
|
2527
|
+
let existing = "";
|
|
2528
|
+
let present;
|
|
2631
2529
|
try {
|
|
2632
|
-
if (await hasSymlinkParent(ctx,
|
|
2633
|
-
return `Refused: ${
|
|
2530
|
+
if (await hasSymlinkParent(ctx, resolved2.target)) {
|
|
2531
|
+
return `Refused: ${resolved2.target} is outside the repo root (${ctx.root}).`;
|
|
2634
2532
|
}
|
|
2635
|
-
let existing = "";
|
|
2636
2533
|
try {
|
|
2637
|
-
existing = await readFile4(
|
|
2534
|
+
existing = await readFile4(resolved2.target, "utf8");
|
|
2638
2535
|
} catch (err) {
|
|
2639
2536
|
if (err.code !== "ENOENT") throw err;
|
|
2640
2537
|
}
|
|
2641
|
-
|
|
2538
|
+
present = [APP_ID_VAR, API_KEY_VAR].filter(
|
|
2642
2539
|
(name) => hasEnv(existing, name)
|
|
2643
2540
|
);
|
|
2644
|
-
|
|
2645
|
-
|
|
2541
|
+
} catch (err) {
|
|
2542
|
+
return `Error writing credentials to ${filePath}: ${err.message}`;
|
|
2543
|
+
}
|
|
2544
|
+
let credentials = [];
|
|
2545
|
+
if (present.length === 0) {
|
|
2546
|
+
try {
|
|
2547
|
+
const appId = (await requireApplication()).id;
|
|
2548
|
+
credentials = [
|
|
2549
|
+
[APP_ID_VAR, appId],
|
|
2550
|
+
[API_KEY_VAR, (await resolveWriteKey(targetIndex, appId)).key]
|
|
2551
|
+
];
|
|
2552
|
+
} catch (err) {
|
|
2553
|
+
return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
|
|
2646
2554
|
}
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2555
|
+
}
|
|
2556
|
+
try {
|
|
2557
|
+
const updated = upsertEnv(
|
|
2558
|
+
appendEnv(existing, credentials),
|
|
2559
|
+
INDEX_NAME_VAR,
|
|
2560
|
+
targetIndex
|
|
2561
|
+
);
|
|
2562
|
+
await mkdir4(dirname5(resolved2.target), { recursive: true });
|
|
2563
|
+
await writeFile4(resolved2.target, updated, "utf8");
|
|
2564
|
+
const wrote = `Wrote ${[...credentials.map(([name]) => name), INDEX_NAME_VAR].join(", ")} to ${filePath}`;
|
|
2565
|
+
return present.length > 0 ? `${wrote}. Skipped ${present.join(" and ")}: already defined there.` : wrote;
|
|
2654
2566
|
} catch (err) {
|
|
2655
2567
|
return `Error writing credentials to ${filePath}: ${err.message}`;
|
|
2656
2568
|
}
|
|
@@ -2664,19 +2576,11 @@ import z13 from "zod";
|
|
|
2664
2576
|
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2665
2577
|
import { join as join7 } from "node:path";
|
|
2666
2578
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2667
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2668
|
-
"node_modules",
|
|
2669
|
-
"dist",
|
|
2670
|
-
"build",
|
|
2671
|
-
"vendor",
|
|
2672
|
-
"venv",
|
|
2673
|
-
"__pycache__",
|
|
2674
|
-
"target"
|
|
2675
|
-
]);
|
|
2676
2579
|
async function walkFiles(dir) {
|
|
2580
|
+
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2677
2581
|
const out = [];
|
|
2678
2582
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2679
|
-
if (e.name.startsWith(".") ||
|
|
2583
|
+
if (e.name.startsWith(".") || skip.has(e.name)) continue;
|
|
2680
2584
|
const full = join7(dir, e.name);
|
|
2681
2585
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2682
2586
|
else if (e.isFile()) out.push(full);
|
|
@@ -2698,8 +2602,8 @@ function searchFilesTool(ctx) {
|
|
|
2698
2602
|
if (query.length > MAX_QUERY_LENGTH) {
|
|
2699
2603
|
return `Refused: query exceeds ${MAX_QUERY_LENGTH} characters. Use a shorter pattern.`;
|
|
2700
2604
|
}
|
|
2701
|
-
const
|
|
2702
|
-
if (!
|
|
2605
|
+
const resolved2 = resolveInRoot(ctx, path);
|
|
2606
|
+
if (!resolved2.ok) return resolved2.error;
|
|
2703
2607
|
let re;
|
|
2704
2608
|
try {
|
|
2705
2609
|
re = new RegExp(query);
|
|
@@ -2707,7 +2611,7 @@ function searchFilesTool(ctx) {
|
|
|
2707
2611
|
return `Invalid regex: ${err.message}`;
|
|
2708
2612
|
}
|
|
2709
2613
|
const matches = [];
|
|
2710
|
-
for (const file of await walkFiles(
|
|
2614
|
+
for (const file of await walkFiles(resolved2.target)) {
|
|
2711
2615
|
let content;
|
|
2712
2616
|
try {
|
|
2713
2617
|
content = await readFile5(file, "utf8");
|
|
@@ -2730,194 +2634,92 @@ function searchFilesTool(ctx) {
|
|
|
2730
2634
|
});
|
|
2731
2635
|
}
|
|
2732
2636
|
|
|
2733
|
-
// src/lib/tools/
|
|
2637
|
+
// src/lib/tools/verifyImplementation.ts
|
|
2734
2638
|
import { tool as tool8 } from "ai";
|
|
2735
2639
|
import z14 from "zod";
|
|
2736
|
-
import { relative as relative2 } from "node:path";
|
|
2737
2640
|
|
|
2738
|
-
// src/lib/tools/utils/
|
|
2641
|
+
// src/lib/tools/utils/runCommand.ts
|
|
2739
2642
|
import { spawn as spawn2 } from "node:child_process";
|
|
2740
|
-
|
|
2741
|
-
// src/lib/tools/context.ts
|
|
2742
|
-
var DEFAULT_TOOL_LIMITS = {
|
|
2743
|
-
list: 10,
|
|
2744
|
-
search: 10,
|
|
2745
|
-
read: 20,
|
|
2746
|
-
match: 100,
|
|
2747
|
-
shell: 30
|
|
2748
|
-
};
|
|
2749
|
-
var DEFAULT_SHELL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2750
|
-
async function refuseByDefault() {
|
|
2751
|
-
return "reject";
|
|
2752
|
-
}
|
|
2753
|
-
function createShellContext(overrides = {}) {
|
|
2754
|
-
return {
|
|
2755
|
-
env: async () => ({}),
|
|
2756
|
-
timeoutMs: DEFAULT_SHELL_TIMEOUT_MS,
|
|
2757
|
-
approved: /* @__PURE__ */ new Set(),
|
|
2758
|
-
executions: [],
|
|
2759
|
-
approve: refuseByDefault,
|
|
2760
|
-
...overrides
|
|
2761
|
-
};
|
|
2762
|
-
}
|
|
2763
|
-
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd(), shell2 = createShellContext()) {
|
|
2764
|
-
return {
|
|
2765
|
-
root: cwd,
|
|
2766
|
-
cwd,
|
|
2767
|
-
limits: { ...limits },
|
|
2768
|
-
counts: { list: 0, search: 0, read: 0, shell: 0 },
|
|
2769
|
-
shell: shell2
|
|
2770
|
-
};
|
|
2771
|
-
}
|
|
2772
|
-
|
|
2773
|
-
// src/lib/tools/utils/runShell.ts
|
|
2774
|
-
var SIGKILL_DELAY_MS = 5e3;
|
|
2775
|
-
var HEAD_CHARS = 4e3;
|
|
2776
|
-
var TAIL_CHARS = 8e3;
|
|
2777
|
-
function truncateOutput(output) {
|
|
2778
|
-
if (output.length <= HEAD_CHARS + TAIL_CHARS) return output;
|
|
2779
|
-
const omitted = output.length - HEAD_CHARS - TAIL_CHARS;
|
|
2780
|
-
return [
|
|
2781
|
-
output.slice(0, HEAD_CHARS),
|
|
2782
|
-
`
|
|
2783
|
-
\u2026 [${omitted} characters omitted] \u2026
|
|
2784
|
-
`,
|
|
2785
|
-
output.slice(-TAIL_CHARS)
|
|
2786
|
-
].join("");
|
|
2787
|
-
}
|
|
2788
|
-
function runShell(command, opts) {
|
|
2789
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
|
|
2790
|
-
const startedAt = Date.now();
|
|
2643
|
+
function runCommand(command, args, cwd) {
|
|
2791
2644
|
return new Promise((resolve4) => {
|
|
2792
2645
|
let output = "";
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
shell: true,
|
|
2797
|
-
cwd: opts.cwd,
|
|
2798
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2799
|
-
env: { ...process.env, ...opts.env }
|
|
2646
|
+
const child = spawn2(command, args, {
|
|
2647
|
+
cwd,
|
|
2648
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2800
2649
|
});
|
|
2801
|
-
const finish = (exitCode) => {
|
|
2802
|
-
if (settled) return;
|
|
2803
|
-
settled = true;
|
|
2804
|
-
clearTimeout(timer);
|
|
2805
|
-
clearTimeout(killTimer);
|
|
2806
|
-
resolve4({
|
|
2807
|
-
exitCode,
|
|
2808
|
-
output: truncateOutput(output.trim()),
|
|
2809
|
-
timedOut,
|
|
2810
|
-
durationMs: Date.now() - startedAt
|
|
2811
|
-
});
|
|
2812
|
-
};
|
|
2813
|
-
let killTimer;
|
|
2814
|
-
const timer = setTimeout(() => {
|
|
2815
|
-
timedOut = true;
|
|
2816
|
-
output += `
|
|
2817
|
-
[timed out after ${timeoutMs}ms]`;
|
|
2818
|
-
child.kill("SIGTERM");
|
|
2819
|
-
killTimer = setTimeout(() => child.kill("SIGKILL"), SIGKILL_DELAY_MS);
|
|
2820
|
-
}, timeoutMs);
|
|
2821
2650
|
child.stdout?.on("data", (d) => output += d);
|
|
2822
2651
|
child.stderr?.on("data", (d) => output += d);
|
|
2823
|
-
child.on(
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
child.on("close", (code) =>
|
|
2652
|
+
child.on(
|
|
2653
|
+
"error",
|
|
2654
|
+
(err) => resolve4({ code: 1, output: `Failed to run ${command}: ${err.message}` })
|
|
2655
|
+
);
|
|
2656
|
+
child.on("close", (code) => resolve4({ code: code ?? 1, output }));
|
|
2828
2657
|
});
|
|
2829
2658
|
}
|
|
2830
2659
|
|
|
2831
|
-
// src/lib/tools/
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
}
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2660
|
+
// src/lib/tools/utils/packageManager.ts
|
|
2661
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2662
|
+
import { existsSync } from "node:fs";
|
|
2663
|
+
import { join as join8 } from "node:path";
|
|
2664
|
+
var LOCKFILES = [
|
|
2665
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
2666
|
+
["yarn.lock", "yarn"],
|
|
2667
|
+
["bun.lockb", "bun"],
|
|
2668
|
+
["bun.lock", "bun"],
|
|
2669
|
+
["package-lock.json", "npm"]
|
|
2670
|
+
];
|
|
2671
|
+
async function readPackageJson(cwd = process.cwd()) {
|
|
2672
|
+
return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
|
|
2673
|
+
}
|
|
2674
|
+
function packageManagerFrom(pkg) {
|
|
2675
|
+
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2676
|
+
}
|
|
2677
|
+
function packageManagerFromLockfile(cwd) {
|
|
2678
|
+
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2679
|
+
}
|
|
2680
|
+
async function detectPackageManager(cwd) {
|
|
2681
|
+
try {
|
|
2682
|
+
const pkg = await readPackageJson(cwd);
|
|
2683
|
+
if (pkg.packageManager) return packageManagerFrom(pkg);
|
|
2684
|
+
} catch {
|
|
2685
|
+
}
|
|
2686
|
+
return packageManagerFromLockfile(cwd) ?? "npm";
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
// src/lib/tools/repoVerification.ts
|
|
2690
|
+
var VERIFICATION_SCRIPT_CANDIDATES = ["lint", "typecheck", "check"];
|
|
2691
|
+
async function runRepoVerificationCheck() {
|
|
2692
|
+
let pkg;
|
|
2693
|
+
try {
|
|
2694
|
+
pkg = await readPackageJson();
|
|
2695
|
+
} catch (err) {
|
|
2696
|
+
const limitation = `Could not read package.json to detect verification conventions: ${err.message}`;
|
|
2697
|
+
return { ok: false, checks: [], limitation };
|
|
2698
|
+
}
|
|
2699
|
+
const scripts = pkg.scripts ?? {};
|
|
2700
|
+
const present = VERIFICATION_SCRIPT_CANDIDATES.filter((s) => s in scripts);
|
|
2701
|
+
if (present.length === 0) {
|
|
2702
|
+
const limitation = `No verification script found in package.json (looked for: ${VERIFICATION_SCRIPT_CANDIDATES.join(", ")}).`;
|
|
2703
|
+
return { ok: false, checks: [], limitation };
|
|
2704
|
+
}
|
|
2705
|
+
const pm = await detectPackageManager(process.cwd());
|
|
2706
|
+
const checks = [];
|
|
2707
|
+
for (const script of present) {
|
|
2708
|
+
const command = `${pm} run ${script}`;
|
|
2709
|
+
const { code, output } = await runCommand(pm, ["run", script]);
|
|
2710
|
+
checks.push({ command, exitCode: code, ok: code === 0, output: output.trim() });
|
|
2711
|
+
}
|
|
2712
|
+
return { ok: checks.every((c) => c.ok), checks };
|
|
2849
2713
|
}
|
|
2850
|
-
|
|
2714
|
+
|
|
2715
|
+
// src/lib/tools/verifyImplementation.ts
|
|
2716
|
+
function verifyImplementationTool() {
|
|
2851
2717
|
return tool8({
|
|
2852
|
-
description: "Run
|
|
2853
|
-
inputSchema: z14.object(
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
)
|
|
2857
|
-
cwd: z14.string().optional().describe(
|
|
2858
|
-
"Directory to run in, relative to the project root. Defaults to the project root."
|
|
2859
|
-
),
|
|
2860
|
-
explanation: z14.string().describe(
|
|
2861
|
-
"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."
|
|
2862
|
-
)
|
|
2863
|
-
}),
|
|
2864
|
-
execute: async ({ command, cwd, explanation }) => {
|
|
2865
|
-
if (++ctx.counts.shell > ctx.limits.shell) {
|
|
2866
|
-
return `Refused: command limit (${ctx.limits.shell}) reached. Stop running commands and report what you have.`;
|
|
2867
|
-
}
|
|
2868
|
-
const resolved = resolveInRoot(ctx, cwd ?? ".");
|
|
2869
|
-
if (!resolved.ok) return resolved.error;
|
|
2870
|
-
logger.info({ command, cwd: resolved.target }, "called runShell tool");
|
|
2871
|
-
const key = approvalKey(resolved.target, command);
|
|
2872
|
-
const decision = ctx.shell.approved.has(key) ? "approve" : await ctx.shell.approve({
|
|
2873
|
-
command,
|
|
2874
|
-
cwd: resolved.target,
|
|
2875
|
-
explanation
|
|
2876
|
-
});
|
|
2877
|
-
if (decision === "reject") {
|
|
2878
|
-
ctx.shell.executions.push({
|
|
2879
|
-
command,
|
|
2880
|
-
cwd: resolved.target,
|
|
2881
|
-
approved: false
|
|
2882
|
-
});
|
|
2883
|
-
logger.info({ command }, "runShell: user rejected the command");
|
|
2884
|
-
return "The user rejected this command. Do not retry it. Propose a different command, or report the limitation via reportStatus.";
|
|
2885
|
-
}
|
|
2886
|
-
if (decision === "always") ctx.shell.approved.add(key);
|
|
2887
|
-
useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
|
|
2888
|
-
const env = await ctx.shell.env().catch((err) => {
|
|
2889
|
-
logger.warn({ err, command }, "runShell: could not resolve command env");
|
|
2890
|
-
return {};
|
|
2891
|
-
});
|
|
2892
|
-
const run2 = await (ctx.shell.run ?? runShell)(command, {
|
|
2893
|
-
cwd: resolved.target,
|
|
2894
|
-
env,
|
|
2895
|
-
timeoutMs: ctx.shell.timeoutMs
|
|
2896
|
-
});
|
|
2897
|
-
logger.info(
|
|
2898
|
-
{
|
|
2899
|
-
command,
|
|
2900
|
-
exitCode: run2.exitCode,
|
|
2901
|
-
timedOut: run2.timedOut,
|
|
2902
|
-
durationMs: run2.durationMs
|
|
2903
|
-
},
|
|
2904
|
-
"runShell finished"
|
|
2905
|
-
);
|
|
2906
|
-
await markInteraction();
|
|
2907
|
-
ctx.shell.executions.push({
|
|
2908
|
-
command,
|
|
2909
|
-
cwd: resolved.target,
|
|
2910
|
-
approved: true,
|
|
2911
|
-
exitCode: run2.exitCode,
|
|
2912
|
-
output: run2.output,
|
|
2913
|
-
timedOut: run2.timedOut,
|
|
2914
|
-
durationMs: run2.durationMs
|
|
2915
|
-
});
|
|
2916
|
-
return {
|
|
2917
|
-
exitCode: run2.exitCode,
|
|
2918
|
-
timedOut: run2.timedOut,
|
|
2919
|
-
output: run2.output
|
|
2920
|
-
};
|
|
2718
|
+
description: "Run the repo's mechanical verification check for generated implementation changes. Detects lint/typecheck/check from package.json and returns structured pass/fail evidence for the verifier to interpret.",
|
|
2719
|
+
inputSchema: z14.object(),
|
|
2720
|
+
execute: async () => {
|
|
2721
|
+
logger.info("called verifyImplementation tool");
|
|
2722
|
+
return runRepoVerificationCheck();
|
|
2921
2723
|
}
|
|
2922
2724
|
});
|
|
2923
2725
|
}
|
|
@@ -2997,18 +2799,18 @@ function generateRecordTool(ctx) {
|
|
|
2997
2799
|
}));
|
|
2998
2800
|
const slug = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
2999
2801
|
const relPath = `${DATA_DIR}/${slug}.json`;
|
|
3000
|
-
const
|
|
3001
|
-
if (
|
|
3002
|
-
if (await hasSymlinkParent(ctx,
|
|
3003
|
-
return `Refused: ${
|
|
2802
|
+
const resolved2 = resolveInRoot(ctx, relPath);
|
|
2803
|
+
if (resolved2.ok === false) return resolved2.error;
|
|
2804
|
+
if (await hasSymlinkParent(ctx, resolved2.target)) {
|
|
2805
|
+
return `Refused: ${resolved2.target} is outside the repo root (${ctx.root}).`;
|
|
3004
2806
|
}
|
|
3005
|
-
await mkdir5(dirname6(
|
|
3006
|
-
await writeFile5(
|
|
2807
|
+
await mkdir5(dirname6(resolved2.target), { recursive: true });
|
|
2808
|
+
await writeFile5(resolved2.target, JSON.stringify(records, null, 2), "utf8");
|
|
3007
2809
|
logger.info({ entityName, count: records.length, relPath }, "generateRecord wrote records to disk");
|
|
3008
2810
|
return {
|
|
3009
2811
|
filePath: relPath,
|
|
3010
2812
|
count: records.length,
|
|
3011
|
-
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime
|
|
2813
|
+
message: `Wrote ${records.length} records to ${relPath}. Read and parse this file in the script at runtime (e.g. JSON.parse(readFileSync(...)) in Node/Bun, json.load(open(...)) in Python) \u2014 do not inline the records as literals.`
|
|
3012
2814
|
};
|
|
3013
2815
|
} catch (err) {
|
|
3014
2816
|
return `Error generating records: ${err.message}`;
|
|
@@ -3036,6 +2838,22 @@ function notifyUserTool() {
|
|
|
3036
2838
|
});
|
|
3037
2839
|
}
|
|
3038
2840
|
|
|
2841
|
+
// src/lib/tools/context.ts
|
|
2842
|
+
var DEFAULT_TOOL_LIMITS = {
|
|
2843
|
+
list: 10,
|
|
2844
|
+
search: 10,
|
|
2845
|
+
read: 20,
|
|
2846
|
+
match: 100
|
|
2847
|
+
};
|
|
2848
|
+
function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd()) {
|
|
2849
|
+
return {
|
|
2850
|
+
root: cwd,
|
|
2851
|
+
cwd,
|
|
2852
|
+
limits,
|
|
2853
|
+
counts: { list: 0, search: 0, read: 0 }
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
|
|
3039
2857
|
// src/lib/tools/index.ts
|
|
3040
2858
|
function withLogging(name, def) {
|
|
3041
2859
|
const execute = def.execute;
|
|
@@ -3067,7 +2885,10 @@ function createTools(ctx, { output, tools }) {
|
|
|
3067
2885
|
writeCredentialsTool(ctx)
|
|
3068
2886
|
),
|
|
3069
2887
|
searchFiles: withLogging("searchFiles", searchFilesTool(ctx)),
|
|
3070
|
-
|
|
2888
|
+
verifyImplementation: withLogging(
|
|
2889
|
+
"verifyImplementation",
|
|
2890
|
+
verifyImplementationTool()
|
|
2891
|
+
),
|
|
3071
2892
|
generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
|
|
3072
2893
|
notifyUser: withLogging("notifyUser", notifyUserTool())
|
|
3073
2894
|
};
|
|
@@ -3102,7 +2923,7 @@ async function runAgent(req) {
|
|
|
3102
2923
|
baseURL: PROXY_BASE_URL,
|
|
3103
2924
|
fetch: proxyFetch
|
|
3104
2925
|
});
|
|
3105
|
-
const toolContext =
|
|
2926
|
+
const toolContext = createToolContext();
|
|
3106
2927
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
3107
2928
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
3108
2929
|
const instructions = [
|
|
@@ -3167,11 +2988,7 @@ async function runAgent(req) {
|
|
|
3167
2988
|
"runAgent finished"
|
|
3168
2989
|
);
|
|
3169
2990
|
logger.info(
|
|
3170
|
-
{
|
|
3171
|
-
counts: toolContext.counts,
|
|
3172
|
-
limits: toolContext.limits,
|
|
3173
|
-
commandsRun: toolContext.shell.executions.length
|
|
3174
|
-
},
|
|
2991
|
+
{ counts: toolContext.counts, limits: toolContext.limits },
|
|
3175
2992
|
"tool usage"
|
|
3176
2993
|
);
|
|
3177
2994
|
const toolResults = await stream.toolResults;
|
|
@@ -3197,8 +3014,8 @@ var detectLanguageSchema = z19.object({
|
|
|
3197
3014
|
var detectLanguage = () => runAgent({
|
|
3198
3015
|
instructions: [
|
|
3199
3016
|
"Analyze the codebase and determine the programming languages and frameworks used",
|
|
3200
|
-
"If a superset language is found, exclude the subset language
|
|
3201
|
-
"If a meta-framework is used, exclude the framework
|
|
3017
|
+
"If a superset language is found, exclude the subset language. TS-over-JS.",
|
|
3018
|
+
"If a meta-framework is used, exclude the framework. Next-over-React.",
|
|
3202
3019
|
"Return the exact version",
|
|
3203
3020
|
"Exclude things like CSS frameworks, build tools, or testing frameworks",
|
|
3204
3021
|
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
@@ -3296,7 +3113,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3296
3113
|
// package.json
|
|
3297
3114
|
var package_default = {
|
|
3298
3115
|
name: "@algolia/wizard",
|
|
3299
|
-
version: "0.9.0-rc.
|
|
3116
|
+
version: "0.9.0-rc.88.85",
|
|
3300
3117
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3301
3118
|
type: "module",
|
|
3302
3119
|
engines: {
|
|
@@ -3347,6 +3164,7 @@ var package_default = {
|
|
|
3347
3164
|
"@hono/node-server": "^2.0.10",
|
|
3348
3165
|
"@segment/analytics-node": "^3.1.0",
|
|
3349
3166
|
ai: "^6.0.190",
|
|
3167
|
+
"cross-keychain": "^1.1.0",
|
|
3350
3168
|
dotenv: "^17.4.2",
|
|
3351
3169
|
hono: "^4.12.27",
|
|
3352
3170
|
ink: "^7.0.5",
|
|
@@ -3444,11 +3262,9 @@ var CURATED_FRAMEWORKS = [
|
|
|
3444
3262
|
"Next.js",
|
|
3445
3263
|
"React",
|
|
3446
3264
|
"Vue",
|
|
3447
|
-
"
|
|
3448
|
-
"
|
|
3449
|
-
"
|
|
3450
|
-
"Rails",
|
|
3451
|
-
"Symfony"
|
|
3265
|
+
"Angular",
|
|
3266
|
+
"Svelte",
|
|
3267
|
+
"Vanilla JS"
|
|
3452
3268
|
];
|
|
3453
3269
|
var OTHER_OPTION = "Other";
|
|
3454
3270
|
var normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -3461,15 +3277,12 @@ var FRAMEWORK_ALIASES = {
|
|
|
3461
3277
|
vuejs: "vue",
|
|
3462
3278
|
angular: "angular",
|
|
3463
3279
|
angularjs: "angular",
|
|
3280
|
+
svelte: "svelte",
|
|
3281
|
+
sveltekit: "svelte",
|
|
3464
3282
|
vanillajs: "vanillajs",
|
|
3465
3283
|
vanilla: "vanillajs",
|
|
3466
3284
|
javascript: "vanillajs",
|
|
3467
|
-
js: "vanillajs"
|
|
3468
|
-
django: "django",
|
|
3469
|
-
laravel: "laravel",
|
|
3470
|
-
rails: "rails",
|
|
3471
|
-
rubyonrails: "rails",
|
|
3472
|
-
symfony: "symfony"
|
|
3285
|
+
js: "vanillajs"
|
|
3473
3286
|
};
|
|
3474
3287
|
var isSameFramework = (a, b) => {
|
|
3475
3288
|
const x = FRAMEWORK_ALIASES[normalize(a)] ?? normalize(a);
|
|
@@ -3703,9 +3516,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3703
3516
|
import z26 from "zod";
|
|
3704
3517
|
|
|
3705
3518
|
// src/lib/worktree.ts
|
|
3706
|
-
import { execFile } from "node:child_process";
|
|
3707
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3708
|
-
import {
|
|
3519
|
+
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3520
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3521
|
+
import {
|
|
3522
|
+
basename as basename2,
|
|
3523
|
+
dirname as dirname7,
|
|
3524
|
+
isAbsolute as isAbsolute2,
|
|
3525
|
+
join as join9,
|
|
3526
|
+
relative as relative2,
|
|
3527
|
+
resolve as resolve3
|
|
3528
|
+
} from "node:path";
|
|
3709
3529
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
3710
3530
|
var MAX_WIZARD_WORKTREES = 3;
|
|
3711
3531
|
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
@@ -3736,7 +3556,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3736
3556
|
return out.trim().length > 0;
|
|
3737
3557
|
}
|
|
3738
3558
|
async function pruneOldWorktrees(repoRoot) {
|
|
3739
|
-
const dir =
|
|
3559
|
+
const dir = join9(stateDir(repoRoot), "worktrees");
|
|
3740
3560
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3741
3561
|
for (const slug of stale) {
|
|
3742
3562
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3747,7 +3567,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3747
3567
|
"worktree",
|
|
3748
3568
|
"remove",
|
|
3749
3569
|
"--force",
|
|
3750
|
-
|
|
3570
|
+
join9(dir, slug)
|
|
3751
3571
|
]);
|
|
3752
3572
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3753
3573
|
} catch (err) {
|
|
@@ -3761,13 +3581,113 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3761
3581
|
async function createWorktree(repoRoot) {
|
|
3762
3582
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3763
3583
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3764
|
-
const path =
|
|
3584
|
+
const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3765
3585
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3766
3586
|
await pruneOldWorktrees(repoRoot);
|
|
3767
3587
|
await mkdir6(dirname7(path), { recursive: true });
|
|
3768
3588
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3769
3589
|
return { path, branch };
|
|
3770
3590
|
}
|
|
3591
|
+
async function installWorktreeDeps(worktreePath) {
|
|
3592
|
+
try {
|
|
3593
|
+
await readPackageJson(worktreePath);
|
|
3594
|
+
} catch {
|
|
3595
|
+
return { ok: true, output: "no package.json; skipped install" };
|
|
3596
|
+
}
|
|
3597
|
+
const pm = await detectPackageManager(worktreePath);
|
|
3598
|
+
return new Promise((resolve4) => {
|
|
3599
|
+
let output = "";
|
|
3600
|
+
const child = spawn3(pm, ["install"], {
|
|
3601
|
+
cwd: worktreePath,
|
|
3602
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3603
|
+
});
|
|
3604
|
+
child.stdout?.on("data", (d) => output += d);
|
|
3605
|
+
child.stderr?.on("data", (d) => output += d);
|
|
3606
|
+
child.on(
|
|
3607
|
+
"error",
|
|
3608
|
+
(err) => resolve4({
|
|
3609
|
+
ok: false,
|
|
3610
|
+
output: `Failed to run ${pm} install: ${err.message}`
|
|
3611
|
+
})
|
|
3612
|
+
);
|
|
3613
|
+
child.on(
|
|
3614
|
+
"close",
|
|
3615
|
+
(code) => resolve4({ ok: code === 0, output: output.trim() })
|
|
3616
|
+
);
|
|
3617
|
+
});
|
|
3618
|
+
}
|
|
3619
|
+
var INGEST_RUNTIMES = ["node", "python", "python3", "bun"];
|
|
3620
|
+
function validateIngestEntrypoint(worktreePath, entrypoint) {
|
|
3621
|
+
if (!entrypoint || entrypoint.startsWith("-")) {
|
|
3622
|
+
return {
|
|
3623
|
+
ok: false,
|
|
3624
|
+
reason: `entrypoint "${entrypoint}" is not a plain file path`
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
const target = resolve3(worktreePath, entrypoint);
|
|
3628
|
+
const rel = relative2(worktreePath, target);
|
|
3629
|
+
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
3630
|
+
return {
|
|
3631
|
+
ok: false,
|
|
3632
|
+
reason: `entrypoint "${entrypoint}" resolves outside the worktree`
|
|
3633
|
+
};
|
|
3634
|
+
}
|
|
3635
|
+
return { ok: true, target };
|
|
3636
|
+
}
|
|
3637
|
+
async function runIngestScript(worktreePath, runtime, entrypoint, env = {}) {
|
|
3638
|
+
if (!INGEST_RUNTIMES.includes(runtime)) {
|
|
3639
|
+
return {
|
|
3640
|
+
ran: false,
|
|
3641
|
+
ok: false,
|
|
3642
|
+
output: "",
|
|
3643
|
+
reason: `runtime "${runtime}" is not an allowed interpreter (${INGEST_RUNTIMES.join(", ")})`
|
|
3644
|
+
};
|
|
3645
|
+
}
|
|
3646
|
+
const validated = validateIngestEntrypoint(worktreePath, entrypoint);
|
|
3647
|
+
if (!validated.ok) {
|
|
3648
|
+
return { ran: false, ok: false, output: "", reason: validated.reason };
|
|
3649
|
+
}
|
|
3650
|
+
try {
|
|
3651
|
+
if (!(await stat2(validated.target)).isFile()) {
|
|
3652
|
+
return {
|
|
3653
|
+
ran: false,
|
|
3654
|
+
ok: false,
|
|
3655
|
+
output: "",
|
|
3656
|
+
reason: `entrypoint "${entrypoint}" is not a file`
|
|
3657
|
+
};
|
|
3658
|
+
}
|
|
3659
|
+
} catch {
|
|
3660
|
+
return {
|
|
3661
|
+
ran: false,
|
|
3662
|
+
ok: false,
|
|
3663
|
+
output: "",
|
|
3664
|
+
reason: `entrypoint "${entrypoint}" does not exist`
|
|
3665
|
+
};
|
|
3666
|
+
}
|
|
3667
|
+
return new Promise((resolveRun) => {
|
|
3668
|
+
let output = "";
|
|
3669
|
+
const child = spawn3(runtime, [entrypoint], {
|
|
3670
|
+
cwd: worktreePath,
|
|
3671
|
+
shell: false,
|
|
3672
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3673
|
+
env: { ...process.env, ...env }
|
|
3674
|
+
});
|
|
3675
|
+
child.stdout?.on("data", (d) => output += d);
|
|
3676
|
+
child.stderr?.on("data", (d) => output += d);
|
|
3677
|
+
child.on(
|
|
3678
|
+
"error",
|
|
3679
|
+
(err) => resolveRun({
|
|
3680
|
+
ran: true,
|
|
3681
|
+
ok: false,
|
|
3682
|
+
output: `Failed to run ${runtime} ${entrypoint}: ${err.message}`
|
|
3683
|
+
})
|
|
3684
|
+
);
|
|
3685
|
+
child.on(
|
|
3686
|
+
"close",
|
|
3687
|
+
(code) => resolveRun({ ran: true, ok: code === 0, output: output.trim() })
|
|
3688
|
+
);
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3771
3691
|
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
3772
3692
|
const trimmed = sourcePath.trim();
|
|
3773
3693
|
if (!trimmed) {
|
|
@@ -3781,8 +3701,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3781
3701
|
} catch {
|
|
3782
3702
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3783
3703
|
}
|
|
3784
|
-
const relPath =
|
|
3785
|
-
const dest =
|
|
3704
|
+
const relPath = join9(ingestDir, basename2(source));
|
|
3705
|
+
const dest = join9(worktreePath, relPath);
|
|
3786
3706
|
try {
|
|
3787
3707
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3788
3708
|
await copyFile(source, dest);
|
|
@@ -3800,7 +3720,7 @@ function hasEnvVar(content, name) {
|
|
|
3800
3720
|
async function readEnvVar(worktreePath, name) {
|
|
3801
3721
|
let content;
|
|
3802
3722
|
try {
|
|
3803
|
-
content = await
|
|
3723
|
+
content = await readFile7(join9(worktreePath, ".env"), "utf8");
|
|
3804
3724
|
} catch (err) {
|
|
3805
3725
|
if (err.code !== "ENOENT") throw err;
|
|
3806
3726
|
return void 0;
|
|
@@ -3815,10 +3735,10 @@ async function readEnvVar(worktreePath, name) {
|
|
|
3815
3735
|
return value;
|
|
3816
3736
|
}
|
|
3817
3737
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3818
|
-
const target =
|
|
3738
|
+
const target = join9(worktreePath, ".env");
|
|
3819
3739
|
let existing = "";
|
|
3820
3740
|
try {
|
|
3821
|
-
existing = await
|
|
3741
|
+
existing = await readFile7(target, "utf8");
|
|
3822
3742
|
} catch (err) {
|
|
3823
3743
|
if (err.code !== "ENOENT") throw err;
|
|
3824
3744
|
}
|
|
@@ -3887,15 +3807,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3887
3807
|
}
|
|
3888
3808
|
|
|
3889
3809
|
// src/lib/algoliaDocs.ts
|
|
3890
|
-
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
3891
|
-
import { dirname as dirname8, join as
|
|
3810
|
+
import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
|
|
3811
|
+
import { dirname as dirname8, join as join10 } from "node:path";
|
|
3892
3812
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3893
|
-
var DOCS_SUBPATH =
|
|
3813
|
+
var DOCS_SUBPATH = join10("docs", "algolia-sdk");
|
|
3894
3814
|
function findDocsDir() {
|
|
3895
3815
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3896
3816
|
for (; ; ) {
|
|
3897
|
-
const candidate =
|
|
3898
|
-
if (
|
|
3817
|
+
const candidate = join10(dir, DOCS_SUBPATH);
|
|
3818
|
+
if (existsSync2(candidate)) return candidate;
|
|
3899
3819
|
const parent = dirname8(dir);
|
|
3900
3820
|
if (parent === dir) return void 0;
|
|
3901
3821
|
dir = parent;
|
|
@@ -3917,7 +3837,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3917
3837
|
);
|
|
3918
3838
|
return "";
|
|
3919
3839
|
}
|
|
3920
|
-
return readFileSync(
|
|
3840
|
+
return readFileSync(join10(docsDir, files[0]), "utf8").trim();
|
|
3921
3841
|
}
|
|
3922
3842
|
function getNamedDoc(name, language) {
|
|
3923
3843
|
const docsDir = findDocsDir();
|
|
@@ -3925,15 +3845,14 @@ function getNamedDoc(name, language) {
|
|
|
3925
3845
|
logger.warn("docs/algolia-sdk not found");
|
|
3926
3846
|
return "";
|
|
3927
3847
|
}
|
|
3928
|
-
const file =
|
|
3929
|
-
if (!
|
|
3848
|
+
const file = join10(docsDir, `${name}-${language}.md`);
|
|
3849
|
+
if (!existsSync2(file)) {
|
|
3930
3850
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3931
3851
|
return "";
|
|
3932
3852
|
}
|
|
3933
3853
|
return readFileSync(file, "utf8").trim();
|
|
3934
3854
|
}
|
|
3935
3855
|
function getFrameworkSpecificDoc(frameworks) {
|
|
3936
|
-
if (frameworks.length === 0) return "";
|
|
3937
3856
|
const fw = frameworks.map((f) => f.toLowerCase());
|
|
3938
3857
|
if (fw.includes("vue") || fw.includes("nuxt")) {
|
|
3939
3858
|
return loadAlgoliaDoc("vue");
|
|
@@ -3941,6 +3860,9 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
3941
3860
|
if (fw.includes("react") || fw.includes("next.js")) {
|
|
3942
3861
|
return loadAlgoliaDoc("react");
|
|
3943
3862
|
}
|
|
3863
|
+
if (fw.includes("angular")) {
|
|
3864
|
+
return loadAlgoliaDoc("angular");
|
|
3865
|
+
}
|
|
3944
3866
|
return loadAlgoliaDoc("js");
|
|
3945
3867
|
}
|
|
3946
3868
|
|
|
@@ -3968,7 +3890,11 @@ var implementSchema = z26.object({
|
|
|
3968
3890
|
});
|
|
3969
3891
|
var implementationOutputSchema = z26.object({
|
|
3970
3892
|
summary: z26.string(),
|
|
3971
|
-
|
|
3893
|
+
// Ingestion only: a structured pair the wizard turns into an argv, never a
|
|
3894
|
+
// free-form command string. `runtime` is allowlisted and `entrypoint` is
|
|
3895
|
+
// validated worktree-relative, so the agent cannot inject extra commands.
|
|
3896
|
+
runtime: z26.enum(INGEST_RUNTIMES).optional(),
|
|
3897
|
+
entrypoint: z26.string().optional()
|
|
3972
3898
|
});
|
|
3973
3899
|
var verificationOutputSchema = z26.object({
|
|
3974
3900
|
summary: z26.string(),
|
|
@@ -3978,35 +3904,30 @@ var verificationOutputSchema = z26.object({
|
|
|
3978
3904
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3979
3905
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
3980
3906
|
var INGEST_DIR = ".algolia-wizard";
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
];
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
}
|
|
4001
|
-
function searchUiTarget(language) {
|
|
4002
|
-
return matchUiFramework(language)?.target ?? language.frameworks[0]?.name ?? (isJsProject(language) ? "JavaScript" : "this project");
|
|
4003
|
-
}
|
|
4004
|
-
function frameworksForDoc(language) {
|
|
4005
|
-
if (!isJsProject(language)) return [];
|
|
4006
|
-
return [matchUiFramework(language)?.doc ?? "js"];
|
|
3907
|
+
function detectUiFramework(language) {
|
|
3908
|
+
const names = language.frameworks.map((f) => f.name.toLowerCase());
|
|
3909
|
+
if (names.some((n) => n.includes("vue") || n.includes("nuxt"))) return "Vue";
|
|
3910
|
+
if (names.some((n) => n.includes("react") || n.includes("next")))
|
|
3911
|
+
return "React";
|
|
3912
|
+
if (names.some((n) => n.includes("angular"))) return "Angular";
|
|
3913
|
+
return "JavaScript";
|
|
3914
|
+
}
|
|
3915
|
+
function frameworksForDoc(framework) {
|
|
3916
|
+
switch (framework) {
|
|
3917
|
+
case "React":
|
|
3918
|
+
return ["react"];
|
|
3919
|
+
case "Vue":
|
|
3920
|
+
return ["vue"];
|
|
3921
|
+
case "Angular":
|
|
3922
|
+
return ["angular"];
|
|
3923
|
+
case "JavaScript":
|
|
3924
|
+
return [];
|
|
3925
|
+
}
|
|
4007
3926
|
}
|
|
4008
3927
|
function publicEnvPrefix(language) {
|
|
4009
|
-
const frameworkNames =
|
|
3928
|
+
const frameworkNames = language.frameworks.map(
|
|
3929
|
+
(framework) => framework.name.toLowerCase()
|
|
3930
|
+
);
|
|
4010
3931
|
if (frameworkNames.some((name) => name.includes("next"))) {
|
|
4011
3932
|
return "NEXT_PUBLIC_";
|
|
4012
3933
|
}
|
|
@@ -4019,17 +3940,21 @@ function publicEnvPrefix(language) {
|
|
|
4019
3940
|
if (frameworkNames.some((name) => name.includes("vite"))) {
|
|
4020
3941
|
return "VITE_";
|
|
4021
3942
|
}
|
|
4022
|
-
return
|
|
3943
|
+
return "PUBLIC_";
|
|
4023
3944
|
}
|
|
4024
3945
|
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
4025
3946
|
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
3947
|
+
var INDEX_VAR_SUFFIX = "ALGOLIA_INDEX_NAME";
|
|
4026
3948
|
function appIdVar(language) {
|
|
4027
3949
|
return `${publicEnvPrefix(language)}${APP_ID_VAR_SUFFIX}`;
|
|
4028
3950
|
}
|
|
4029
3951
|
function searchKeyVar(language) {
|
|
4030
3952
|
return `${publicEnvPrefix(language)}${SEARCH_KEY_VAR_SUFFIX}`;
|
|
4031
3953
|
}
|
|
4032
|
-
function
|
|
3954
|
+
function searchIndexVar(language) {
|
|
3955
|
+
return `${publicEnvPrefix(language)}${INDEX_VAR_SUFFIX}`;
|
|
3956
|
+
}
|
|
3957
|
+
function searchEnvVars(language, index, appId, searchKey) {
|
|
4033
3958
|
return [
|
|
4034
3959
|
{
|
|
4035
3960
|
name: appIdVar(language),
|
|
@@ -4038,17 +3963,23 @@ function searchEnvVars(language, appId, searchKey) {
|
|
|
4038
3963
|
{
|
|
4039
3964
|
name: searchKeyVar(language),
|
|
4040
3965
|
value: searchKey ?? "<your-algolia-search-only-api-key>"
|
|
3966
|
+
},
|
|
3967
|
+
// Wizard-supplied rather than written into the generated code, because an
|
|
3968
|
+
// agent that retypes the name (appending the project name, re-casing it)
|
|
3969
|
+
// leaves the UI querying an index that does not exist.
|
|
3970
|
+
{
|
|
3971
|
+
name: searchIndexVar(language),
|
|
3972
|
+
value: index
|
|
4041
3973
|
}
|
|
4042
3974
|
];
|
|
4043
3975
|
}
|
|
4044
3976
|
function baseInstructions(input) {
|
|
4045
3977
|
return [
|
|
4046
|
-
|
|
3978
|
+
// Agents have renamed this (e.g. appending the project name), which the
|
|
3979
|
+
// index-scoped keys then reject with a 403.
|
|
3980
|
+
`Target Algolia index, to be used exactly as written \u2014 never renamed, re-cased, prefixed, or suffixed: "${input.targetIndex}"`,
|
|
4047
3981
|
`Project languages and frameworks: ${JSON.stringify(input.language)}`,
|
|
4048
|
-
"Make minimal, idiomatic changes; do not touch unrelated code."
|
|
4049
|
-
`Commands run through a shell on ${process.platform}. Write commands that work there.`,
|
|
4050
|
-
"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.",
|
|
4051
|
-
"runShell needs the developer to approve each command, so give every call a clear `explanation` naming what it does and any side effect. If a command is rejected, do not retry it \u2014 take a different approach or report the limitation."
|
|
3982
|
+
"Make minimal, idiomatic changes; do not touch unrelated code."
|
|
4052
3983
|
];
|
|
4053
3984
|
}
|
|
4054
3985
|
function sourceSpecificInstructions(input) {
|
|
@@ -4068,50 +3999,38 @@ function sourceSpecificInstructions(input) {
|
|
|
4068
3999
|
generated: [
|
|
4069
4000
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
4070
4001
|
"Call the generateRecord tool once per entity (entityName, attributes, count 20-50); it invents the values and unique objectIDs and writes them to a JSON file in the worktree, returning the file path. Do not write records or objectIDs yourself.",
|
|
4071
|
-
"In the script, read and parse each returned file path at runtime
|
|
4002
|
+
"In the script, read and parse each returned file path at runtime (e.g. JSON.parse(readFileSync(...)) in Node/Bun, json.load(open(...)) in Python) instead of inlining the records as literals.",
|
|
4072
4003
|
"Add a prominent TODO where the developer swaps the generated records (and the JSON file under `.algolia-wizard/data/`) for their real record source."
|
|
4073
4004
|
]
|
|
4074
4005
|
};
|
|
4075
4006
|
return byLine[input.ingestionSource];
|
|
4076
4007
|
}
|
|
4077
|
-
function algoliaClientDoc(input) {
|
|
4078
|
-
const doc = getNamedDoc("save-records", "js");
|
|
4079
|
-
if (!doc) return [];
|
|
4080
|
-
if (isJsProject(input.language)) return [doc];
|
|
4081
|
-
return [
|
|
4082
|
-
"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:",
|
|
4083
|
-
doc
|
|
4084
|
-
];
|
|
4085
|
-
}
|
|
4086
4008
|
function ingestionInstructions(input) {
|
|
4087
4009
|
return [
|
|
4088
4010
|
...input.confirmed && input.confirmed.length ? [
|
|
4089
4011
|
`Create an ingestion script under "${input.ingestDir}/" at the repo root.`,
|
|
4090
4012
|
`Ingest only these confirmed entities (name, source paths, attributes): ${JSON.stringify(input.confirmed)}.`,
|
|
4091
4013
|
`Ingesting writes to Algolia, so the script needs a write API key and App ID \u2014 read them from the ${API_KEY_VAR} and ${APP_ID_VAR} environment variables rather than hardcoding them. The wizard sets these when it runs the script.`,
|
|
4092
|
-
|
|
4014
|
+
`Read the index name from the ${INDEX_NAME_VAR} environment variable, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or entity name \u2014 the write key only works for that exact index. Exit with an error if ${INDEX_NAME_VAR} is unset.`,
|
|
4015
|
+
"Use the appropriate Algolia package in the ingestion script. Do not use the raw HTTP API.",
|
|
4093
4016
|
"After a successful ingest, the script must print exactly one line to stdout in the form `ALGOLIA_WIZARD_RECORD_COUNT=<n>`, where <n> is the total number of records pushed to Algolia. Print it last, on its own line, with no surrounding text.",
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
'Then run the script yourself via runShell, and report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
|
|
4017
|
+
getNamedDoc("save-records", "js"),
|
|
4018
|
+
'Add algoliasearch to package.json "dependencies" with a valid version range; the wizard installs the worktree deps after you finish.',
|
|
4097
4019
|
"The summary should be extremely concise.",
|
|
4020
|
+
`Return how to run the script as two fields, not a command string: "runtime" (one of ${INGEST_RUNTIMES.join(", ")}) and "entrypoint" (the script path relative to the worktree root, e.g. "${input.ingestDir}/ingest.mjs"). The wizard runs \`<runtime> <entrypoint>\` directly, so the entrypoint must be a plain path with no flags or arguments. Write a script one of those interpreters can run as-is.`,
|
|
4098
4021
|
...sourceSpecificInstructions(input)
|
|
4099
4022
|
] : []
|
|
4100
4023
|
];
|
|
4101
4024
|
}
|
|
4102
4025
|
function searchInstructions(input) {
|
|
4103
|
-
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.
|
|
4026
|
+
const doc = getFrameworkSpecificDoc(frameworksForDoc(input.uiFramework));
|
|
4104
4027
|
return [
|
|
4105
4028
|
"Implement an in-app Algolia search experience.",
|
|
4106
|
-
`Build the search UI for ${input.
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
"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."
|
|
4112
|
-
],
|
|
4113
|
-
`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 "${input.targetIndex}" index.`,
|
|
4114
|
-
"Read the App ID and a search-only API key from env vars; never hardcode them. A search-only key is safe to expose client-side.",
|
|
4029
|
+
`Build the search UI for ${input.uiFramework}.`,
|
|
4030
|
+
"Follow the Algolia JS SDK reference below for client setup and InstantSearch wiring; prefer it over prior knowledge:",
|
|
4031
|
+
doc,
|
|
4032
|
+
`Add the search UI at ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working SearchBox and Hits against the target index.`,
|
|
4033
|
+
`Read the index name from the ${searchIndexVar(input.language)} env var, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or component name.`,
|
|
4115
4034
|
// The key is provisioned only after verification passes, so the agent never
|
|
4116
4035
|
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
4117
4036
|
// decide whether a key already exists, and an agent-invented value there
|
|
@@ -4120,8 +4039,9 @@ function searchInstructions(input) {
|
|
|
4120
4039
|
// Not the agent's to rename: the wizard writes these exact names into
|
|
4121
4040
|
// ".env" right after this step, so a renamed prefix would leave the code
|
|
4122
4041
|
// reading a var the wizard never wrote.
|
|
4123
|
-
`Use exactly these env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4124
|
-
"
|
|
4042
|
+
`Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4043
|
+
"Read the App ID, the search-only API key, and the index name from public env vars; never hardcode them. A search-only key is safe to expose client-side.",
|
|
4044
|
+
'Add any Algolia/InstantSearch packages you import to package.json "dependencies" with a valid version range; the wizard installs them in the worktree after you finish.',
|
|
4125
4045
|
"The summary should be extremely concise; do not mention env var setup or manual testing steps \u2014 the wizard writes the resolved credentials to .env and reports that separately."
|
|
4126
4046
|
];
|
|
4127
4047
|
}
|
|
@@ -4129,12 +4049,11 @@ function verificationInstructions(input) {
|
|
|
4129
4049
|
return [
|
|
4130
4050
|
"Verify the Algolia implementation changes in the current worktree.",
|
|
4131
4051
|
`Verification tools found in the codebase: ${JSON.stringify(input.findings.verification ?? [])}.`,
|
|
4132
|
-
"
|
|
4133
|
-
"
|
|
4134
|
-
"
|
|
4135
|
-
"Do not make speculative fixes when no checks exist, a check cannot run, or failures are unrelated to these changes \u2014 note the limitation in your summary.",
|
|
4052
|
+
"Call verifyImplementation at least once; it runs every repo-defined lint/typecheck/check script and returns per-check results plus an aggregate ok.",
|
|
4053
|
+
"For issues caused by the implementation, make minimal fixes with writeFile and re-run verifyImplementation.",
|
|
4054
|
+
"Do not make speculative fixes when verifyImplementation cannot run, no checks exist, or failures are unrelated to these changes \u2014 note the limitation in your summary.",
|
|
4136
4055
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
4137
|
-
`Do not modify "${input.ingestDir}/" unless
|
|
4056
|
+
`Do not modify "${input.ingestDir}/" unless verifyImplementation reports an actionable issue in its files.`,
|
|
4138
4057
|
"Always call reportStatus with status=success once verification has run, even when sufficient=false.",
|
|
4139
4058
|
"Set sufficient=true only when the implementation is complete and checks pass (or fail for a clearly unrelated reason).",
|
|
4140
4059
|
"Set sufficient=false when the implementation is incomplete or has implementation-caused failures; include concrete additionalInstructions for the next pass."
|
|
@@ -4155,15 +4074,14 @@ var IMPLEMENT_CONFIG = {
|
|
|
4155
4074
|
}
|
|
4156
4075
|
};
|
|
4157
4076
|
var useCaseToolMap = {
|
|
4158
|
-
ingestion: [
|
|
4077
|
+
ingestion: [...FS_READ_TOOLS, "writeFile", "writeCredentials", "notifyUser"],
|
|
4078
|
+
search: [...FS_READ_TOOLS, "writeFile", "notifyUser"],
|
|
4079
|
+
verification: [
|
|
4159
4080
|
...FS_READ_TOOLS,
|
|
4160
4081
|
"writeFile",
|
|
4161
|
-
"
|
|
4162
|
-
"runShell",
|
|
4082
|
+
"verifyImplementation",
|
|
4163
4083
|
"notifyUser"
|
|
4164
|
-
]
|
|
4165
|
-
search: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"],
|
|
4166
|
-
verification: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"]
|
|
4084
|
+
]
|
|
4167
4085
|
};
|
|
4168
4086
|
function toolsForUseCase(useCase, ingestionSource) {
|
|
4169
4087
|
const tools = useCaseToolMap[useCase];
|
|
@@ -4186,31 +4104,15 @@ function formatSummary(useCase, summary) {
|
|
|
4186
4104
|
const label = useCase === "ingestion" ? "Ingestion" : useCase === "search" ? "Search" : "Verification";
|
|
4187
4105
|
return `${label}: ${summary}`;
|
|
4188
4106
|
}
|
|
4107
|
+
function buildIngestCommand(worktree, runtime, entrypoint) {
|
|
4108
|
+
return `cd ${shellQuote(worktree)} && ${runtime} ${shellQuote(entrypoint)}`;
|
|
4109
|
+
}
|
|
4189
4110
|
function parseIngestRecordCount(output) {
|
|
4190
4111
|
const match = output.match(/ALGOLIA_WIZARD_RECORD_COUNT=(\d+)/);
|
|
4191
4112
|
if (!match) return void 0;
|
|
4192
4113
|
const count = Number(match[1]);
|
|
4193
4114
|
return Number.isFinite(count) ? count : void 0;
|
|
4194
4115
|
}
|
|
4195
|
-
function ingestOutcome(executions, ingestCommand) {
|
|
4196
|
-
const newestFirst = [...executions].reverse();
|
|
4197
|
-
const withCount = newestFirst.filter(
|
|
4198
|
-
(e) => parseIngestRecordCount(e.output ?? "") != null
|
|
4199
|
-
);
|
|
4200
|
-
const succeeded = newestFirst.filter((e) => e.approved && e.exitCode === 0);
|
|
4201
|
-
return {
|
|
4202
|
-
run: succeeded.find((e) => e.command === ingestCommand) ?? succeeded.find((e) => withCount.includes(e)),
|
|
4203
|
-
recordCount: parseIngestRecordCount(withCount[0]?.output ?? "")
|
|
4204
|
-
};
|
|
4205
|
-
}
|
|
4206
|
-
var PLACEHOLDER_WRITE_KEY = "ALGOLIA_WRITE_KEY_PLACEHOLDER";
|
|
4207
|
-
function makeToolContext(worktree, env = async () => ({})) {
|
|
4208
|
-
return createToolContext(
|
|
4209
|
-
DEFAULT_TOOL_LIMITS,
|
|
4210
|
-
worktree,
|
|
4211
|
-
createShellContext({ env, approve: storeApproval(worktree) })
|
|
4212
|
-
);
|
|
4213
|
-
}
|
|
4214
4116
|
function verificationRetryInstructions(verification) {
|
|
4215
4117
|
return [
|
|
4216
4118
|
`Implementation insufficient. Address these findings before reporting completion: ${verification.additionalInstructions ?? verification.summary}`
|
|
@@ -4289,13 +4191,9 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4289
4191
|
const confirmed2 = normalized.confirmedEntities;
|
|
4290
4192
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
4291
4193
|
let appId;
|
|
4292
|
-
let ingestAppId;
|
|
4293
4194
|
if (useCases.includes("search")) {
|
|
4294
4195
|
appId = (await requireApplication()).id;
|
|
4295
4196
|
}
|
|
4296
|
-
if (useCases.includes("ingestion")) {
|
|
4297
|
-
ingestAppId = appId ?? (await requireApplication()).id;
|
|
4298
|
-
}
|
|
4299
4197
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
4300
4198
|
try {
|
|
4301
4199
|
process.chdir(worktree);
|
|
@@ -4328,11 +4226,11 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4328
4226
|
appId,
|
|
4329
4227
|
// Names only: the search-only key is provisioned after verification, so
|
|
4330
4228
|
// every value here is still a placeholder when the agent reads them.
|
|
4331
|
-
searchEnvVars: searchEnvVars(language, appId),
|
|
4229
|
+
searchEnvVars: searchEnvVars(language, targetIndex, appId),
|
|
4332
4230
|
ingestDir: INGEST_DIR,
|
|
4333
4231
|
ingestionSource,
|
|
4334
4232
|
uploadFilePath,
|
|
4335
|
-
|
|
4233
|
+
uiFramework: detectUiFramework(language)
|
|
4336
4234
|
};
|
|
4337
4235
|
const summaries = [];
|
|
4338
4236
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
@@ -4355,29 +4253,41 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4355
4253
|
}
|
|
4356
4254
|
let finalSearchEnvVars = input.searchEnvVars;
|
|
4357
4255
|
let agentRuns = 0;
|
|
4358
|
-
let
|
|
4256
|
+
let ingestRuntime;
|
|
4257
|
+
let ingestEntrypoint;
|
|
4359
4258
|
let ingestScriptRan = false;
|
|
4360
4259
|
let ingestRecordCount;
|
|
4361
4260
|
let ingestDurationMs;
|
|
4261
|
+
let installFailed = false;
|
|
4362
4262
|
let ingestOutcomeMessage;
|
|
4363
|
-
const ingestionTools = ingestAppId ? makeToolContext(worktree, async () => ({
|
|
4364
|
-
[APP_ID_VAR]: ingestAppId,
|
|
4365
|
-
[API_KEY_VAR]: PLACEHOLDER_WRITE_KEY
|
|
4366
|
-
})) : void 0;
|
|
4367
|
-
const searchTools = makeToolContext(worktree);
|
|
4368
4263
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4369
4264
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
4370
4265
|
agentRuns += 1;
|
|
4371
|
-
|
|
4266
|
+
const result = await runAgent({
|
|
4372
4267
|
instructions: buildAgentInstructions(
|
|
4373
4268
|
currentUseCase,
|
|
4374
4269
|
input,
|
|
4375
4270
|
extraInstructions
|
|
4376
4271
|
),
|
|
4377
4272
|
tools: toolsForUseCase(currentUseCase, input.ingestionSource),
|
|
4378
|
-
outputSchema: implementationOutputSchema
|
|
4379
|
-
|
|
4273
|
+
outputSchema: implementationOutputSchema
|
|
4274
|
+
});
|
|
4275
|
+
ctx.notify({
|
|
4276
|
+
messages: [`Installing dependencies for ${currentUseCase}\u2026`]
|
|
4277
|
+
});
|
|
4278
|
+
const installLogId = ctx.logStart("installWorktreeDeps", {
|
|
4279
|
+
useCase: currentUseCase
|
|
4380
4280
|
});
|
|
4281
|
+
const install = await installWorktreeDeps(worktree);
|
|
4282
|
+
ctx.logEnd(installLogId, install.ok ? "success" : "error");
|
|
4283
|
+
if (!install.ok) {
|
|
4284
|
+
installFailed = true;
|
|
4285
|
+
logger.warn(
|
|
4286
|
+
{ useCase: currentUseCase, output: install.output },
|
|
4287
|
+
"implement: dependency install in worktree failed; generated commands may not run until deps are installed"
|
|
4288
|
+
);
|
|
4289
|
+
}
|
|
4290
|
+
return result;
|
|
4381
4291
|
}
|
|
4382
4292
|
async function runVerificationUseCase() {
|
|
4383
4293
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
@@ -4385,55 +4295,111 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4385
4295
|
return runAgent({
|
|
4386
4296
|
instructions: buildAgentInstructions("verification", input),
|
|
4387
4297
|
tools: toolsForUseCase("verification"),
|
|
4388
|
-
outputSchema: verificationOutputSchema
|
|
4389
|
-
toolContext: searchTools
|
|
4298
|
+
outputSchema: verificationOutputSchema
|
|
4390
4299
|
});
|
|
4391
4300
|
}
|
|
4392
4301
|
if (useCases.includes("ingestion")) {
|
|
4393
|
-
const
|
|
4394
|
-
summaries.push(formatSummary("ingestion",
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4302
|
+
const { summary, runtime, entrypoint } = await runImplementationUseCase("ingestion");
|
|
4303
|
+
summaries.push(formatSummary("ingestion", summary));
|
|
4304
|
+
ingestRuntime = runtime;
|
|
4305
|
+
ingestEntrypoint = entrypoint;
|
|
4306
|
+
if (ingestRuntime && ingestEntrypoint && !installFailed) {
|
|
4307
|
+
ctx.clearNotices();
|
|
4308
|
+
const runNow = await ctx.requestUserInput({
|
|
4309
|
+
prompt: `Run the ingestion script now? This writes records to the "${targetIndex}" index.`,
|
|
4310
|
+
promptType: "acceptReject",
|
|
4311
|
+
options: ["Yes", "No"],
|
|
4312
|
+
messages: []
|
|
4313
|
+
}) === true;
|
|
4314
|
+
if (runNow) {
|
|
4315
|
+
const ingestApp = await requireApplication();
|
|
4316
|
+
const writeKey = (await resolveWriteKey(targetIndex, ingestApp.id)).key;
|
|
4317
|
+
ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
|
|
4318
|
+
const scriptLogId = ctx.logStart("runIngestScript", {
|
|
4319
|
+
runtime: ingestRuntime,
|
|
4320
|
+
entrypoint: ingestEntrypoint
|
|
4411
4321
|
});
|
|
4322
|
+
const startedAt = Date.now();
|
|
4323
|
+
const run2 = await runIngestScript(
|
|
4324
|
+
worktree,
|
|
4325
|
+
ingestRuntime,
|
|
4326
|
+
ingestEntrypoint,
|
|
4327
|
+
{
|
|
4328
|
+
[APP_ID_VAR]: ingestApp.id,
|
|
4329
|
+
[API_KEY_VAR]: writeKey,
|
|
4330
|
+
[INDEX_NAME_VAR]: targetIndex
|
|
4331
|
+
}
|
|
4332
|
+
);
|
|
4333
|
+
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
4334
|
+
ingestScriptRan = run2.ran && run2.ok;
|
|
4335
|
+
if (ingestScriptRan) {
|
|
4336
|
+
ingestDurationMs = Date.now() - startedAt;
|
|
4337
|
+
ingestRecordCount = parseIngestRecordCount(run2.output);
|
|
4338
|
+
if (ingestRecordCount != null) {
|
|
4339
|
+
track("AI Wizard Ingest Successful", {
|
|
4340
|
+
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
4341
|
+
record_count: ingestRecordCount,
|
|
4342
|
+
duration_ms: ingestDurationMs
|
|
4343
|
+
});
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
let summaryLine;
|
|
4347
|
+
let outcomeMessage;
|
|
4348
|
+
if (!run2.ran) {
|
|
4349
|
+
summaryLine = `\u26A0\uFE0F Skipped running the ingestion script: ${run2.reason}`;
|
|
4350
|
+
outcomeMessage = `\u26A0\uFE0F The ingestion script did not run: ${run2.reason}`;
|
|
4351
|
+
logger.warn(
|
|
4352
|
+
{
|
|
4353
|
+
runtime: ingestRuntime,
|
|
4354
|
+
entrypoint: ingestEntrypoint,
|
|
4355
|
+
reason: run2.reason
|
|
4356
|
+
},
|
|
4357
|
+
"implement: refused to auto-run ingestion script"
|
|
4358
|
+
);
|
|
4359
|
+
track("Error", {
|
|
4360
|
+
step: "Push Data",
|
|
4361
|
+
error: `ingestion script skipped: ${run2.reason}`,
|
|
4362
|
+
product_area: "AI Wizard"
|
|
4363
|
+
});
|
|
4364
|
+
} else if (run2.ok) {
|
|
4365
|
+
const status = "Ingestion run: succeeded.";
|
|
4366
|
+
summaryLine = run2.output ? `${status}
|
|
4367
|
+
${run2.output}` : status;
|
|
4368
|
+
outcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4369
|
+
} else {
|
|
4370
|
+
const status = "\u26A0\uFE0F Ingestion run failed:";
|
|
4371
|
+
summaryLine = run2.output ? `${status}
|
|
4372
|
+
${run2.output}` : status;
|
|
4373
|
+
outcomeMessage = `\u274C Ingestion failed.${run2.output ? ` ${run2.output}` : ""}`;
|
|
4374
|
+
logger.warn(
|
|
4375
|
+
{
|
|
4376
|
+
runtime: ingestRuntime,
|
|
4377
|
+
entrypoint: ingestEntrypoint,
|
|
4378
|
+
output: run2.output
|
|
4379
|
+
},
|
|
4380
|
+
"implement: ingestion script run failed"
|
|
4381
|
+
);
|
|
4382
|
+
track("Error", {
|
|
4383
|
+
step: "Push Data",
|
|
4384
|
+
error: run2.output || "ingestion script exited non-zero",
|
|
4385
|
+
product_area: "AI Wizard"
|
|
4386
|
+
});
|
|
4387
|
+
}
|
|
4388
|
+
summaries.push(summaryLine);
|
|
4389
|
+
ingestOutcomeMessage = outcomeMessage;
|
|
4412
4390
|
}
|
|
4413
|
-
} else {
|
|
4414
|
-
const rejected = executions.some((e) => !e.approved);
|
|
4415
|
-
const reason = rejected ? "you declined to run it" : "no successful run was recorded";
|
|
4416
|
-
ingestOutcomeMessage = `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.${ingestCommand ? " Run the command below when you are ready." : ""}`;
|
|
4417
|
-
summaries.push(`\u26A0\uFE0F The ingestion script did not run: ${reason}.`);
|
|
4418
|
-
logger.warn(
|
|
4419
|
-
{ ingestCommand, rejected, commandsRun: executions.length },
|
|
4420
|
-
"implement: ingestion script did not complete successfully"
|
|
4421
|
-
);
|
|
4422
|
-
track("Error", {
|
|
4423
|
-
step: "Push Data",
|
|
4424
|
-
error: `ingestion did not run: ${reason}`,
|
|
4425
|
-
product_area: "AI Wizard"
|
|
4426
|
-
});
|
|
4427
4391
|
}
|
|
4428
4392
|
const commandMessages = [`Open the worktree: cd ${shellQuote(worktree)}`];
|
|
4429
|
-
if (
|
|
4430
|
-
commandMessages.push(
|
|
4393
|
+
if (ingestRuntime && ingestEntrypoint) {
|
|
4394
|
+
commandMessages.push(
|
|
4395
|
+
`Ingestion command: ${buildIngestCommand(worktree, ingestRuntime, ingestEntrypoint)}`
|
|
4396
|
+
);
|
|
4431
4397
|
}
|
|
4432
4398
|
await ctx.requestUserInput({
|
|
4433
4399
|
prompt: "",
|
|
4434
4400
|
promptType: "enterToContinue",
|
|
4435
4401
|
options: [],
|
|
4436
|
-
messages: [ingestOutcomeMessage, ...commandMessages]
|
|
4402
|
+
messages: ingestOutcomeMessage ? [ingestOutcomeMessage, ...commandMessages] : commandMessages
|
|
4437
4403
|
});
|
|
4438
4404
|
}
|
|
4439
4405
|
if (useCases.includes("search")) {
|
|
@@ -4482,14 +4448,14 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4482
4448
|
let searchKeyError;
|
|
4483
4449
|
if (appId) {
|
|
4484
4450
|
try {
|
|
4485
|
-
const
|
|
4451
|
+
const resolved2 = await resolveSearchOnlyKey(
|
|
4486
4452
|
targetIndex,
|
|
4487
4453
|
appId,
|
|
4488
4454
|
envSearchKey
|
|
4489
4455
|
);
|
|
4490
|
-
searchKey =
|
|
4456
|
+
searchKey = resolved2.key;
|
|
4491
4457
|
summaries.push(
|
|
4492
|
-
|
|
4458
|
+
resolved2.source === "created" ? `Created a new search-only Algolia API key for the "${targetIndex}" index in app ${appId} \u2014 safe to expose in frontend code.` : `Reused the existing search-only Algolia API key for the "${targetIndex}" index in app ${appId}.`
|
|
4493
4459
|
);
|
|
4494
4460
|
} catch (err) {
|
|
4495
4461
|
searchKeyError = err.message;
|
|
@@ -4499,7 +4465,12 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4499
4465
|
);
|
|
4500
4466
|
}
|
|
4501
4467
|
}
|
|
4502
|
-
finalSearchEnvVars = searchEnvVars(
|
|
4468
|
+
finalSearchEnvVars = searchEnvVars(
|
|
4469
|
+
language,
|
|
4470
|
+
targetIndex,
|
|
4471
|
+
appId,
|
|
4472
|
+
searchKey
|
|
4473
|
+
);
|
|
4503
4474
|
const resolvedSearchEnvVars = finalSearchEnvVars.filter(
|
|
4504
4475
|
(v) => !v.value.startsWith("<")
|
|
4505
4476
|
);
|
|
@@ -4545,13 +4516,22 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4545
4516
|
"implement: agent reported success but no files changed in the worktree"
|
|
4546
4517
|
);
|
|
4547
4518
|
}
|
|
4519
|
+
if (installFailed) {
|
|
4520
|
+
summaries.push(
|
|
4521
|
+
'\u26A0\uFE0F Dependency install in the worktree failed. Run your package manager install in the worktree before the command below, or it will fail with "Cannot find module".'
|
|
4522
|
+
);
|
|
4523
|
+
}
|
|
4548
4524
|
return {
|
|
4549
4525
|
ingestionSource,
|
|
4550
4526
|
filesChanged,
|
|
4551
4527
|
summary: summaries.join("\n\n"),
|
|
4552
4528
|
worktreePath: worktree,
|
|
4553
|
-
...useCases.includes("ingestion") &&
|
|
4554
|
-
ingestCommand
|
|
4529
|
+
...useCases.includes("ingestion") && ingestRuntime && ingestEntrypoint ? {
|
|
4530
|
+
ingestCommand: buildIngestCommand(
|
|
4531
|
+
worktree,
|
|
4532
|
+
ingestRuntime,
|
|
4533
|
+
ingestEntrypoint
|
|
4534
|
+
),
|
|
4555
4535
|
ingestScriptRan,
|
|
4556
4536
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4557
4537
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
@@ -4879,10 +4859,11 @@ function parseCliArgs(argv) {
|
|
|
4879
4859
|
|
|
4880
4860
|
// src/lib/resetState.ts
|
|
4881
4861
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4882
|
-
import { join as
|
|
4862
|
+
import { join as join11 } from "node:path";
|
|
4883
4863
|
var KEEP = ["wizard.log"];
|
|
4884
4864
|
async function resetProjectState() {
|
|
4885
4865
|
const dir = stateDir();
|
|
4866
|
+
forgetResolvedKeys();
|
|
4886
4867
|
let entries;
|
|
4887
4868
|
try {
|
|
4888
4869
|
entries = await readdir4(dir);
|
|
@@ -4891,14 +4872,17 @@ async function resetProjectState() {
|
|
|
4891
4872
|
}
|
|
4892
4873
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4893
4874
|
await Promise.all(
|
|
4894
|
-
targets.map(
|
|
4875
|
+
targets.map(
|
|
4876
|
+
(name) => rm2(join11(dir, name), { recursive: true, force: true })
|
|
4877
|
+
)
|
|
4895
4878
|
);
|
|
4896
4879
|
return { dir, removed: targets };
|
|
4897
4880
|
}
|
|
4898
4881
|
|
|
4899
4882
|
// src/main.tsx
|
|
4900
|
-
import { jsx as
|
|
4883
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
4901
4884
|
async function startup() {
|
|
4885
|
+
setProjectRoot(process.cwd());
|
|
4902
4886
|
let args;
|
|
4903
4887
|
try {
|
|
4904
4888
|
args = parseCliArgs(process.argv.slice(2));
|
|
@@ -4946,7 +4930,7 @@ ${formatStepList(workflow)}`);
|
|
|
4946
4930
|
}
|
|
4947
4931
|
async function run(workflow) {
|
|
4948
4932
|
const store = useWizard.getState();
|
|
4949
|
-
const instance = render(/* @__PURE__ */
|
|
4933
|
+
const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4950
4934
|
await store.waitForStart();
|
|
4951
4935
|
let user = await getUser();
|
|
4952
4936
|
if (!user) {
|