@algolia/wizard 0.3.0-rc.44.2 → 0.3.0-rc.47.9
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 +980 -582
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -4,27 +4,191 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/ui/App.tsx
|
|
7
|
-
import {
|
|
8
|
-
import { Box as Box10, Text as Text10, useApp, useInput as useInput3, useWindowSize as useWindowSize2 } from "ink";
|
|
7
|
+
import { Box as Box12, Text as Text12, useApp, useInput as useInput5, useWindowSize as useWindowSize5 } from "ink";
|
|
9
8
|
|
|
10
9
|
// src/core/store.ts
|
|
11
10
|
import { create } from "zustand";
|
|
11
|
+
|
|
12
|
+
// src/lib/algoliaCli.ts
|
|
13
|
+
import { spawn } from "node:child_process";
|
|
14
|
+
import { createRequire } from "node:module";
|
|
15
|
+
var require2 = createRequire(import.meta.url);
|
|
16
|
+
function algoliaCliEntry() {
|
|
17
|
+
return require2.resolve("@algolia/cli/bin/run.js");
|
|
18
|
+
}
|
|
19
|
+
function runAlgoliaCli(args) {
|
|
20
|
+
return new Promise((resolve4, reject) => {
|
|
21
|
+
const child = spawn(process.execPath, [algoliaCliEntry(), ...args]);
|
|
22
|
+
let stdout = "";
|
|
23
|
+
let stderr = "";
|
|
24
|
+
child.stdout.on("data", (chunk) => stdout += chunk);
|
|
25
|
+
child.stderr.on("data", (chunk) => stderr += chunk);
|
|
26
|
+
child.on("error", reject);
|
|
27
|
+
child.on("close", (code) => {
|
|
28
|
+
if (code === 0) {
|
|
29
|
+
resolve4(stdout);
|
|
30
|
+
} else {
|
|
31
|
+
const detail = stderr.trim() || stdout.trim();
|
|
32
|
+
reject(
|
|
33
|
+
new Error(
|
|
34
|
+
`Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail ? `: ${detail}` : ""}`
|
|
35
|
+
)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function getUser() {
|
|
42
|
+
let raw;
|
|
43
|
+
try {
|
|
44
|
+
raw = await runAlgoliaCli(["auth", "get", "--with-access-token"]);
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return toUserInfo(JSON.parse(raw));
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function runAuthLogin() {
|
|
55
|
+
return new Promise((resolve4, reject) => {
|
|
56
|
+
const child = spawn(
|
|
57
|
+
process.execPath,
|
|
58
|
+
[algoliaCliEntry(), "auth", "login", "--default"],
|
|
59
|
+
{ stdio: "inherit" }
|
|
60
|
+
);
|
|
61
|
+
child.on("error", reject);
|
|
62
|
+
child.on("close", (code) => {
|
|
63
|
+
if (code === 0) resolve4();
|
|
64
|
+
else reject(new Error(`Algolia authentication failed (exit ${code}).`));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/lib/auth.ts
|
|
70
|
+
function getAuthToken() {
|
|
71
|
+
return useWizard.getState().user?.token || null;
|
|
72
|
+
}
|
|
73
|
+
var inFlightRefresh = null;
|
|
74
|
+
function refreshAuthToken() {
|
|
75
|
+
inFlightRefresh ??= (async () => {
|
|
76
|
+
try {
|
|
77
|
+
const raw = await runAlgoliaCli(["auth", "get", "--with-access-token"]);
|
|
78
|
+
const user2 = toUserInfo(JSON.parse(raw));
|
|
79
|
+
useWizard.getState().setUser(user2);
|
|
80
|
+
return user2.token;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
} finally {
|
|
84
|
+
inFlightRefresh = null;
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
return inFlightRefresh;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/lib/logger.ts
|
|
91
|
+
import pino from "pino";
|
|
92
|
+
import { join as join2, dirname } from "node:path";
|
|
93
|
+
import { devNull } from "node:os";
|
|
94
|
+
import { mkdirSync, openSync, closeSync } from "node:fs";
|
|
95
|
+
|
|
96
|
+
// src/core/constants.ts
|
|
97
|
+
import { homedir } from "node:os";
|
|
98
|
+
import { join, resolve } from "node:path";
|
|
99
|
+
function rootDir() {
|
|
100
|
+
return process.env.WIZARD_HOME ?? join(homedir(), ".algolia");
|
|
101
|
+
}
|
|
102
|
+
function projectSlug(cwd = process.cwd()) {
|
|
103
|
+
return resolve(cwd).replace(/[/\\:]+/g, "-").replace(/^-+/, "") || "root";
|
|
104
|
+
}
|
|
105
|
+
function stateDir(cwd = process.cwd()) {
|
|
106
|
+
return join(rootDir(), projectSlug(cwd));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/lib/logger.ts
|
|
110
|
+
var STDERR_FD = 2;
|
|
111
|
+
function resolveDest() {
|
|
112
|
+
const target = process.env.VITEST ? devNull : process.env.WIZARD_LOG ?? join2(stateDir(), "wizard.log");
|
|
113
|
+
try {
|
|
114
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
115
|
+
closeSync(openSync(target, "a"));
|
|
116
|
+
return target;
|
|
117
|
+
} catch {
|
|
118
|
+
return STDERR_FD;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function logDestination() {
|
|
122
|
+
return pino.destination({ dest: resolveDest(), sync: false });
|
|
123
|
+
}
|
|
124
|
+
var logger = pino(
|
|
125
|
+
{ level: process.env.LOG_LEVEL ?? "info" },
|
|
126
|
+
logDestination()
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// src/lib/proxyFetch.ts
|
|
130
|
+
var PROXY_BASE_URL = process.env.PROXY_BASE_URL ?? "https://proxy-624203421261.us-east4.run.app";
|
|
131
|
+
var PROXY_AUTH_REJECTED_HEADER = "x-wizard-proxy-auth";
|
|
132
|
+
var proxyFetch = async (input, init) => {
|
|
133
|
+
const req = new Request(input, init);
|
|
134
|
+
const retry = req.clone();
|
|
135
|
+
const res = await fetch(req);
|
|
136
|
+
if (res.status !== 401) return res;
|
|
137
|
+
if (res.headers.get(PROXY_AUTH_REJECTED_HEADER) !== "rejected") return res;
|
|
138
|
+
const fresh = await refreshAuthToken();
|
|
139
|
+
if (!fresh) return res;
|
|
140
|
+
const headers = new Headers(retry.headers);
|
|
141
|
+
if (headers.has("authorization")) {
|
|
142
|
+
headers.set("authorization", `Bearer ${fresh}`);
|
|
143
|
+
} else {
|
|
144
|
+
headers.set("x-api-key", fresh);
|
|
145
|
+
}
|
|
146
|
+
return fetch(new Request(retry, { headers }));
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/lib/interaction.ts
|
|
150
|
+
async function markInteraction() {
|
|
151
|
+
const token = getAuthToken();
|
|
152
|
+
if (!token) return;
|
|
153
|
+
try {
|
|
154
|
+
const res = await proxyFetch(`${PROXY_BASE_URL}/interaction`, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: { authorization: `Bearer ${token}` }
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) throw new Error(`proxy interaction ${res.status}`);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
logger.warn({ err }, "failed to mark wizard interaction");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/core/store.ts
|
|
12
165
|
function toUserInfo({ user_id, ...rest }) {
|
|
13
166
|
return { userId: user_id, ...rest };
|
|
14
167
|
}
|
|
168
|
+
var NOTICE_INTERVAL_MS = 2e3;
|
|
15
169
|
var useWizard = create((set, get) => ({
|
|
16
170
|
phase: "idle",
|
|
171
|
+
homeScreen: "home",
|
|
17
172
|
user: null,
|
|
18
173
|
workflow: null,
|
|
19
174
|
steps: [],
|
|
20
175
|
currentStepIndex: 0,
|
|
21
176
|
output: "",
|
|
177
|
+
notices: [],
|
|
178
|
+
_noticeQueue: [],
|
|
179
|
+
_noticeTimer: null,
|
|
22
180
|
error: null,
|
|
23
181
|
inputReq: null,
|
|
24
182
|
_resolve: null,
|
|
25
183
|
// Advances past the welcome screen. Only meaningful from 'idle' — once the
|
|
26
184
|
// workflow is running there's nothing left to confirm.
|
|
27
|
-
|
|
185
|
+
// Reset `homeScreen` so preflight shows Welcome, not the Learn more sub-view.
|
|
186
|
+
confirmStart: () => set(
|
|
187
|
+
(s) => s.phase === "idle" ? { phase: "preflight", homeScreen: "home" } : {}
|
|
188
|
+
),
|
|
189
|
+
// Welcome sub-view navigation; leaves `phase` untouched so the workflow stays paused.
|
|
190
|
+
openLearnMore: () => set({ homeScreen: "learnMore" }),
|
|
191
|
+
backToHome: () => set({ homeScreen: "home" }),
|
|
28
192
|
// Resolves once the phase leaves 'idle', whether that happens before or
|
|
29
193
|
// after this is called (the welcome screen's spacebar handler is what
|
|
30
194
|
// drives the transition via `confirmStart`).
|
|
@@ -48,10 +212,49 @@ var useWizard = create((set, get) => ({
|
|
|
48
212
|
error: null
|
|
49
213
|
}),
|
|
50
214
|
syncSteps: (steps, currentStepIndex) => set({ steps, currentStepIndex }),
|
|
51
|
-
setActiveStep: (index) =>
|
|
215
|
+
setActiveStep: (index) => {
|
|
216
|
+
get()._clearNoticeQueue();
|
|
217
|
+
set({ phase: "running", currentStepIndex: index, output: "", notices: [] });
|
|
218
|
+
},
|
|
52
219
|
setUser: (user2) => set({ user: user2 }),
|
|
53
220
|
appendToken: (text) => set((s) => ({ output: s.output + text })),
|
|
54
221
|
clearOutput: () => set({ output: "" }),
|
|
222
|
+
// Renders the first notice of a burst immediately, then holds later
|
|
223
|
+
// arrivals in `_noticeQueue` and drains one per `NOTICE_INTERVAL_MS` —
|
|
224
|
+
// the timer stays armed through an empty drain so the cooldown always
|
|
225
|
+
// covers the time since the last render, even across bursts.
|
|
226
|
+
pushNotice: (notice) => {
|
|
227
|
+
const { notices, _noticeQueue, _noticeTimer } = get();
|
|
228
|
+
if (_noticeTimer === null) {
|
|
229
|
+
set({
|
|
230
|
+
notices: [...notices, notice],
|
|
231
|
+
_noticeTimer: setTimeout(() => get()._drainNoticeQueue(), NOTICE_INTERVAL_MS)
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
set({ _noticeQueue: [..._noticeQueue, notice] });
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
_drainNoticeQueue: () => {
|
|
238
|
+
const [next, ...rest] = get()._noticeQueue;
|
|
239
|
+
if (next) {
|
|
240
|
+
set((s) => ({
|
|
241
|
+
notices: [...s.notices, next],
|
|
242
|
+
_noticeQueue: rest,
|
|
243
|
+
_noticeTimer: setTimeout(() => get()._drainNoticeQueue(), NOTICE_INTERVAL_MS)
|
|
244
|
+
}));
|
|
245
|
+
} else {
|
|
246
|
+
set({ _noticeTimer: null });
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
_clearNoticeQueue: () => {
|
|
250
|
+
const timer = get()._noticeTimer;
|
|
251
|
+
if (timer) clearTimeout(timer);
|
|
252
|
+
set({ _noticeQueue: [], _noticeTimer: null });
|
|
253
|
+
},
|
|
254
|
+
clearNotices: () => {
|
|
255
|
+
get()._clearNoticeQueue();
|
|
256
|
+
set({ notices: [] });
|
|
257
|
+
},
|
|
55
258
|
requestUserInput: (req) => new Promise((resolve4) => {
|
|
56
259
|
set({
|
|
57
260
|
phase: "awaitingInput",
|
|
@@ -59,43 +262,33 @@ var useWizard = create((set, get) => ({
|
|
|
59
262
|
_resolve: resolve4
|
|
60
263
|
});
|
|
61
264
|
}),
|
|
62
|
-
submitInput: (value) => {
|
|
265
|
+
submitInput: async (value) => {
|
|
266
|
+
await markInteraction();
|
|
63
267
|
get()._resolve?.(value);
|
|
64
268
|
set({ inputReq: null, _resolve: null, phase: "running" });
|
|
65
269
|
},
|
|
66
270
|
setDone: () => set({ phase: "done" }),
|
|
67
271
|
setError: (message) => set({ phase: "error", error: message }),
|
|
68
|
-
reset: () =>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
272
|
+
reset: () => {
|
|
273
|
+
get()._clearNoticeQueue();
|
|
274
|
+
set({
|
|
275
|
+
phase: "idle",
|
|
276
|
+
homeScreen: "home",
|
|
277
|
+
workflow: null,
|
|
278
|
+
steps: [],
|
|
279
|
+
currentStepIndex: 0,
|
|
280
|
+
output: "",
|
|
281
|
+
notices: [],
|
|
282
|
+
error: null,
|
|
283
|
+
inputReq: null,
|
|
284
|
+
_resolve: null
|
|
285
|
+
});
|
|
286
|
+
}
|
|
78
287
|
}));
|
|
79
288
|
|
|
80
|
-
// src/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
stream.write(`\x1B]52;c;${encoded}\x07`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// src/lib/shell.ts
|
|
87
|
-
function shellQuote(value) {
|
|
88
|
-
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// src/ui/PromptInput.tsx
|
|
92
|
-
import { Box as Box3, Text as Text3 } from "ink";
|
|
93
|
-
import TextInput from "ink-text-input";
|
|
94
|
-
import { useState as useState3 } from "react";
|
|
95
|
-
|
|
96
|
-
// src/ui/SelectPrompt.tsx
|
|
97
|
-
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
98
|
-
import { useState as useState2 } from "react";
|
|
289
|
+
// src/ui/Notices.tsx
|
|
290
|
+
import { Box as Box2, Text as Text2, useWindowSize as useWindowSize2 } from "ink";
|
|
291
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
99
292
|
|
|
100
293
|
// src/ui/Table.tsx
|
|
101
294
|
import { Box, Text, measureElement, useWindowSize } from "ink";
|
|
@@ -159,6 +352,7 @@ var MARKER = {
|
|
|
159
352
|
};
|
|
160
353
|
var BRAND = "#003DFF";
|
|
161
354
|
var SECONDARY = "#5468FF";
|
|
355
|
+
var DANGER = "#F86E7E";
|
|
162
356
|
var COLORS = {
|
|
163
357
|
brand: BRAND,
|
|
164
358
|
primary: "#E6EDF3",
|
|
@@ -168,23 +362,145 @@ var COLORS = {
|
|
|
168
362
|
dim: "#484F58",
|
|
169
363
|
highlight: { bg: "#12331C", fg: "#4ADE80" },
|
|
170
364
|
badge: "#E3B341",
|
|
171
|
-
|
|
365
|
+
danger: DANGER,
|
|
172
366
|
success: "#4ADE80",
|
|
173
367
|
bg: {
|
|
174
368
|
main: "#0B0E14",
|
|
175
369
|
sidebar: "#14171E"
|
|
176
370
|
},
|
|
371
|
+
border: "#30363D",
|
|
177
372
|
accent: "#76A0FF",
|
|
178
373
|
status: {
|
|
179
374
|
pending: "gray",
|
|
180
375
|
running: "#76A0FF",
|
|
181
376
|
done: "#4ADE80",
|
|
182
|
-
error:
|
|
377
|
+
error: DANGER
|
|
183
378
|
}
|
|
184
379
|
};
|
|
185
380
|
|
|
186
|
-
// src/ui/
|
|
381
|
+
// src/ui/Notices.tsx
|
|
187
382
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
383
|
+
var AGENT_MARKER = "\u2726";
|
|
384
|
+
var RESERVED_ROWS = 14;
|
|
385
|
+
var PANEL_TEXT_WIDTH = 45;
|
|
386
|
+
function messageLineCount(text) {
|
|
387
|
+
return Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH));
|
|
388
|
+
}
|
|
389
|
+
function noticeLineCount(notice) {
|
|
390
|
+
const messageLines = (notice.messages ?? []).reduce((sum, m) => {
|
|
391
|
+
const text = typeof m === "string" ? m : m.text;
|
|
392
|
+
return sum + messageLineCount(text);
|
|
393
|
+
}, 0);
|
|
394
|
+
const tableLines = notice.table ? notice.table.rows.length + 4 : 0;
|
|
395
|
+
return messageLines + tableLines;
|
|
396
|
+
}
|
|
397
|
+
function fitVisibleNotices(notices, windowRows) {
|
|
398
|
+
const budget = Math.max(windowRows - RESERVED_ROWS, 3);
|
|
399
|
+
let used = 0;
|
|
400
|
+
let count = 0;
|
|
401
|
+
for (let i = notices.length - 1; i >= 0; i--) {
|
|
402
|
+
const height = noticeLineCount(notices[i]) + (count > 0 ? 1 : 0);
|
|
403
|
+
if (count > 0 && used + height > budget) break;
|
|
404
|
+
used += height;
|
|
405
|
+
count++;
|
|
406
|
+
}
|
|
407
|
+
return notices.slice(notices.length - count);
|
|
408
|
+
}
|
|
409
|
+
var PULSE_STEPS = 12;
|
|
410
|
+
var PULSE_STEP_MS = 150;
|
|
411
|
+
var PULSE_COLORS = Array.from(
|
|
412
|
+
{ length: PULSE_STEPS },
|
|
413
|
+
(_, i) => mixHex(COLORS.accent, COLORS.strong, i / (PULSE_STEPS - 1))
|
|
414
|
+
);
|
|
415
|
+
function mixHex(from, to, t) {
|
|
416
|
+
const a = parseHex(from);
|
|
417
|
+
const b = parseHex(to);
|
|
418
|
+
const channel = (k) => Math.round(a[k] + (b[k] - a[k]) * t).toString(16).padStart(2, "0");
|
|
419
|
+
return `#${channel("r")}${channel("g")}${channel("b")}`;
|
|
420
|
+
}
|
|
421
|
+
function parseHex(hex) {
|
|
422
|
+
const n = hex.replace("#", "");
|
|
423
|
+
return {
|
|
424
|
+
r: parseInt(n.slice(0, 2), 16),
|
|
425
|
+
g: parseInt(n.slice(2, 4), 16),
|
|
426
|
+
b: parseInt(n.slice(4, 6), 16)
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function Notices() {
|
|
430
|
+
const notices = useWizard((s) => s.notices);
|
|
431
|
+
const { rows: windowRows } = useWindowSize2();
|
|
432
|
+
const visible = fitVisibleNotices(notices, windowRows);
|
|
433
|
+
const [pulseStep, setPulseStep] = useState2(0);
|
|
434
|
+
useEffect2(() => {
|
|
435
|
+
let direction = 1;
|
|
436
|
+
const id = setInterval(() => {
|
|
437
|
+
setPulseStep((step) => {
|
|
438
|
+
const next = step + direction;
|
|
439
|
+
if (next >= PULSE_COLORS.length - 1) direction = -1;
|
|
440
|
+
else if (next <= 0) direction = 1;
|
|
441
|
+
return Math.min(Math.max(next, 0), PULSE_COLORS.length - 1);
|
|
442
|
+
});
|
|
443
|
+
}, PULSE_STEP_MS);
|
|
444
|
+
return () => clearInterval(id);
|
|
445
|
+
}, []);
|
|
446
|
+
if (!visible.length) return null;
|
|
447
|
+
const pulseColor = PULSE_COLORS[pulseStep];
|
|
448
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
|
|
449
|
+
const isLatest = i === visible.length - 1;
|
|
450
|
+
return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", children: [
|
|
451
|
+
notice.messages?.map((m, j) => {
|
|
452
|
+
const line = typeof m === "string" ? { text: m } : m;
|
|
453
|
+
const prefix = j === 0 ? `${AGENT_MARKER} ` : " ";
|
|
454
|
+
return /* @__PURE__ */ jsxs(
|
|
455
|
+
Text2,
|
|
456
|
+
{
|
|
457
|
+
color: isLatest && !line.color ? pulseColor : line.color ?? COLORS.dim,
|
|
458
|
+
bold: line.bold,
|
|
459
|
+
children: [
|
|
460
|
+
prefix,
|
|
461
|
+
line.text
|
|
462
|
+
]
|
|
463
|
+
},
|
|
464
|
+
`notice-${i}-${j}`
|
|
465
|
+
);
|
|
466
|
+
}),
|
|
467
|
+
notice.table && /* @__PURE__ */ jsx2(Table, { columns: notice.table.columns, rows: notice.table.rows })
|
|
468
|
+
] }, `notice-${i}`);
|
|
469
|
+
}) });
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/ui/PromptInput.tsx
|
|
473
|
+
import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
|
|
474
|
+
import TextInput from "ink-text-input";
|
|
475
|
+
import { useState as useState4 } from "react";
|
|
476
|
+
|
|
477
|
+
// src/ui/NextAction.tsx
|
|
478
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
479
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
480
|
+
function NextAction({
|
|
481
|
+
action,
|
|
482
|
+
keyHint,
|
|
483
|
+
hierarchy = "primary"
|
|
484
|
+
}) {
|
|
485
|
+
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "row", gap: 1, children: [
|
|
486
|
+
hierarchy === "primary" && /* @__PURE__ */ jsx3(Text3, { color: COLORS.success, bold: true, children: `> ${action}` }),
|
|
487
|
+
hierarchy === "secondary" && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
488
|
+
/* @__PURE__ */ jsx3(Text3, { color: COLORS.success, bold: true, children: `>` }),
|
|
489
|
+
/* @__PURE__ */ jsx3(Text3, { color: COLORS.primary, bold: true, children: action })
|
|
490
|
+
] }),
|
|
491
|
+
/* @__PURE__ */ jsxs2(Box3, { children: [
|
|
492
|
+
/* @__PURE__ */ jsx3(Text3, { color: COLORS.muted, children: "press " }),
|
|
493
|
+
/* @__PURE__ */ jsx3(Text3, { color: COLORS.muted, children: `[` }),
|
|
494
|
+
/* @__PURE__ */ jsx3(Text3, { color: COLORS.primary, children: keyHint }),
|
|
495
|
+
/* @__PURE__ */ jsx3(Text3, { color: COLORS.muted, children: `]` })
|
|
496
|
+
] })
|
|
497
|
+
] });
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/ui/SelectPrompt.tsx
|
|
501
|
+
import { Box as Box4, Text as Text4, useInput } from "ink";
|
|
502
|
+
import { useState as useState3 } from "react";
|
|
503
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
188
504
|
var CANCEL = "cancel";
|
|
189
505
|
function SelectPrompt({
|
|
190
506
|
options,
|
|
@@ -199,10 +515,10 @@ function SelectPrompt({
|
|
|
199
515
|
secondary,
|
|
200
516
|
defaultSelectedIndex = 0
|
|
201
517
|
}) {
|
|
202
|
-
const [index, setIndex] =
|
|
518
|
+
const [index, setIndex] = useState3(
|
|
203
519
|
() => defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0
|
|
204
520
|
);
|
|
205
|
-
const [checked, setChecked] =
|
|
521
|
+
const [checked, setChecked] = useState3(() => /* @__PURE__ */ new Set());
|
|
206
522
|
const hasCancel = Boolean(multi || cancelable);
|
|
207
523
|
const rows = hasCancel ? [...options, "Cancel"] : options;
|
|
208
524
|
const cancelIndex = hasCancel ? options.length : -1;
|
|
@@ -239,46 +555,46 @@ function SelectPrompt({
|
|
|
239
555
|
}
|
|
240
556
|
}
|
|
241
557
|
});
|
|
242
|
-
return /* @__PURE__ */
|
|
243
|
-
error && /* @__PURE__ */
|
|
244
|
-
messages?.map((m, i) => /* @__PURE__ */
|
|
245
|
-
table && /* @__PURE__ */
|
|
246
|
-
/* @__PURE__ */
|
|
247
|
-
question && /* @__PURE__ */
|
|
248
|
-
helpText && /* @__PURE__ */
|
|
558
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 1, children: [
|
|
559
|
+
error && /* @__PURE__ */ jsx4(Text4, { color: COLORS.danger, children: error }),
|
|
560
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx4(Text4, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
561
|
+
table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
|
|
562
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
|
|
563
|
+
question && /* @__PURE__ */ jsx4(Text4, { color: COLORS.muted, children: question }),
|
|
564
|
+
helpText && /* @__PURE__ */ jsx4(Text4, { color: COLORS.dim, children: helpText })
|
|
249
565
|
] }),
|
|
250
|
-
/* @__PURE__ */
|
|
566
|
+
/* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: rows.map((option, i) => {
|
|
251
567
|
const highlighted = i === index;
|
|
252
568
|
const isCancel = i === cancelIndex;
|
|
253
569
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
254
570
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
255
571
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
256
|
-
const label = /* @__PURE__ */
|
|
572
|
+
const label = /* @__PURE__ */ jsxs3(Text4, { color: labelColor, children: [
|
|
257
573
|
highlighted ? "\u276F " : " ",
|
|
258
574
|
bullet,
|
|
259
575
|
option
|
|
260
576
|
] });
|
|
261
|
-
return /* @__PURE__ */
|
|
262
|
-
|
|
577
|
+
return /* @__PURE__ */ jsxs3(
|
|
578
|
+
Box4,
|
|
263
579
|
{
|
|
264
580
|
width: rowWidth,
|
|
265
581
|
paddingX: 1,
|
|
266
582
|
paddingY: 1,
|
|
267
583
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
268
584
|
children: [
|
|
269
|
-
sec?.kind === "text" ? /* @__PURE__ */
|
|
270
|
-
sec?.kind === "text" && /* @__PURE__ */
|
|
271
|
-
/* @__PURE__ */
|
|
272
|
-
sec?.kind === "badge" && /* @__PURE__ */
|
|
585
|
+
sec?.kind === "text" ? /* @__PURE__ */ jsx4(Box4, { width: labelColWidth, flexShrink: 0, children: label }) : label,
|
|
586
|
+
sec?.kind === "text" && /* @__PURE__ */ jsx4(Text4, { color: highlighted ? COLORS.primary : COLORS.muted, children: sec.value }),
|
|
587
|
+
/* @__PURE__ */ jsx4(Box4, { flexGrow: 1 }),
|
|
588
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx4(Text4, { color: COLORS.badge, children: sec.value })
|
|
273
589
|
]
|
|
274
590
|
},
|
|
275
591
|
`row-${i}`
|
|
276
592
|
);
|
|
277
593
|
}) }),
|
|
278
|
-
/* @__PURE__ */
|
|
594
|
+
/* @__PURE__ */ jsx4(Box4, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs3(Text4, { children: [
|
|
279
595
|
i > 0 ? " " : "",
|
|
280
|
-
/* @__PURE__ */
|
|
281
|
-
/* @__PURE__ */
|
|
596
|
+
/* @__PURE__ */ jsx4(Text4, { color: COLORS.primary, children: key }),
|
|
597
|
+
/* @__PURE__ */ jsxs3(Text4, { color: COLORS.dim, children: [
|
|
282
598
|
" ",
|
|
283
599
|
label
|
|
284
600
|
] })
|
|
@@ -287,17 +603,35 @@ function SelectPrompt({
|
|
|
287
603
|
}
|
|
288
604
|
|
|
289
605
|
// src/ui/PromptInput.tsx
|
|
290
|
-
import { jsx as
|
|
606
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
291
607
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
608
|
+
function SpaceToContinuePrompt({
|
|
609
|
+
question,
|
|
610
|
+
messages,
|
|
611
|
+
onDecide
|
|
612
|
+
}) {
|
|
613
|
+
useInput2((input, key) => {
|
|
614
|
+
if (input === " ") onDecide(true);
|
|
615
|
+
else if (key.escape) onDecide(false);
|
|
616
|
+
});
|
|
617
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
|
|
618
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
619
|
+
question && /* @__PURE__ */ jsx5(Text5, { color: COLORS.primary, children: question }),
|
|
620
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 1, flexDirection: "column", children: [
|
|
621
|
+
/* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "space" }),
|
|
622
|
+
/* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
623
|
+
] })
|
|
624
|
+
] });
|
|
625
|
+
}
|
|
292
626
|
function PromptInput() {
|
|
293
627
|
const { phase, inputReq, submitInput } = useWizard();
|
|
294
|
-
const [draft, setDraft] =
|
|
628
|
+
const [draft, setDraft] = useState4("");
|
|
295
629
|
if (phase === "done" || phase === "error") {
|
|
296
|
-
return /* @__PURE__ */
|
|
630
|
+
return /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
297
631
|
}
|
|
298
632
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
299
633
|
if (inputReq.promptType === "multipleChoice") {
|
|
300
|
-
return /* @__PURE__ */
|
|
634
|
+
return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
|
|
301
635
|
SelectPrompt,
|
|
302
636
|
{
|
|
303
637
|
question: inputReq.prompt,
|
|
@@ -314,7 +648,7 @@ function PromptInput() {
|
|
|
314
648
|
) });
|
|
315
649
|
}
|
|
316
650
|
if (inputReq.promptType === "multiSelect") {
|
|
317
|
-
return /* @__PURE__ */
|
|
651
|
+
return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
|
|
318
652
|
SelectPrompt,
|
|
319
653
|
{
|
|
320
654
|
multi: true,
|
|
@@ -329,7 +663,7 @@ function PromptInput() {
|
|
|
329
663
|
) });
|
|
330
664
|
}
|
|
331
665
|
if (inputReq.promptType === "notice") {
|
|
332
|
-
return /* @__PURE__ */
|
|
666
|
+
return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
|
|
333
667
|
SelectPrompt,
|
|
334
668
|
{
|
|
335
669
|
question: inputReq.prompt,
|
|
@@ -339,9 +673,19 @@ function PromptInput() {
|
|
|
339
673
|
}
|
|
340
674
|
) });
|
|
341
675
|
}
|
|
676
|
+
if (inputReq.promptType === "spaceToContinue") {
|
|
677
|
+
return /* @__PURE__ */ jsx5(
|
|
678
|
+
SpaceToContinuePrompt,
|
|
679
|
+
{
|
|
680
|
+
question: inputReq.prompt,
|
|
681
|
+
messages: inputReq.messages,
|
|
682
|
+
onDecide: submitInput
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
}
|
|
342
686
|
if (inputReq.promptType === "acceptReject") {
|
|
343
687
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
344
|
-
return /* @__PURE__ */
|
|
688
|
+
return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
|
|
345
689
|
SelectPrompt,
|
|
346
690
|
{
|
|
347
691
|
question: inputReq.prompt,
|
|
@@ -352,15 +696,15 @@ function PromptInput() {
|
|
|
352
696
|
}
|
|
353
697
|
) });
|
|
354
698
|
}
|
|
355
|
-
return /* @__PURE__ */
|
|
356
|
-
inputReq.error && /* @__PURE__ */
|
|
357
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */
|
|
358
|
-
/* @__PURE__ */
|
|
359
|
-
/* @__PURE__ */
|
|
699
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
700
|
+
inputReq.error && /* @__PURE__ */ jsx5(Text5, { color: COLORS.danger, children: inputReq.error }),
|
|
701
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
702
|
+
/* @__PURE__ */ jsxs4(Box5, { children: [
|
|
703
|
+
/* @__PURE__ */ jsxs4(Text5, { color: COLORS.primary, children: [
|
|
360
704
|
inputReq.prompt,
|
|
361
705
|
" "
|
|
362
706
|
] }),
|
|
363
|
-
/* @__PURE__ */
|
|
707
|
+
/* @__PURE__ */ jsx5(
|
|
364
708
|
TextInput,
|
|
365
709
|
{
|
|
366
710
|
value: draft,
|
|
@@ -376,32 +720,9 @@ function PromptInput() {
|
|
|
376
720
|
}
|
|
377
721
|
|
|
378
722
|
// src/ui/Welcome.tsx
|
|
379
|
-
import { dirname, join } from "node:path";
|
|
723
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
380
724
|
import { fileURLToPath } from "node:url";
|
|
381
|
-
import { Box as
|
|
382
|
-
|
|
383
|
-
// src/ui/NextAction.tsx
|
|
384
|
-
import { Box as Box4, Text as Text4 } from "ink";
|
|
385
|
-
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
386
|
-
function NextAction({
|
|
387
|
-
action,
|
|
388
|
-
keyHint,
|
|
389
|
-
hierarchy = "primary"
|
|
390
|
-
}) {
|
|
391
|
-
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "row", gap: 1, children: [
|
|
392
|
-
hierarchy === "primary" && /* @__PURE__ */ jsx4(Text4, { color: COLORS.success, bold: true, children: `> ${action}` }),
|
|
393
|
-
hierarchy === "secondary" && /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
394
|
-
/* @__PURE__ */ jsx4(Text4, { color: COLORS.success, bold: true, children: `>` }),
|
|
395
|
-
/* @__PURE__ */ jsx4(Text4, { color: COLORS.primary, bold: true, children: action })
|
|
396
|
-
] }),
|
|
397
|
-
/* @__PURE__ */ jsxs3(Box4, { children: [
|
|
398
|
-
/* @__PURE__ */ jsx4(Text4, { color: COLORS.muted, children: "press " }),
|
|
399
|
-
/* @__PURE__ */ jsx4(Text4, { color: COLORS.muted, children: `[` }),
|
|
400
|
-
/* @__PURE__ */ jsx4(Text4, { color: COLORS.primary, children: keyHint }),
|
|
401
|
-
/* @__PURE__ */ jsx4(Text4, { color: COLORS.muted, children: `]` })
|
|
402
|
-
] })
|
|
403
|
-
] });
|
|
404
|
-
}
|
|
725
|
+
import { Box as Box6, Spacer, Text as Text6, useInput as useInput3, useWindowSize as useWindowSize3 } from "ink";
|
|
405
726
|
|
|
406
727
|
// src/ui/copy/welcome.ts
|
|
407
728
|
var sidebarItems = [
|
|
@@ -429,92 +750,246 @@ var sidebarItems = [
|
|
|
429
750
|
|
|
430
751
|
// src/ui/Welcome.tsx
|
|
431
752
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
432
|
-
import { jsx as
|
|
433
|
-
var IMAGE_PATH =
|
|
753
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
754
|
+
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
434
755
|
function SidebarItem({
|
|
435
756
|
title,
|
|
436
757
|
description
|
|
437
758
|
}) {
|
|
438
|
-
return /* @__PURE__ */
|
|
439
|
-
/* @__PURE__ */
|
|
440
|
-
/* @__PURE__ */
|
|
441
|
-
/* @__PURE__ */
|
|
759
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
760
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
|
|
761
|
+
/* @__PURE__ */ jsx6(Text6, { color: COLORS.success, children: "\u2192" }),
|
|
762
|
+
/* @__PURE__ */ jsx6(Text6, { color: COLORS.strong, bold: true, children: title })
|
|
442
763
|
] }),
|
|
443
|
-
/* @__PURE__ */
|
|
444
|
-
/* @__PURE__ */
|
|
445
|
-
/* @__PURE__ */
|
|
764
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", gap: 2, children: [
|
|
765
|
+
/* @__PURE__ */ jsx6(Spacer, {}),
|
|
766
|
+
/* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: description })
|
|
446
767
|
] })
|
|
447
768
|
] });
|
|
448
769
|
}
|
|
449
770
|
function Welcome() {
|
|
450
771
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
451
|
-
|
|
772
|
+
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
773
|
+
const { rows } = useWindowSize3();
|
|
774
|
+
useInput3((input) => {
|
|
452
775
|
if (input === " ") confirmStart();
|
|
776
|
+
else if (input === "i") openLearnMore();
|
|
453
777
|
});
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
778
|
+
const scales = {
|
|
779
|
+
large: {
|
|
780
|
+
sidebar: { padding: { x: 4, y: 2 }, gap: 2 },
|
|
781
|
+
main: { padding: { x: 8, y: 4 } }
|
|
782
|
+
},
|
|
783
|
+
small: {
|
|
784
|
+
sidebar: { padding: { x: 2, y: 1 }, gap: 1 },
|
|
785
|
+
main: { padding: { x: 4, y: 2 } }
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
let layout = scales["large"];
|
|
789
|
+
if (rows < 30) {
|
|
790
|
+
layout = scales["small"];
|
|
791
|
+
}
|
|
792
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
793
|
+
/* @__PURE__ */ jsx6(
|
|
794
|
+
Box6,
|
|
795
|
+
{
|
|
796
|
+
paddingY: layout.main.padding.y,
|
|
797
|
+
paddingX: layout.main.padding.x,
|
|
798
|
+
flexDirection: "column",
|
|
799
|
+
justifyContent: "center",
|
|
800
|
+
children: /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 2, children: [
|
|
801
|
+
/* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
|
|
802
|
+
Image,
|
|
803
|
+
{
|
|
804
|
+
src: IMAGE_PATH,
|
|
805
|
+
objectFit: "contain",
|
|
806
|
+
alt: "Algolia",
|
|
807
|
+
width: 20,
|
|
808
|
+
height: 10,
|
|
809
|
+
protocol: "halfBlock"
|
|
810
|
+
}
|
|
811
|
+
) }),
|
|
812
|
+
/* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
813
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
|
|
814
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "space" }),
|
|
815
|
+
/* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
816
|
+
] })
|
|
817
|
+
] })
|
|
818
|
+
}
|
|
819
|
+
),
|
|
820
|
+
/* @__PURE__ */ jsxs5(
|
|
821
|
+
Box6,
|
|
475
822
|
{
|
|
476
823
|
backgroundColor: COLORS.bg.sidebar,
|
|
477
824
|
width: 40,
|
|
478
|
-
|
|
479
|
-
|
|
825
|
+
paddingY: layout.sidebar.padding.y,
|
|
826
|
+
paddingX: layout.sidebar.padding.x,
|
|
827
|
+
gap: layout.sidebar.gap,
|
|
480
828
|
flexDirection: "column",
|
|
481
829
|
justifyContent: "center",
|
|
482
830
|
children: [
|
|
483
|
-
/* @__PURE__ */
|
|
484
|
-
sidebarItems.map((i, idx) => /* @__PURE__ */
|
|
831
|
+
/* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
832
|
+
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
485
833
|
]
|
|
486
834
|
}
|
|
487
835
|
)
|
|
488
836
|
] });
|
|
489
837
|
}
|
|
490
838
|
|
|
491
|
-
// src/ui/
|
|
492
|
-
import {
|
|
493
|
-
|
|
494
|
-
// src/ui/Steps.tsx
|
|
495
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
496
|
-
import Spinner from "ink-spinner";
|
|
839
|
+
// src/ui/LearnMore.tsx
|
|
840
|
+
import { Fragment as Fragment2 } from "react";
|
|
841
|
+
import { Box as Box7, Text as Text7, useInput as useInput4, useWindowSize as useWindowSize4 } from "ink";
|
|
497
842
|
|
|
498
|
-
// src/
|
|
499
|
-
|
|
500
|
-
|
|
843
|
+
// src/ui/copy/learn-more.ts
|
|
844
|
+
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
845
|
+
var accessItems = [
|
|
846
|
+
{
|
|
847
|
+
tag: "READ",
|
|
848
|
+
title: "Project files",
|
|
849
|
+
description: "reads package.json, configs & source to detect your stack. Read-only; nothing is uploaded."
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
tag: "WRITE",
|
|
853
|
+
title: "Code changes",
|
|
854
|
+
description: "creates & edits files (search UI, config). Shown as a diff first \u2014 nothing lands without your approval."
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
tag: "NET",
|
|
858
|
+
title: "Algolia API",
|
|
859
|
+
description: "sends index settings & the records you pick to your Algolia app over HTTPS."
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
tag: "KEY",
|
|
863
|
+
title: "Credentials",
|
|
864
|
+
description: "saves your Admin API key to .env and adds it to .gitignore."
|
|
865
|
+
}
|
|
866
|
+
];
|
|
867
|
+
var neverItems = [
|
|
868
|
+
"Send your source code to a model or third party",
|
|
869
|
+
"Commit or push to git",
|
|
870
|
+
"Touch files outside your project directory"
|
|
871
|
+
];
|
|
872
|
+
var policyLinks = [
|
|
873
|
+
{ label: "Terms", url: "https://www.algolia.com/policies/terms" },
|
|
874
|
+
{ label: "Privacy Policy", url: "https://www.algolia.com/policies/privacy" }
|
|
875
|
+
];
|
|
501
876
|
|
|
502
|
-
// src/
|
|
503
|
-
import {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
877
|
+
// src/ui/LearnMore.tsx
|
|
878
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
879
|
+
var TAG_COLORS = {
|
|
880
|
+
READ: COLORS.success,
|
|
881
|
+
WRITE: COLORS.badge,
|
|
882
|
+
NET: COLORS.accent,
|
|
883
|
+
KEY: COLORS.muted
|
|
884
|
+
};
|
|
885
|
+
var TAG_COLUMN_WIDTH = 10;
|
|
886
|
+
var PADDING_X = 6;
|
|
887
|
+
var NEVER_BOX_PAD_X = 2;
|
|
888
|
+
function NeverLine({
|
|
889
|
+
width,
|
|
890
|
+
segments = []
|
|
891
|
+
}) {
|
|
892
|
+
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
893
|
+
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
894
|
+
return /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
895
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: "\u2502" }),
|
|
896
|
+
" ".repeat(NEVER_BOX_PAD_X),
|
|
897
|
+
segments.map((s, i) => /* @__PURE__ */ jsx7(Text7, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
898
|
+
" ".repeat(rightPad),
|
|
899
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: "\u2502" })
|
|
900
|
+
] });
|
|
510
901
|
}
|
|
511
|
-
function
|
|
512
|
-
|
|
902
|
+
function LearnMore() {
|
|
903
|
+
const confirmStart = useWizard((s) => s.confirmStart);
|
|
904
|
+
const backToHome = useWizard((s) => s.backToHome);
|
|
905
|
+
const { columns } = useWindowSize4();
|
|
906
|
+
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
907
|
+
useInput4((input, key) => {
|
|
908
|
+
if (key.escape) backToHome();
|
|
909
|
+
else if (input === " ") confirmStart();
|
|
910
|
+
});
|
|
911
|
+
return /* @__PURE__ */ jsxs6(
|
|
912
|
+
Box7,
|
|
913
|
+
{
|
|
914
|
+
flexDirection: "column",
|
|
915
|
+
paddingX: PADDING_X,
|
|
916
|
+
paddingY: 2,
|
|
917
|
+
width: "100%",
|
|
918
|
+
gap: 1,
|
|
919
|
+
children: [
|
|
920
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
921
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: accessIntro }),
|
|
922
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
923
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
924
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
925
|
+
/* @__PURE__ */ jsx7(Box7, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text7, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
926
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
927
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.strong, bold: true, children: item.title }),
|
|
928
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
929
|
+
] }) })
|
|
930
|
+
] })
|
|
931
|
+
] }, item.tag)) }),
|
|
932
|
+
/* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
933
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
934
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
935
|
+
/* @__PURE__ */ jsx7(
|
|
936
|
+
NeverLine,
|
|
937
|
+
{
|
|
938
|
+
width: dividerWidth,
|
|
939
|
+
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
940
|
+
}
|
|
941
|
+
),
|
|
942
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
943
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
944
|
+
/* @__PURE__ */ jsx7(
|
|
945
|
+
NeverLine,
|
|
946
|
+
{
|
|
947
|
+
width: dividerWidth,
|
|
948
|
+
segments: [
|
|
949
|
+
{ text: "\u2715", color: COLORS.danger },
|
|
950
|
+
{ text: " " },
|
|
951
|
+
{ text: item, color: COLORS.primary }
|
|
952
|
+
]
|
|
953
|
+
}
|
|
954
|
+
)
|
|
955
|
+
] }, item)),
|
|
956
|
+
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
957
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
958
|
+
] }),
|
|
959
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
|
|
960
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
961
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.accent, children: link.url })
|
|
962
|
+
] }, link.label)) }),
|
|
963
|
+
/* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
964
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
|
|
965
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "[" }),
|
|
966
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.primary, children: "esc" }),
|
|
967
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "] back" })
|
|
968
|
+
] }),
|
|
969
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
|
|
970
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "[" }),
|
|
971
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.primary, children: "space" }),
|
|
972
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "]" }),
|
|
973
|
+
/* @__PURE__ */ jsx7(Text7, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
974
|
+
] })
|
|
975
|
+
] })
|
|
976
|
+
]
|
|
977
|
+
}
|
|
978
|
+
);
|
|
513
979
|
}
|
|
514
980
|
|
|
981
|
+
// src/ui/Sidebar.tsx
|
|
982
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
983
|
+
|
|
984
|
+
// src/ui/Steps.tsx
|
|
985
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
986
|
+
import Spinner from "ink-spinner";
|
|
987
|
+
|
|
515
988
|
// src/core/persistence.ts
|
|
989
|
+
import { mkdir, readFile, writeFile, rm } from "node:fs/promises";
|
|
990
|
+
import { join as join4 } from "node:path";
|
|
516
991
|
var isStepVisible = (s) => s.visible !== false;
|
|
517
|
-
var stateFile = (workflowId) =>
|
|
992
|
+
var stateFile = (workflowId) => join4(stateDir(), `state-${workflowId}.json`);
|
|
518
993
|
async function loadWorkflowState(workflowId) {
|
|
519
994
|
try {
|
|
520
995
|
const raw = await readFile(stateFile(workflowId), "utf8");
|
|
@@ -536,154 +1011,55 @@ async function clearWorkflowState(workflowId) {
|
|
|
536
1011
|
}
|
|
537
1012
|
|
|
538
1013
|
// src/ui/Steps.tsx
|
|
539
|
-
import { jsx as
|
|
540
|
-
function introspectDetail(output) {
|
|
541
|
-
if (!output || typeof output !== "object") return null;
|
|
542
|
-
const o = output;
|
|
543
|
-
if (!o.recordShape || typeof o.recordShape !== "object" || !Array.isArray(o.facets)) {
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
const fields = Object.entries(o.recordShape).map(
|
|
547
|
-
([name, type]) => ({ name, type: String(type) })
|
|
548
|
-
);
|
|
549
|
-
return { fields, facets: o.facets.map(String) };
|
|
550
|
-
}
|
|
551
|
-
function reviewDetail(output) {
|
|
552
|
-
if (!output || typeof output !== "object") return null;
|
|
553
|
-
const o = output;
|
|
554
|
-
if (!Array.isArray(o.stepSummaries) || typeof o.reviewPrompt !== "string" || !Array.isArray(o.nextSteps)) {
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
const stepSummaries = o.stepSummaries.filter((entry) => {
|
|
558
|
-
if (!entry || typeof entry !== "object") return false;
|
|
559
|
-
const s = entry;
|
|
560
|
-
return typeof s.stepId === "string" && typeof s.stepTitle === "string" && typeof s.summary === "string";
|
|
561
|
-
}).map((s) => ({
|
|
562
|
-
stepId: s.stepId,
|
|
563
|
-
stepTitle: s.stepTitle,
|
|
564
|
-
summary: s.summary
|
|
565
|
-
}));
|
|
566
|
-
return {
|
|
567
|
-
stepSummaries,
|
|
568
|
-
reviewPrompt: o.reviewPrompt,
|
|
569
|
-
nextSteps: o.nextSteps.map(String)
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
function plural(count, noun) {
|
|
573
|
-
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
574
|
-
}
|
|
575
|
-
function IntrospectSummary({ detail }) {
|
|
576
|
-
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
577
|
-
/* @__PURE__ */ jsxs5(Text6, { color: "gray", children: [
|
|
578
|
-
" ",
|
|
579
|
-
"Inferred ",
|
|
580
|
-
plural(detail.fields.length, "field"),
|
|
581
|
-
",",
|
|
582
|
-
" ",
|
|
583
|
-
plural(detail.facets.length, "facet")
|
|
584
|
-
] }),
|
|
585
|
-
detail.fields.map((f) => /* @__PURE__ */ jsxs5(Text6, { color: "gray", children: [
|
|
586
|
-
" ",
|
|
587
|
-
f.name,
|
|
588
|
-
": ",
|
|
589
|
-
f.type
|
|
590
|
-
] }, f.name)),
|
|
591
|
-
/* @__PURE__ */ jsxs5(Text6, { color: "gray", children: [
|
|
592
|
-
" ",
|
|
593
|
-
"Facets:",
|
|
594
|
-
" ",
|
|
595
|
-
detail.facets.length ? detail.facets.join(", ") : "none"
|
|
596
|
-
] })
|
|
597
|
-
] });
|
|
598
|
-
}
|
|
599
|
-
function ReviewSummary({ detail }) {
|
|
600
|
-
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
601
|
-
detail.stepSummaries.map((s) => /* @__PURE__ */ jsxs5(Text6, { color: "gray", children: [
|
|
602
|
-
" ",
|
|
603
|
-
s.stepTitle,
|
|
604
|
-
": ",
|
|
605
|
-
s.summary
|
|
606
|
-
] }, s.stepId)),
|
|
607
|
-
/* @__PURE__ */ jsxs5(Text6, { color: COLORS.brand, children: [
|
|
608
|
-
" ",
|
|
609
|
-
detail.reviewPrompt
|
|
610
|
-
] }),
|
|
611
|
-
detail.nextSteps.map((step, i) => {
|
|
612
|
-
const isIngestCommand = step.includes(".algolia-wizard/ingest.sh");
|
|
613
|
-
const isWorktreeCommand = step.includes("/worktrees/");
|
|
614
|
-
const color = isIngestCommand ? COLORS.brand : isWorktreeCommand ? COLORS.secondary : "gray";
|
|
615
|
-
return /* @__PURE__ */ jsxs5(
|
|
616
|
-
Text6,
|
|
617
|
-
{
|
|
618
|
-
color,
|
|
619
|
-
bold: isIngestCommand || isWorktreeCommand,
|
|
620
|
-
children: [
|
|
621
|
-
" ",
|
|
622
|
-
"\u2192 ",
|
|
623
|
-
step
|
|
624
|
-
]
|
|
625
|
-
},
|
|
626
|
-
`next-${i}`
|
|
627
|
-
);
|
|
628
|
-
})
|
|
629
|
-
] });
|
|
630
|
-
}
|
|
1014
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
631
1015
|
function Steps() {
|
|
632
1016
|
const { steps } = useWizard();
|
|
633
1017
|
const visibleSteps = steps.filter(isStepVisible);
|
|
634
|
-
return /* @__PURE__ */
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
s.status === "running" ? /* @__PURE__ */ jsx6(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
640
|
-
" ",
|
|
641
|
-
s.title
|
|
642
|
-
] }),
|
|
643
|
-
introspect && /* @__PURE__ */ jsx6(IntrospectSummary, { detail: introspect }),
|
|
644
|
-
review && /* @__PURE__ */ jsx6(ReviewSummary, { detail: review })
|
|
645
|
-
] }, s.id);
|
|
646
|
-
}) });
|
|
1018
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text8, { color: COLORS.status[s.status], children: [
|
|
1019
|
+
s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1020
|
+
" ",
|
|
1021
|
+
s.title
|
|
1022
|
+
] }) }, s.id)) });
|
|
647
1023
|
}
|
|
648
1024
|
function CurrentStep() {
|
|
649
1025
|
const { steps } = useWizard();
|
|
650
1026
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
651
1027
|
if (!currentStep) return null;
|
|
652
|
-
return /* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
1028
|
+
return /* @__PURE__ */ jsxs7(Text8, { color: COLORS.status.running, children: [
|
|
1029
|
+
/* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
|
|
654
1030
|
" ",
|
|
655
1031
|
` ${currentStep.title}`
|
|
656
1032
|
] });
|
|
657
1033
|
}
|
|
658
1034
|
|
|
659
1035
|
// src/ui/Progress.tsx
|
|
660
|
-
import { Box as
|
|
661
|
-
import { jsx as
|
|
1036
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
1037
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
662
1038
|
function Progress() {
|
|
663
1039
|
const { steps, currentStepIndex } = useWizard();
|
|
664
1040
|
const visibleSteps = steps.filter(isStepVisible);
|
|
665
1041
|
if (visibleSteps.length === 0) return null;
|
|
666
1042
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
667
1043
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
668
|
-
return /* @__PURE__ */
|
|
669
|
-
/* @__PURE__ */
|
|
670
|
-
/* @__PURE__ */
|
|
671
|
-
/* @__PURE__ */
|
|
672
|
-
/* @__PURE__ */
|
|
1044
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
|
|
1045
|
+
/* @__PURE__ */ jsx9(Text9, { color: COLORS.muted, children: "STEP" }),
|
|
1046
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: activeStepNumber }),
|
|
1047
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "/" }),
|
|
1048
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: visibleSteps.length })
|
|
673
1049
|
] });
|
|
674
1050
|
}
|
|
675
1051
|
|
|
676
1052
|
// src/ui/copy/sidebar-commands.ts
|
|
677
1053
|
var sidebarCommands = [
|
|
678
1054
|
{ keyHint: "tab", description: "toggle logs" },
|
|
679
|
-
{ keyHint: "esc", description: "
|
|
1055
|
+
{ keyHint: "esc", description: "exit wizard" }
|
|
680
1056
|
];
|
|
681
1057
|
|
|
682
1058
|
// src/ui/Sidebar.tsx
|
|
683
|
-
import { jsx as
|
|
1059
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
684
1060
|
function Sidebar() {
|
|
685
|
-
return /* @__PURE__ */
|
|
686
|
-
|
|
1061
|
+
return /* @__PURE__ */ jsxs9(
|
|
1062
|
+
Box10,
|
|
687
1063
|
{
|
|
688
1064
|
backgroundColor: "#14171E",
|
|
689
1065
|
width: 30,
|
|
@@ -692,16 +1068,16 @@ function Sidebar() {
|
|
|
692
1068
|
flexDirection: "column",
|
|
693
1069
|
justifyContent: "space-between",
|
|
694
1070
|
children: [
|
|
695
|
-
/* @__PURE__ */
|
|
696
|
-
/* @__PURE__ */
|
|
697
|
-
/* @__PURE__ */
|
|
1071
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
|
|
1072
|
+
/* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1073
|
+
/* @__PURE__ */ jsx10(Steps, {})
|
|
698
1074
|
] }),
|
|
699
|
-
/* @__PURE__ */
|
|
700
|
-
/* @__PURE__ */
|
|
701
|
-
/* @__PURE__ */
|
|
702
|
-
return /* @__PURE__ */
|
|
703
|
-
/* @__PURE__ */
|
|
704
|
-
/* @__PURE__ */
|
|
1075
|
+
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
|
|
1076
|
+
/* @__PURE__ */ jsx10(Progress, {}),
|
|
1077
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1078
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1079
|
+
/* @__PURE__ */ jsx10(Text10, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1080
|
+
/* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: c.description })
|
|
705
1081
|
] });
|
|
706
1082
|
}) })
|
|
707
1083
|
] })
|
|
@@ -711,12 +1087,12 @@ function Sidebar() {
|
|
|
711
1087
|
}
|
|
712
1088
|
|
|
713
1089
|
// src/ui/Ribbon.tsx
|
|
714
|
-
import { Box as
|
|
715
|
-
import { jsx as
|
|
1090
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1091
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
716
1092
|
function Ribbon() {
|
|
717
1093
|
const firstCommand = sidebarCommands[0];
|
|
718
|
-
return /* @__PURE__ */
|
|
719
|
-
|
|
1094
|
+
return /* @__PURE__ */ jsxs10(
|
|
1095
|
+
Box11,
|
|
720
1096
|
{
|
|
721
1097
|
backgroundColor: "#14171E",
|
|
722
1098
|
flexDirection: "row",
|
|
@@ -724,11 +1100,11 @@ function Ribbon() {
|
|
|
724
1100
|
paddingX: 2,
|
|
725
1101
|
paddingY: 1,
|
|
726
1102
|
children: [
|
|
727
|
-
/* @__PURE__ */
|
|
728
|
-
/* @__PURE__ */
|
|
729
|
-
/* @__PURE__ */
|
|
730
|
-
/* @__PURE__ */
|
|
731
|
-
/* @__PURE__ */
|
|
1103
|
+
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1104
|
+
/* @__PURE__ */ jsx11(CurrentStep, {}),
|
|
1105
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1106
|
+
/* @__PURE__ */ jsx11(Text11, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1107
|
+
/* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: firstCommand.description })
|
|
732
1108
|
] })
|
|
733
1109
|
]
|
|
734
1110
|
}
|
|
@@ -736,36 +1112,16 @@ function Ribbon() {
|
|
|
736
1112
|
}
|
|
737
1113
|
|
|
738
1114
|
// src/ui/App.tsx
|
|
739
|
-
import { jsx as
|
|
1115
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
740
1116
|
function App() {
|
|
741
|
-
const { phase, error } = useWizard();
|
|
742
|
-
const implementOutput = useWizard((s) => {
|
|
743
|
-
return s.steps.find((st) => st.id === "ingestion")?.output;
|
|
744
|
-
});
|
|
745
|
-
const ingestCommand = implementOutput?.ingestCommand;
|
|
746
|
-
const openWorktreeCommand = implementOutput?.worktreePath ? `cd ${shellQuote(implementOutput.worktreePath)}` : void 0;
|
|
1117
|
+
const { phase, error, homeScreen } = useWizard();
|
|
747
1118
|
const { exit } = useApp();
|
|
748
|
-
const { columns, rows } =
|
|
749
|
-
const [copied, setCopied] = useState4(null);
|
|
1119
|
+
const { columns, rows } = useWindowSize5();
|
|
750
1120
|
const finished = phase === "done" || phase === "error";
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const timer = setTimeout(() => setCopied(null), 5e3);
|
|
754
|
-
return () => clearTimeout(timer);
|
|
755
|
-
}, [copied]);
|
|
756
|
-
useInput3(
|
|
757
|
-
(input, key) => {
|
|
1121
|
+
useInput5(
|
|
1122
|
+
(_input, key) => {
|
|
758
1123
|
if (key.return || key.escape) {
|
|
759
1124
|
exit();
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
if (ingestCommand && (input === "c" || input === "C")) {
|
|
763
|
-
copyToClipboard(ingestCommand);
|
|
764
|
-
setCopied("ingest");
|
|
765
|
-
}
|
|
766
|
-
if (openWorktreeCommand && (input === "d" || input === "D")) {
|
|
767
|
-
copyToClipboard(openWorktreeCommand);
|
|
768
|
-
setCopied("worktree");
|
|
769
1125
|
}
|
|
770
1126
|
},
|
|
771
1127
|
{ isActive: finished }
|
|
@@ -773,38 +1129,36 @@ function App() {
|
|
|
773
1129
|
const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
774
1130
|
const flexDirection = columns > 90 ? "row" : "column";
|
|
775
1131
|
const showSidebar = flexDirection === "row";
|
|
776
|
-
return /* @__PURE__ */
|
|
777
|
-
|
|
1132
|
+
return /* @__PURE__ */ jsxs11(
|
|
1133
|
+
Box12,
|
|
778
1134
|
{
|
|
779
1135
|
backgroundColor: COLORS.bg.main,
|
|
780
1136
|
flexDirection: "row",
|
|
781
1137
|
width: columns,
|
|
782
1138
|
minHeight: rows,
|
|
783
1139
|
children: [
|
|
784
|
-
mainWindowVisible && /* @__PURE__ */
|
|
785
|
-
|
|
1140
|
+
mainWindowVisible && /* @__PURE__ */ jsxs11(
|
|
1141
|
+
Box12,
|
|
786
1142
|
{
|
|
787
1143
|
flexDirection,
|
|
788
1144
|
width: "100%",
|
|
789
1145
|
justifyContent: "space-between",
|
|
790
1146
|
children: [
|
|
791
|
-
/* @__PURE__ */
|
|
792
|
-
/* @__PURE__ */
|
|
793
|
-
|
|
1147
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingX: 4, paddingY: 2, width: 70, children: [
|
|
1148
|
+
/* @__PURE__ */ jsx12(Notices, {}),
|
|
1149
|
+
/* @__PURE__ */ jsx12(PromptInput, {}),
|
|
1150
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(CurrentStep, {}) }),
|
|
1151
|
+
phase === "error" && error && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: COLORS.status.error, children: [
|
|
794
1152
|
"\u2716 ",
|
|
795
1153
|
error
|
|
796
|
-
] }) })
|
|
797
|
-
finished && (ingestCommand || openWorktreeCommand) && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
798
|
-
ingestCommand && /* @__PURE__ */ jsx10(Text10, { color: COLORS.brand, children: copied === "ingest" ? "\u2713 Ingestion command copied to clipboard" : 'Press "c" to copy the ingestion command' }),
|
|
799
|
-
openWorktreeCommand && /* @__PURE__ */ jsx10(Text10, { color: COLORS.secondary, children: copied === "worktree" ? "\u2713 Worktree command copied to clipboard" : 'Press "d" to copy the command to open the worktree' })
|
|
800
|
-
] })
|
|
1154
|
+
] }) })
|
|
801
1155
|
] }),
|
|
802
|
-
/* @__PURE__ */
|
|
803
|
-
showSidebar ? /* @__PURE__ */
|
|
1156
|
+
/* @__PURE__ */ jsx12(Text12, { color: "white", backgroundColor: "#14171E" }),
|
|
1157
|
+
showSidebar ? /* @__PURE__ */ jsx12(Sidebar, {}) : /* @__PURE__ */ jsx12(Ribbon, {})
|
|
804
1158
|
]
|
|
805
1159
|
}
|
|
806
1160
|
),
|
|
807
|
-
(phase === "idle" || phase === "preflight") && /* @__PURE__ */
|
|
1161
|
+
(phase === "idle" || phase === "preflight") && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx12(LearnMore, {}) : /* @__PURE__ */ jsx12(Welcome, {}))
|
|
808
1162
|
]
|
|
809
1163
|
}
|
|
810
1164
|
);
|
|
@@ -815,8 +1169,8 @@ import "zod";
|
|
|
815
1169
|
|
|
816
1170
|
// src/core/config.ts
|
|
817
1171
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
818
|
-
import { join as
|
|
819
|
-
var configFile = () =>
|
|
1172
|
+
import { join as join5 } from "node:path";
|
|
1173
|
+
var configFile = () => join5(stateDir(), "config.json");
|
|
820
1174
|
var DEFAULT_CONFIG = {
|
|
821
1175
|
version: 1,
|
|
822
1176
|
aiConsent: false,
|
|
@@ -840,128 +1194,6 @@ async function recordWorkflowRun(workflowId, completedAt) {
|
|
|
840
1194
|
await saveConfig(config);
|
|
841
1195
|
}
|
|
842
1196
|
|
|
843
|
-
// src/lib/algoliaCli.ts
|
|
844
|
-
import { spawn } from "node:child_process";
|
|
845
|
-
import { createRequire } from "node:module";
|
|
846
|
-
var require2 = createRequire(import.meta.url);
|
|
847
|
-
function algoliaCliEntry() {
|
|
848
|
-
return require2.resolve("@algolia/cli/bin/run.js");
|
|
849
|
-
}
|
|
850
|
-
function runAlgoliaCli(args) {
|
|
851
|
-
return new Promise((resolve4, reject) => {
|
|
852
|
-
const child = spawn(process.execPath, [algoliaCliEntry(), ...args]);
|
|
853
|
-
let stdout = "";
|
|
854
|
-
let stderr = "";
|
|
855
|
-
child.stdout.on("data", (chunk) => stdout += chunk);
|
|
856
|
-
child.stderr.on("data", (chunk) => stderr += chunk);
|
|
857
|
-
child.on("error", reject);
|
|
858
|
-
child.on("close", (code) => {
|
|
859
|
-
if (code === 0) {
|
|
860
|
-
resolve4(stdout);
|
|
861
|
-
} else {
|
|
862
|
-
const detail = stderr.trim() || stdout.trim();
|
|
863
|
-
reject(
|
|
864
|
-
new Error(
|
|
865
|
-
`Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail ? `: ${detail}` : ""}`
|
|
866
|
-
)
|
|
867
|
-
);
|
|
868
|
-
}
|
|
869
|
-
});
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
async function getUser() {
|
|
873
|
-
let raw;
|
|
874
|
-
try {
|
|
875
|
-
raw = await runAlgoliaCli(["auth", "get", "--with-access-token"]);
|
|
876
|
-
} catch {
|
|
877
|
-
return null;
|
|
878
|
-
}
|
|
879
|
-
try {
|
|
880
|
-
return toUserInfo(JSON.parse(raw));
|
|
881
|
-
} catch {
|
|
882
|
-
return null;
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
function runAuthLogin() {
|
|
886
|
-
return new Promise((resolve4, reject) => {
|
|
887
|
-
const child = spawn(
|
|
888
|
-
process.execPath,
|
|
889
|
-
[algoliaCliEntry(), "auth", "login", "--default"],
|
|
890
|
-
{ stdio: "inherit" }
|
|
891
|
-
);
|
|
892
|
-
child.on("error", reject);
|
|
893
|
-
child.on("close", (code) => {
|
|
894
|
-
if (code === 0) resolve4();
|
|
895
|
-
else reject(new Error(`Algolia authentication failed (exit ${code}).`));
|
|
896
|
-
});
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// src/lib/auth.ts
|
|
901
|
-
function getAuthToken() {
|
|
902
|
-
return useWizard.getState().user?.token || null;
|
|
903
|
-
}
|
|
904
|
-
var inFlightRefresh = null;
|
|
905
|
-
function refreshAuthToken() {
|
|
906
|
-
inFlightRefresh ??= (async () => {
|
|
907
|
-
try {
|
|
908
|
-
const raw = await runAlgoliaCli(["auth", "get", "--with-access-token"]);
|
|
909
|
-
const user2 = toUserInfo(JSON.parse(raw));
|
|
910
|
-
useWizard.getState().setUser(user2);
|
|
911
|
-
return user2.token;
|
|
912
|
-
} catch {
|
|
913
|
-
return null;
|
|
914
|
-
} finally {
|
|
915
|
-
inFlightRefresh = null;
|
|
916
|
-
}
|
|
917
|
-
})();
|
|
918
|
-
return inFlightRefresh;
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
// src/lib/logger.ts
|
|
922
|
-
import pino from "pino";
|
|
923
|
-
import { join as join5, dirname as dirname2 } from "node:path";
|
|
924
|
-
import { devNull } from "node:os";
|
|
925
|
-
import { mkdirSync, openSync, closeSync } from "node:fs";
|
|
926
|
-
var STDERR_FD = 2;
|
|
927
|
-
function resolveDest() {
|
|
928
|
-
const target = process.env.VITEST ? devNull : process.env.WIZARD_LOG ?? join5(stateDir(), "wizard.log");
|
|
929
|
-
try {
|
|
930
|
-
mkdirSync(dirname2(target), { recursive: true });
|
|
931
|
-
closeSync(openSync(target, "a"));
|
|
932
|
-
return target;
|
|
933
|
-
} catch {
|
|
934
|
-
return STDERR_FD;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
function logDestination() {
|
|
938
|
-
return pino.destination({ dest: resolveDest(), sync: false });
|
|
939
|
-
}
|
|
940
|
-
var logger = pino(
|
|
941
|
-
{ level: process.env.LOG_LEVEL ?? "info" },
|
|
942
|
-
logDestination()
|
|
943
|
-
);
|
|
944
|
-
|
|
945
|
-
// src/lib/proxyFetch.ts
|
|
946
|
-
var PROXY_BASE_URL = process.env.PROXY_BASE_URL ?? "https://proxy-624203421261.us-east4.run.app";
|
|
947
|
-
var PROXY_AUTH_REJECTED_HEADER = "x-wizard-proxy-auth";
|
|
948
|
-
var proxyFetch = async (input, init) => {
|
|
949
|
-
const req = new Request(input, init);
|
|
950
|
-
const retry = req.clone();
|
|
951
|
-
const res = await fetch(req);
|
|
952
|
-
if (res.status !== 401) return res;
|
|
953
|
-
if (res.headers.get(PROXY_AUTH_REJECTED_HEADER) !== "rejected") return res;
|
|
954
|
-
const fresh = await refreshAuthToken();
|
|
955
|
-
if (!fresh) return res;
|
|
956
|
-
const headers = new Headers(retry.headers);
|
|
957
|
-
if (headers.has("authorization")) {
|
|
958
|
-
headers.set("authorization", `Bearer ${fresh}`);
|
|
959
|
-
} else {
|
|
960
|
-
headers.set("x-api-key", fresh);
|
|
961
|
-
}
|
|
962
|
-
return fetch(new Request(retry, { headers }));
|
|
963
|
-
};
|
|
964
|
-
|
|
965
1197
|
// src/lib/telemetry.ts
|
|
966
1198
|
function isTelemetryEnabled() {
|
|
967
1199
|
return Boolean(getAuthToken()) && !process.env.VITEST && process.env.WIZARD_TELEMETRY !== "false";
|
|
@@ -1186,12 +1418,14 @@ async function ensureConsent() {
|
|
|
1186
1418
|
if (config.aiConsent) return;
|
|
1187
1419
|
const store2 = useWizard.getState();
|
|
1188
1420
|
const answer = await store2.requestUserInput({
|
|
1189
|
-
prompt:
|
|
1190
|
-
promptType: "
|
|
1421
|
+
prompt: "Wizard will make AI-authored changes to this repository.",
|
|
1422
|
+
promptType: "spaceToContinue",
|
|
1191
1423
|
options: []
|
|
1192
1424
|
});
|
|
1193
|
-
if (
|
|
1194
|
-
throw new Error(
|
|
1425
|
+
if (answer !== true) {
|
|
1426
|
+
throw new Error(
|
|
1427
|
+
"AI consent declined \u2014 cannot proceed. If you change your mind, just run the Wizard again!"
|
|
1428
|
+
);
|
|
1195
1429
|
}
|
|
1196
1430
|
config.aiConsent = true;
|
|
1197
1431
|
await saveConfig(config);
|
|
@@ -1212,6 +1446,8 @@ async function makeContext(state) {
|
|
|
1212
1446
|
completedSteps,
|
|
1213
1447
|
getStepOutput: (stepId) => outputs[stepId],
|
|
1214
1448
|
requestUserInput: (prompt) => useWizard.getState().requestUserInput(prompt),
|
|
1449
|
+
notify: (notice) => useWizard.getState().pushNotice(notice),
|
|
1450
|
+
clearNotices: () => useWizard.getState().clearNotices(),
|
|
1215
1451
|
updateAlgoliaState: (key, value) => {
|
|
1216
1452
|
state.algoliaState[key] = value;
|
|
1217
1453
|
},
|
|
@@ -1243,6 +1479,7 @@ async function runStep(state, index, step, appId) {
|
|
|
1243
1479
|
store2.setActiveStep(index);
|
|
1244
1480
|
store2.syncSteps([...state.steps], index);
|
|
1245
1481
|
await saveWorkflowState(state);
|
|
1482
|
+
await markInteraction();
|
|
1246
1483
|
const ctx = await makeContext(state);
|
|
1247
1484
|
const raw = await step.run(ctx);
|
|
1248
1485
|
const output = step.outputSchema.parse(raw);
|
|
@@ -1263,7 +1500,6 @@ async function runStep(state, index, step, appId) {
|
|
|
1263
1500
|
async function runWorkflow(workflow2, appId) {
|
|
1264
1501
|
const store2 = useWizard.getState();
|
|
1265
1502
|
try {
|
|
1266
|
-
await ensureConsent();
|
|
1267
1503
|
const persisted = await loadWorkflowState(workflow2.id);
|
|
1268
1504
|
const state = (persisted && reconcileWorkflowState(persisted, workflow2)) ?? initWorkflowState(workflow2, nowIso());
|
|
1269
1505
|
ensureExecutedStepCount(state);
|
|
@@ -1275,6 +1511,7 @@ async function runWorkflow(workflow2, appId) {
|
|
|
1275
1511
|
},
|
|
1276
1512
|
[...state.steps]
|
|
1277
1513
|
);
|
|
1514
|
+
await ensureConsent();
|
|
1278
1515
|
trackWorkflowStart({ workflowId: workflow2.id, appId });
|
|
1279
1516
|
for (let i = state.currentStepIndex; i < workflow2.steps.length; i++) {
|
|
1280
1517
|
await runStep(state, i, workflow2.steps[i], appId);
|
|
@@ -1384,7 +1621,7 @@ async function loadActiveProfile() {
|
|
|
1384
1621
|
}
|
|
1385
1622
|
|
|
1386
1623
|
// src/workflows/default.ts
|
|
1387
|
-
import { z as
|
|
1624
|
+
import { z as z25 } from "zod";
|
|
1388
1625
|
|
|
1389
1626
|
// src/actions/listIndices.ts
|
|
1390
1627
|
import { z as z3 } from "zod";
|
|
@@ -1864,7 +2101,10 @@ function verifyImplementationTool() {
|
|
|
1864
2101
|
import { tool as tool9, generateText, Output, NoObjectGeneratedError } from "ai";
|
|
1865
2102
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
1866
2103
|
import { nanoid } from "nanoid";
|
|
2104
|
+
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2105
|
+
import { dirname as dirname6 } from "node:path";
|
|
1867
2106
|
import z12 from "zod";
|
|
2107
|
+
var DATA_DIR = ".algolia-wizard/data";
|
|
1868
2108
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
1869
2109
|
var MAX_RECORDS = 100;
|
|
1870
2110
|
var BATCH_SIZE = 10;
|
|
@@ -1872,9 +2112,9 @@ var MAX_BATCH_ATTEMPTS = 3;
|
|
|
1872
2112
|
var anthropic = createAnthropic({
|
|
1873
2113
|
apiKey: process.env.PROVIDER_API_KEY ?? ""
|
|
1874
2114
|
});
|
|
1875
|
-
function generateRecordTool() {
|
|
2115
|
+
function generateRecordTool(ctx) {
|
|
1876
2116
|
return tool9({
|
|
1877
|
-
description: "Generate realistic sample records for an entity. Provide the entity name and its attributes; this tool asks a model to invent varied, realistic values and returns
|
|
2117
|
+
description: "Generate realistic sample records for an entity and write them to a JSON file in the worktree. Provide the entity name and its attributes; this tool asks a model to invent varied, realistic values, each with a unique objectID, and returns the file path to read them from at runtime. Do not invent the record values or objectIDs yourself, and do not inline the returned records into the script \u2014 call this tool and read the file it writes.",
|
|
1878
2118
|
inputSchema: z12.object({
|
|
1879
2119
|
entityName: z12.string().describe("Name of the entity to generate records for."),
|
|
1880
2120
|
attributes: z12.array(z12.string()).describe("Attribute names each record must contain."),
|
|
@@ -1930,8 +2170,21 @@ function generateRecordTool() {
|
|
|
1930
2170
|
...record,
|
|
1931
2171
|
objectID: nanoid()
|
|
1932
2172
|
}));
|
|
1933
|
-
|
|
1934
|
-
|
|
2173
|
+
const slug = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
2174
|
+
const relPath = `${DATA_DIR}/${slug}.json`;
|
|
2175
|
+
const resolved = resolveInRoot(ctx, relPath);
|
|
2176
|
+
if (resolved.ok === false) return resolved.error;
|
|
2177
|
+
if (await hasSymlinkParent(ctx, resolved.target)) {
|
|
2178
|
+
return `Refused: ${resolved.target} is outside the repo root (${ctx.root}).`;
|
|
2179
|
+
}
|
|
2180
|
+
await mkdir5(dirname6(resolved.target), { recursive: true });
|
|
2181
|
+
await writeFile5(resolved.target, JSON.stringify(records, null, 2), "utf8");
|
|
2182
|
+
logger.info({ entityName, count: records.length, relPath }, "generateRecord wrote records to disk");
|
|
2183
|
+
return {
|
|
2184
|
+
filePath: relPath,
|
|
2185
|
+
count: records.length,
|
|
2186
|
+
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.`
|
|
2187
|
+
};
|
|
1935
2188
|
} catch (err) {
|
|
1936
2189
|
return `Error generating records: ${err.message}`;
|
|
1937
2190
|
}
|
|
@@ -1939,6 +2192,25 @@ function generateRecordTool() {
|
|
|
1939
2192
|
});
|
|
1940
2193
|
}
|
|
1941
2194
|
|
|
2195
|
+
// src/lib/tools/notifyUser.ts
|
|
2196
|
+
import { tool as tool10 } from "ai";
|
|
2197
|
+
import z13 from "zod";
|
|
2198
|
+
function notifyUserTool() {
|
|
2199
|
+
return tool10({
|
|
2200
|
+
description: `Give the user a brief, high-level update on what you are currently doing or about to do next. This is for the big picture (e.g. "Reading through your data models", "Writing the search UI") \u2014 not granular detail like individual tool calls, which are already logged separately. Call it when you start a new phase of work or your focus shifts, just not on every step, enough to keep the user engaged. Don't say things like "starting", just describe what you are doing. Don't mention tool calls themselves, just general direction of the work.`,
|
|
2201
|
+
inputSchema: z13.object({
|
|
2202
|
+
message: z13.string().describe(
|
|
2203
|
+
"Short, plain-language description of what you are doing now."
|
|
2204
|
+
)
|
|
2205
|
+
}),
|
|
2206
|
+
execute: async ({ message }) => {
|
|
2207
|
+
logger.info({ message }, "called notifyUser tool");
|
|
2208
|
+
useWizard.getState().pushNotice({ messages: [message] });
|
|
2209
|
+
return "ok";
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
|
|
1942
2214
|
// src/lib/tools/context.ts
|
|
1943
2215
|
var DEFAULT_TOOL_LIMITS = {
|
|
1944
2216
|
list: 10,
|
|
@@ -1966,10 +2238,11 @@ function createTools(ctx, { output, tools }) {
|
|
|
1966
2238
|
writeCredentials: writeCredentialsTool(ctx),
|
|
1967
2239
|
searchFiles: searchFilesTool(ctx),
|
|
1968
2240
|
verifyImplementation: verifyImplementationTool(),
|
|
1969
|
-
generateRecord: generateRecordTool()
|
|
2241
|
+
generateRecord: generateRecordTool(ctx),
|
|
2242
|
+
notifyUser: notifyUserTool()
|
|
1970
2243
|
};
|
|
1971
2244
|
if (!tools) return all;
|
|
1972
|
-
const selection = /* @__PURE__ */ new Set([...tools, "reportStatus"]);
|
|
2245
|
+
const selection = /* @__PURE__ */ new Set([...tools, "reportStatus", "notifyUser"]);
|
|
1973
2246
|
return Object.fromEntries(
|
|
1974
2247
|
Object.entries(all).filter(([name]) => selection.has(name))
|
|
1975
2248
|
);
|
|
@@ -2002,16 +2275,19 @@ async function runAgent(req) {
|
|
|
2002
2275
|
const toolContext = createToolContext();
|
|
2003
2276
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
2004
2277
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
2005
|
-
const instructions =
|
|
2278
|
+
const instructions = [
|
|
2006
2279
|
...req.instructions ?? [],
|
|
2007
|
-
|
|
2008
|
-
|
|
2280
|
+
...hasReadTools ? [
|
|
2281
|
+
"When you need to read or search multiple files, issue those tool calls together in one step rather than one at a time."
|
|
2282
|
+
] : [],
|
|
2283
|
+
"Call notifyUser whenever you start a new phase of work or your focus shifts (e.g. moving from reading code to writing files) \u2014 a short, plain-language, high-level update. Do not call it for every tool use."
|
|
2284
|
+
];
|
|
2009
2285
|
const agent = new ToolLoopAgent({
|
|
2010
2286
|
model: anthropic2(MODEL_BY_SIZE[req.modelSize ?? "medium"]),
|
|
2011
2287
|
// Cache tools + system on the last system block. Tools render before
|
|
2012
2288
|
// system, so one breakpoint here caches both, reused on every loop turn
|
|
2013
2289
|
// after the first.
|
|
2014
|
-
instructions: instructions
|
|
2290
|
+
instructions: instructions.map((i, idx, arr) => {
|
|
2015
2291
|
return {
|
|
2016
2292
|
role: "system",
|
|
2017
2293
|
content: i,
|
|
@@ -2079,10 +2355,10 @@ async function runAgent(req) {
|
|
|
2079
2355
|
}
|
|
2080
2356
|
|
|
2081
2357
|
// src/actions/detectLanguage.ts
|
|
2082
|
-
import
|
|
2083
|
-
var detectLanguageSchema =
|
|
2084
|
-
languages:
|
|
2085
|
-
frameworks:
|
|
2358
|
+
import z16 from "zod";
|
|
2359
|
+
var detectLanguageSchema = z16.object({
|
|
2360
|
+
languages: z16.array(z16.object({ name: z16.string(), version: z16.string() })),
|
|
2361
|
+
frameworks: z16.array(z16.object({ name: z16.string(), version: z16.string() }))
|
|
2086
2362
|
});
|
|
2087
2363
|
var detectLanguage = () => runAgent({
|
|
2088
2364
|
instructions: [
|
|
@@ -2100,31 +2376,31 @@ var detectLanguage = () => runAgent({
|
|
|
2100
2376
|
});
|
|
2101
2377
|
|
|
2102
2378
|
// src/actions/analyzeCodebase.ts
|
|
2103
|
-
import
|
|
2379
|
+
import z17 from "zod";
|
|
2104
2380
|
var READONLY_TOOLS = [
|
|
2105
2381
|
"listFiles",
|
|
2106
2382
|
"changeDirectory",
|
|
2107
2383
|
"readFile",
|
|
2108
2384
|
"searchFiles"
|
|
2109
2385
|
];
|
|
2110
|
-
var ingestionAnalysisSchema =
|
|
2111
|
-
ingestionAnalysis:
|
|
2112
|
-
|
|
2113
|
-
name:
|
|
2114
|
-
paths:
|
|
2386
|
+
var ingestionAnalysisSchema = z17.object({
|
|
2387
|
+
ingestionAnalysis: z17.array(
|
|
2388
|
+
z17.object({
|
|
2389
|
+
name: z17.string(),
|
|
2390
|
+
paths: z17.array(z17.string()),
|
|
2115
2391
|
// indexable fields the agent found for this entity
|
|
2116
|
-
attributes:
|
|
2392
|
+
attributes: z17.array(z17.string())
|
|
2117
2393
|
})
|
|
2118
2394
|
)
|
|
2119
2395
|
});
|
|
2120
|
-
var searchImplementationAnalysisSchema =
|
|
2121
|
-
searchImplementationAnalysis:
|
|
2396
|
+
var searchImplementationAnalysisSchema = z17.object({
|
|
2397
|
+
searchImplementationAnalysis: z17.string()
|
|
2122
2398
|
});
|
|
2123
|
-
var verificationSchema =
|
|
2124
|
-
verification:
|
|
2399
|
+
var verificationSchema = z17.object({
|
|
2400
|
+
verification: z17.array(z17.string())
|
|
2125
2401
|
});
|
|
2126
2402
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
2127
|
-
var analyzeCodebaseSchema =
|
|
2403
|
+
var analyzeCodebaseSchema = z17.object({
|
|
2128
2404
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2129
2405
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
2130
2406
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -2186,7 +2462,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
2186
2462
|
// package.json
|
|
2187
2463
|
var package_default = {
|
|
2188
2464
|
name: "@algolia/wizard",
|
|
2189
|
-
version: "0.3.0-rc.
|
|
2465
|
+
version: "0.3.0-rc.47.9",
|
|
2190
2466
|
description: "Magically implement Algolia functionality in your codebase",
|
|
2191
2467
|
type: "module",
|
|
2192
2468
|
engines: {
|
|
@@ -2202,7 +2478,7 @@ var package_default = {
|
|
|
2202
2478
|
scripts: {
|
|
2203
2479
|
"build:proxy": "node scripts/build.mjs proxy",
|
|
2204
2480
|
build: "node scripts/build.mjs",
|
|
2205
|
-
"dev:proxy": "NODE_OPTIONS=--use-system-ca tsx watch src/proxy/index.ts",
|
|
2481
|
+
"dev:proxy": "touch .env && NODE_OPTIONS=--use-system-ca tsx watch --env-file=.env src/proxy/index.ts",
|
|
2206
2482
|
dev: "touch .env && tsx --env-file=.env ./src/main.tsx",
|
|
2207
2483
|
"env:load": "pnpm exec -- varlock load",
|
|
2208
2484
|
prepare: "husky",
|
|
@@ -2307,8 +2583,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
2307
2583
|
}
|
|
2308
2584
|
|
|
2309
2585
|
// src/actions/confirmLanguage.ts
|
|
2310
|
-
import
|
|
2311
|
-
var confirmLanguageSchema =
|
|
2586
|
+
import z19 from "zod";
|
|
2587
|
+
var confirmLanguageSchema = z19.object({
|
|
2312
2588
|
languages: detectLanguageSchema.shape.languages
|
|
2313
2589
|
});
|
|
2314
2590
|
async function confirmLanguage(ctx) {
|
|
@@ -2329,8 +2605,8 @@ async function confirmLanguage(ctx) {
|
|
|
2329
2605
|
}
|
|
2330
2606
|
|
|
2331
2607
|
// src/actions/confirmFramework.ts
|
|
2332
|
-
import
|
|
2333
|
-
var confirmFrameworkSchema =
|
|
2608
|
+
import z20 from "zod";
|
|
2609
|
+
var confirmFrameworkSchema = z20.object({
|
|
2334
2610
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
2335
2611
|
});
|
|
2336
2612
|
var CURATED_FRAMEWORKS = [
|
|
@@ -2458,8 +2734,8 @@ async function promptUser(ctx, params) {
|
|
|
2458
2734
|
}
|
|
2459
2735
|
|
|
2460
2736
|
// src/actions/confirmEntities.ts
|
|
2461
|
-
import
|
|
2462
|
-
var confirmEntitiesSchema =
|
|
2737
|
+
import z21 from "zod";
|
|
2738
|
+
var confirmEntitiesSchema = z21.object({
|
|
2463
2739
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
2464
2740
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2465
2741
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -2529,17 +2805,15 @@ async function confirmEntities(ctx) {
|
|
|
2529
2805
|
}
|
|
2530
2806
|
|
|
2531
2807
|
// src/actions/review.ts
|
|
2532
|
-
import { z as
|
|
2533
|
-
var reviewSchema =
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
)
|
|
2541
|
-
reviewPrompt: z21.string(),
|
|
2542
|
-
nextSteps: z21.array(z21.string())
|
|
2808
|
+
import { z as z22 } from "zod";
|
|
2809
|
+
var reviewSchema = z22.object({
|
|
2810
|
+
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
2811
|
+
// not one entry per workflow step — a step's raw output can be a long,
|
|
2812
|
+
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
2813
|
+
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
2814
|
+
summaryPoints: z22.array(z22.string()),
|
|
2815
|
+
reviewPrompt: z22.string(),
|
|
2816
|
+
nextSteps: z22.array(z22.string())
|
|
2543
2817
|
});
|
|
2544
2818
|
function formatCompletedSteps(steps) {
|
|
2545
2819
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -2549,32 +2823,55 @@ Output:
|
|
|
2549
2823
|
${JSON.stringify(s.output, null, 2)}`
|
|
2550
2824
|
).join("\n\n");
|
|
2551
2825
|
}
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2826
|
+
function formatReviewSummary(result) {
|
|
2827
|
+
const nextStepLines = result.nextSteps.map((step) => {
|
|
2828
|
+
const isIngestCommand = step.includes(".algolia-wizard/ingest.sh");
|
|
2829
|
+
const isWorktreeCommand = step.includes("/worktrees/");
|
|
2830
|
+
return {
|
|
2831
|
+
text: `\u2192 ${step}`,
|
|
2832
|
+
color: isIngestCommand ? COLORS.brand : isWorktreeCommand ? COLORS.secondary : void 0,
|
|
2833
|
+
bold: isIngestCommand || isWorktreeCommand
|
|
2834
|
+
};
|
|
2835
|
+
});
|
|
2836
|
+
return [
|
|
2837
|
+
// Plain lines, same as nextSteps' un-highlighted entries — the summary is
|
|
2838
|
+
// an overview, not a call to action, so it gets no arrow/color/bold.
|
|
2839
|
+
...result.summaryPoints,
|
|
2840
|
+
{ text: result.reviewPrompt, color: COLORS.brand },
|
|
2841
|
+
...nextStepLines
|
|
2842
|
+
];
|
|
2843
|
+
}
|
|
2844
|
+
var reviewStep = async (ctx, options) => {
|
|
2845
|
+
const result = await runAgent({
|
|
2846
|
+
instructions: [
|
|
2847
|
+
"Summarize what was accomplished in the workflow, leaving out verbose details.",
|
|
2848
|
+
"Base your summary only on the step outputs provided \u2014 do not read the repository.",
|
|
2849
|
+
"Group summaryPoints into a few broad, high-level takeaways (e.g. what was ingested, what search UI was built) rather than one entry per step \u2014 do not mirror the step outputs 1:1 or restate every detail.",
|
|
2850
|
+
"Each summaryPoint should be a short, standalone statement.",
|
|
2851
|
+
'Set reviewPrompt to a short message that asks the user to review the changes \u2014 it must include wording like "Please review the generated code".',
|
|
2852
|
+
"Set nextSteps to concrete actions the user should take to use what this workflow set up \u2014 do not suggest open-ended or generic tasks (e.g. deploying, writing tests) unless they are directly required.",
|
|
2853
|
+
options.nextStepsGuidance,
|
|
2854
|
+
`Completed steps:
|
|
2561
2855
|
${formatCompletedSteps(ctx.completedSteps)}`,
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
});
|
|
2856
|
+
"When done, call reportStatus"
|
|
2857
|
+
],
|
|
2858
|
+
tools: [],
|
|
2859
|
+
outputSchema: reviewSchema,
|
|
2860
|
+
modelSize: "small"
|
|
2861
|
+
});
|
|
2862
|
+
ctx.notify({ messages: formatReviewSummary(result) });
|
|
2863
|
+
return result;
|
|
2864
|
+
};
|
|
2568
2865
|
|
|
2569
2866
|
// src/actions/implement.ts
|
|
2570
|
-
import
|
|
2867
|
+
import z24 from "zod";
|
|
2571
2868
|
|
|
2572
2869
|
// src/lib/worktree.ts
|
|
2573
2870
|
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
2574
|
-
import { copyFile, mkdir as
|
|
2871
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
2575
2872
|
import {
|
|
2576
2873
|
basename as basename2,
|
|
2577
|
-
dirname as
|
|
2874
|
+
dirname as dirname7,
|
|
2578
2875
|
isAbsolute as isAbsolute2,
|
|
2579
2876
|
join as join10,
|
|
2580
2877
|
relative as relative2,
|
|
@@ -2638,7 +2935,7 @@ async function createWorktree(repoRoot) {
|
|
|
2638
2935
|
const path = join10(stateDir(repoRoot), "worktrees", dirSlug);
|
|
2639
2936
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
2640
2937
|
await pruneOldWorktrees(repoRoot);
|
|
2641
|
-
await
|
|
2938
|
+
await mkdir6(dirname7(path), { recursive: true });
|
|
2642
2939
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
2643
2940
|
return { path, branch };
|
|
2644
2941
|
}
|
|
@@ -2758,7 +3055,7 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
2758
3055
|
const relPath = join10(ingestDir, basename2(source));
|
|
2759
3056
|
const dest = join10(worktreePath, relPath);
|
|
2760
3057
|
try {
|
|
2761
|
-
await
|
|
3058
|
+
await mkdir6(dirname7(dest), { recursive: true });
|
|
2762
3059
|
await copyFile(source, dest);
|
|
2763
3060
|
} catch (err) {
|
|
2764
3061
|
return {
|
|
@@ -2768,6 +3065,25 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
2768
3065
|
}
|
|
2769
3066
|
return { ok: true, relPath };
|
|
2770
3067
|
}
|
|
3068
|
+
function hasEnvVar(content, name) {
|
|
3069
|
+
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
3070
|
+
}
|
|
3071
|
+
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3072
|
+
const target = join10(worktreePath, ".env");
|
|
3073
|
+
let existing = "";
|
|
3074
|
+
try {
|
|
3075
|
+
existing = await readFile8(target, "utf8");
|
|
3076
|
+
} catch (err) {
|
|
3077
|
+
if (err.code !== "ENOENT") throw err;
|
|
3078
|
+
}
|
|
3079
|
+
const missing = vars.filter((v) => !hasEnvVar(existing, v.name));
|
|
3080
|
+
if (missing.length === 0) return [];
|
|
3081
|
+
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
3082
|
+
const lines = missing.map(({ name, value }) => `${name}=${value}
|
|
3083
|
+
`).join("");
|
|
3084
|
+
await writeFile6(target, existing + prefix + lines, "utf8");
|
|
3085
|
+
return missing.map((v) => v.name);
|
|
3086
|
+
}
|
|
2771
3087
|
async function listChangedFiles(worktreePath) {
|
|
2772
3088
|
const raw = await git(["-C", worktreePath, "status", "--porcelain", "-z"]);
|
|
2773
3089
|
const entries = raw.split("\0");
|
|
@@ -2825,20 +3141,20 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
2825
3141
|
}
|
|
2826
3142
|
|
|
2827
3143
|
// src/lib/algoliaApiKey.ts
|
|
2828
|
-
import { z as
|
|
3144
|
+
import { z as z23 } from "zod";
|
|
2829
3145
|
var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
|
|
2830
|
-
var apiKeySchema =
|
|
2831
|
-
value:
|
|
2832
|
-
acl:
|
|
2833
|
-
indexes:
|
|
3146
|
+
var apiKeySchema = z23.object({
|
|
3147
|
+
value: z23.string().min(1),
|
|
3148
|
+
acl: z23.array(z23.string()).default([]),
|
|
3149
|
+
indexes: z23.array(z23.string()).default([])
|
|
2834
3150
|
});
|
|
2835
|
-
var apiKeyListSchema =
|
|
2836
|
-
items:
|
|
2837
|
-
keys:
|
|
3151
|
+
var apiKeyListSchema = z23.object({
|
|
3152
|
+
items: z23.array(apiKeySchema).optional(),
|
|
3153
|
+
keys: z23.array(apiKeySchema).optional()
|
|
2838
3154
|
}).transform((o) => o.items ?? o.keys ?? []);
|
|
2839
|
-
var createdKeySchema =
|
|
2840
|
-
key:
|
|
2841
|
-
value:
|
|
3155
|
+
var createdKeySchema = z23.object({
|
|
3156
|
+
key: z23.string().min(1).optional(),
|
|
3157
|
+
value: z23.string().min(1).optional()
|
|
2842
3158
|
});
|
|
2843
3159
|
function canReuse(key, index) {
|
|
2844
3160
|
return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
|
|
@@ -2874,15 +3190,15 @@ async function resolveSearchOnlyKey(index) {
|
|
|
2874
3190
|
|
|
2875
3191
|
// src/lib/algoliaDocs.ts
|
|
2876
3192
|
import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
|
|
2877
|
-
import { dirname as
|
|
3193
|
+
import { dirname as dirname8, join as join11 } from "node:path";
|
|
2878
3194
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2879
3195
|
var DOCS_SUBPATH = join11("docs", "algolia-sdk");
|
|
2880
3196
|
function findDocsDir() {
|
|
2881
|
-
let dir =
|
|
3197
|
+
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
2882
3198
|
for (; ; ) {
|
|
2883
3199
|
const candidate = join11(dir, DOCS_SUBPATH);
|
|
2884
3200
|
if (existsSync2(candidate)) return candidate;
|
|
2885
|
-
const parent =
|
|
3201
|
+
const parent = dirname8(dir);
|
|
2886
3202
|
if (parent === dir) return void 0;
|
|
2887
3203
|
dir = parent;
|
|
2888
3204
|
}
|
|
@@ -2932,51 +3248,56 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
2932
3248
|
return loadAlgoliaDoc("js");
|
|
2933
3249
|
}
|
|
2934
3250
|
|
|
3251
|
+
// src/lib/shell.ts
|
|
3252
|
+
function shellQuote(value) {
|
|
3253
|
+
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
3254
|
+
}
|
|
3255
|
+
|
|
2935
3256
|
// src/actions/implement.ts
|
|
2936
|
-
var implementSchema =
|
|
2937
|
-
filesChanged:
|
|
2938
|
-
summary:
|
|
3257
|
+
var implementSchema = z24.object({
|
|
3258
|
+
filesChanged: z24.array(z24.string()),
|
|
3259
|
+
summary: z24.string(),
|
|
2939
3260
|
// Absolute path to the throwaway worktree holding the generated changes, so
|
|
2940
3261
|
// the user can open it (`cd <worktreePath>`) or inspect the diff
|
|
2941
3262
|
// (`git -C <worktreePath> status/diff`).
|
|
2942
|
-
worktreePath:
|
|
2943
|
-
ingestCommand:
|
|
3263
|
+
worktreePath: z24.string().optional(),
|
|
3264
|
+
ingestCommand: z24.string().optional(),
|
|
2944
3265
|
// True when the user accepted the run-now prompt and the wizard executed the
|
|
2945
3266
|
// ingestion script; downstream steps use this to avoid telling the user to run
|
|
2946
3267
|
// a script that already ran.
|
|
2947
|
-
ingestScriptRan:
|
|
3268
|
+
ingestScriptRan: z24.boolean().optional(),
|
|
2948
3269
|
// Records ingested by the run-now execution, parsed from the script's
|
|
2949
3270
|
// machine-readable count line; absent when the script didn't run or emitted
|
|
2950
3271
|
// no parseable count.
|
|
2951
|
-
ingestRecordCount:
|
|
3272
|
+
ingestRecordCount: z24.number().optional(),
|
|
2952
3273
|
// Wall-clock duration of the run-now ingestion execution, in ms.
|
|
2953
|
-
ingestDurationMs:
|
|
2954
|
-
ingestionSource:
|
|
3274
|
+
ingestDurationMs: z24.number().optional(),
|
|
3275
|
+
ingestionSource: z24.enum(["local", "fileUpload", "generated"]),
|
|
2955
3276
|
// Suggested names/values, built from framework detection. The search agent is
|
|
2956
3277
|
// instructed to rename the prefix if it doesn't match the project's build
|
|
2957
3278
|
// tool, so the names it actually wrote can differ — treat these as hints, not
|
|
2958
3279
|
// ground truth (the agent's summary carries the final names).
|
|
2959
|
-
searchEnvVars:
|
|
2960
|
-
|
|
2961
|
-
name:
|
|
2962
|
-
value:
|
|
3280
|
+
searchEnvVars: z24.array(
|
|
3281
|
+
z24.object({
|
|
3282
|
+
name: z24.string(),
|
|
3283
|
+
value: z24.string()
|
|
2963
3284
|
})
|
|
2964
3285
|
).optional()
|
|
2965
3286
|
});
|
|
2966
|
-
var implementationOutputSchema =
|
|
2967
|
-
summary:
|
|
3287
|
+
var implementationOutputSchema = z24.object({
|
|
3288
|
+
summary: z24.string(),
|
|
2968
3289
|
// Ingestion only: how to run the generated script, as a structured pair the
|
|
2969
3290
|
// wizard turns into an argv (`<runtime> <entrypoint>`) — never a free-form
|
|
2970
3291
|
// command string. `runtime` is constrained to an allowlisted interpreter and
|
|
2971
3292
|
// `entrypoint` is validated to a worktree-relative path before execution, so
|
|
2972
3293
|
// the agent cannot inject extra commands or swap the interpreter.
|
|
2973
|
-
runtime:
|
|
2974
|
-
entrypoint:
|
|
3294
|
+
runtime: z24.enum(INGEST_RUNTIMES).optional(),
|
|
3295
|
+
entrypoint: z24.string().optional()
|
|
2975
3296
|
});
|
|
2976
|
-
var verificationOutputSchema =
|
|
2977
|
-
summary:
|
|
2978
|
-
sufficient:
|
|
2979
|
-
additionalInstructions:
|
|
3297
|
+
var verificationOutputSchema = z24.object({
|
|
3298
|
+
summary: z24.string(),
|
|
3299
|
+
sufficient: z24.boolean(),
|
|
3300
|
+
additionalInstructions: z24.string().optional()
|
|
2980
3301
|
});
|
|
2981
3302
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
2982
3303
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
@@ -3058,9 +3379,9 @@ function sourceSpecificInstructions(input) {
|
|
|
3058
3379
|
],
|
|
3059
3380
|
generated: [
|
|
3060
3381
|
"No real data source exists; use sample records for each confirmed entity.",
|
|
3061
|
-
"Call the generateRecord tool once per entity (entityName, attributes, count 20-50)
|
|
3062
|
-
"
|
|
3063
|
-
"Add a prominent TODO where the developer swaps the generated records for their real record source."
|
|
3382
|
+
"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.",
|
|
3383
|
+
"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.",
|
|
3384
|
+
"Add a prominent TODO where the developer swaps the generated records (and the JSON file under `.algolia-wizard/data/`) for their real record source."
|
|
3064
3385
|
]
|
|
3065
3386
|
};
|
|
3066
3387
|
return byLine[input.ingestionSource];
|
|
@@ -3091,11 +3412,12 @@ function searchInstructions(input) {
|
|
|
3091
3412
|
`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 "${input.targetIndex}" index.`,
|
|
3092
3413
|
"Read the App ID and a search-only API key from public env vars; never hardcode them. A search-only key is safe to expose client-side.",
|
|
3093
3414
|
`Values: App ID ${input.appId ? `"${input.appId}"` : "(placeholder for the developer to fill in)"}, search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3415
|
+
// Names are fixed, not the agent's to rename: the wizard writes the
|
|
3416
|
+
// resolved app id / search-only key into ".env" under these exact names
|
|
3417
|
+
// right after this step, so a renamed prefix here would leave the code
|
|
3418
|
+
// reading a var the wizard never wrote.
|
|
3419
|
+
`Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
3420
|
+
'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.'
|
|
3099
3421
|
];
|
|
3100
3422
|
}
|
|
3101
3423
|
function verificationInstructions(input) {
|
|
@@ -3127,9 +3449,14 @@ var IMPLEMENT_CONFIG = {
|
|
|
3127
3449
|
}
|
|
3128
3450
|
};
|
|
3129
3451
|
var useCaseToolMap = {
|
|
3130
|
-
ingestion: [...FS_READ_TOOLS, "writeFile", "writeCredentials"],
|
|
3131
|
-
search: [...FS_READ_TOOLS, "writeFile"],
|
|
3132
|
-
verification: [
|
|
3452
|
+
ingestion: [...FS_READ_TOOLS, "writeFile", "writeCredentials", "notifyUser"],
|
|
3453
|
+
search: [...FS_READ_TOOLS, "writeFile", "notifyUser"],
|
|
3454
|
+
verification: [
|
|
3455
|
+
...FS_READ_TOOLS,
|
|
3456
|
+
"writeFile",
|
|
3457
|
+
"verifyImplementation",
|
|
3458
|
+
"notifyUser"
|
|
3459
|
+
]
|
|
3133
3460
|
};
|
|
3134
3461
|
function toolsForUseCase(useCase, ingestionSource) {
|
|
3135
3462
|
const tools = useCaseToolMap[useCase];
|
|
@@ -3152,6 +3479,9 @@ function formatSummary(useCase, summary) {
|
|
|
3152
3479
|
const label = useCase === "ingestion" ? "Ingestion" : useCase === "search" ? "Search" : "Verification";
|
|
3153
3480
|
return `${label}: ${summary}`;
|
|
3154
3481
|
}
|
|
3482
|
+
function buildIngestCommand(worktree, runtime, entrypoint) {
|
|
3483
|
+
return `cd ${shellQuote(worktree)} && ${runtime} ${shellQuote(entrypoint)}`;
|
|
3484
|
+
}
|
|
3155
3485
|
function parseIngestRecordCount(output) {
|
|
3156
3486
|
const match = output.match(/ALGOLIA_WIZARD_RECORD_COUNT=(\d+)/);
|
|
3157
3487
|
if (!match) return void 0;
|
|
@@ -3295,6 +3625,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3295
3625
|
let ingestRecordCount;
|
|
3296
3626
|
let ingestDurationMs;
|
|
3297
3627
|
let installFailed = false;
|
|
3628
|
+
let ingestOutcomeMessage;
|
|
3298
3629
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
3299
3630
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
3300
3631
|
agentRuns += 1;
|
|
@@ -3332,6 +3663,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3332
3663
|
ingestRuntime = runtime;
|
|
3333
3664
|
ingestEntrypoint = entrypoint;
|
|
3334
3665
|
if (ingestRuntime && ingestEntrypoint && !installFailed) {
|
|
3666
|
+
ctx.clearNotices();
|
|
3335
3667
|
const runNow = await ctx.requestUserInput({
|
|
3336
3668
|
prompt: `Run the ingestion script now? This writes records to the "${targetIndex}" index.`,
|
|
3337
3669
|
promptType: "acceptReject",
|
|
@@ -3405,19 +3737,38 @@ ${run.output}` : status;
|
|
|
3405
3737
|
});
|
|
3406
3738
|
}
|
|
3407
3739
|
summaries.push(summaryLine);
|
|
3408
|
-
|
|
3409
|
-
prompt: "Continue",
|
|
3410
|
-
promptType: "notice",
|
|
3411
|
-
options: [],
|
|
3412
|
-
messages: [outcomeMessage]
|
|
3413
|
-
});
|
|
3740
|
+
ingestOutcomeMessage = outcomeMessage;
|
|
3414
3741
|
}
|
|
3415
3742
|
}
|
|
3743
|
+
const commandMessages = [`Open the worktree: cd ${shellQuote(worktree)}`];
|
|
3744
|
+
if (ingestRuntime && ingestEntrypoint) {
|
|
3745
|
+
commandMessages.push(
|
|
3746
|
+
`Ingestion command: ${buildIngestCommand(worktree, ingestRuntime, ingestEntrypoint)}`
|
|
3747
|
+
);
|
|
3748
|
+
}
|
|
3749
|
+
await ctx.requestUserInput({
|
|
3750
|
+
// No question being asked here, just an acknowledgement — the
|
|
3751
|
+
// continue/decline hints below already say "continue".
|
|
3752
|
+
prompt: "",
|
|
3753
|
+
promptType: "spaceToContinue",
|
|
3754
|
+
options: [],
|
|
3755
|
+
messages: ingestOutcomeMessage ? [ingestOutcomeMessage, ...commandMessages] : commandMessages
|
|
3756
|
+
});
|
|
3416
3757
|
}
|
|
3417
3758
|
if (useCases.includes("search")) {
|
|
3418
3759
|
let extraInstructions = [];
|
|
3419
3760
|
const preSearchFiles = new Set(await listChangedFiles(worktree));
|
|
3420
3761
|
for (let attempt = 1; attempt <= MAX_IMPLEMENT_VERIFICATION_ATTEMPTS; attempt++) {
|
|
3762
|
+
if (attempt > 1) {
|
|
3763
|
+
logger.info(
|
|
3764
|
+
{
|
|
3765
|
+
attempt,
|
|
3766
|
+
maxAttempts: MAX_IMPLEMENT_VERIFICATION_ATTEMPTS,
|
|
3767
|
+
extraInstructions
|
|
3768
|
+
},
|
|
3769
|
+
"implement: retrying search implementation after failed verification"
|
|
3770
|
+
);
|
|
3771
|
+
}
|
|
3421
3772
|
const { summary } = await runImplementationUseCase(
|
|
3422
3773
|
"search",
|
|
3423
3774
|
extraInstructions
|
|
@@ -3446,6 +3797,26 @@ ${run.output}` : status;
|
|
|
3446
3797
|
}
|
|
3447
3798
|
extraInstructions = verificationRetryInstructions(verification);
|
|
3448
3799
|
}
|
|
3800
|
+
const resolvedSearchEnvVars = input.searchEnvVars.filter(
|
|
3801
|
+
(v) => !v.value.startsWith("<")
|
|
3802
|
+
);
|
|
3803
|
+
if (resolvedSearchEnvVars.length > 0) {
|
|
3804
|
+
const written = await writeSearchEnvValues(
|
|
3805
|
+
worktree,
|
|
3806
|
+
resolvedSearchEnvVars
|
|
3807
|
+
);
|
|
3808
|
+
if (written.length > 0) {
|
|
3809
|
+
summaries.push(`Wrote ${written.join(", ")} to .env.`);
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
const unresolvedSearchEnvVars = input.searchEnvVars.filter(
|
|
3813
|
+
(v) => v.value.startsWith("<")
|
|
3814
|
+
);
|
|
3815
|
+
if (unresolvedSearchEnvVars.length > 0) {
|
|
3816
|
+
summaries.push(
|
|
3817
|
+
`Could not resolve a value for ${unresolvedSearchEnvVars.map((v) => v.name).join(", ")} \u2014 fill it in manually in .env.`
|
|
3818
|
+
);
|
|
3819
|
+
}
|
|
3449
3820
|
} else {
|
|
3450
3821
|
ctx.setUserInput("implementation", "success");
|
|
3451
3822
|
}
|
|
@@ -3466,7 +3837,11 @@ ${run.output}` : status;
|
|
|
3466
3837
|
summary: summaries.join("\n\n"),
|
|
3467
3838
|
worktreePath: worktree,
|
|
3468
3839
|
...useCases.includes("ingestion") && ingestRuntime && ingestEntrypoint ? {
|
|
3469
|
-
ingestCommand:
|
|
3840
|
+
ingestCommand: buildIngestCommand(
|
|
3841
|
+
worktree,
|
|
3842
|
+
ingestRuntime,
|
|
3843
|
+
ingestEntrypoint
|
|
3844
|
+
),
|
|
3470
3845
|
ingestScriptRan,
|
|
3471
3846
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
3472
3847
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
@@ -3488,7 +3863,14 @@ var defaultWorkflow = {
|
|
|
3488
3863
|
id: "project-scan",
|
|
3489
3864
|
title: "project scan",
|
|
3490
3865
|
outputSchema: projectScanSchema,
|
|
3491
|
-
run: (ctx) =>
|
|
3866
|
+
run: (ctx) => {
|
|
3867
|
+
ctx.notify({
|
|
3868
|
+
messages: [
|
|
3869
|
+
"Scanning your project for languages, frameworks, and Algolia integration points\u2026"
|
|
3870
|
+
]
|
|
3871
|
+
});
|
|
3872
|
+
return projectScan(ctx);
|
|
3873
|
+
}
|
|
3492
3874
|
}),
|
|
3493
3875
|
defineStep({
|
|
3494
3876
|
id: "confirm-language",
|
|
@@ -3507,8 +3889,8 @@ var defaultWorkflow = {
|
|
|
3507
3889
|
defineStep({
|
|
3508
3890
|
id: "select-index",
|
|
3509
3891
|
title: "Set up index",
|
|
3510
|
-
outputSchema:
|
|
3511
|
-
selection:
|
|
3892
|
+
outputSchema: z25.object({
|
|
3893
|
+
selection: z25.string()
|
|
3512
3894
|
}),
|
|
3513
3895
|
run: (ctx) => selectIndexStep(ctx)
|
|
3514
3896
|
}),
|
|
@@ -3516,7 +3898,14 @@ var defaultWorkflow = {
|
|
|
3516
3898
|
id: "ingestion",
|
|
3517
3899
|
title: "ingest records",
|
|
3518
3900
|
outputSchema: implementSchema,
|
|
3519
|
-
run: (ctx) =>
|
|
3901
|
+
run: (ctx) => {
|
|
3902
|
+
ctx.notify({
|
|
3903
|
+
messages: [
|
|
3904
|
+
"Setting up an Algolia ingestion pipeline in your project\u2026"
|
|
3905
|
+
]
|
|
3906
|
+
});
|
|
3907
|
+
return implement(ctx, ["ingestion"]);
|
|
3908
|
+
}
|
|
3520
3909
|
}),
|
|
3521
3910
|
defineStep({
|
|
3522
3911
|
id: "confirm-framework",
|
|
@@ -3530,6 +3919,9 @@ var defaultWorkflow = {
|
|
|
3530
3919
|
title: "create search ui",
|
|
3531
3920
|
outputSchema: implementSchema,
|
|
3532
3921
|
run: (ctx) => {
|
|
3922
|
+
ctx.notify({
|
|
3923
|
+
messages: ["Building your Algolia search experience\u2026"]
|
|
3924
|
+
});
|
|
3533
3925
|
const ingestion = ctx.getStepOutput(
|
|
3534
3926
|
"ingestion"
|
|
3535
3927
|
);
|
|
@@ -3541,11 +3933,17 @@ var defaultWorkflow = {
|
|
|
3541
3933
|
title: "done",
|
|
3542
3934
|
outputSchema: reviewSchema,
|
|
3543
3935
|
run: (ctx) => {
|
|
3936
|
+
ctx.notify({
|
|
3937
|
+
messages: ["Summarizing what we did\u2026"]
|
|
3938
|
+
});
|
|
3544
3939
|
const ingestion = ctx.getStepOutput(
|
|
3545
3940
|
"ingestion"
|
|
3546
3941
|
);
|
|
3547
3942
|
return reviewStep(ctx, {
|
|
3548
|
-
|
|
3943
|
+
// The ingestion step already showed the user the exact `ingestCommand`
|
|
3944
|
+
// and worktree path as a notice, so nextSteps must not restate it —
|
|
3945
|
+
// an LLM-paraphrased command risks being wrong.
|
|
3946
|
+
nextStepsGuidance: ingestion?.ingestScriptRan ? "The wizard already ran the ingestion script and records are in the index. Do NOT tell the user to run it again; instead point them at the target index to confirm the records. Do not restate the ingestion command \u2014 the wizard already showed it to them." : "Tell the user to run the ingestion script; do not restate the exact command \u2014 the wizard already showed it to them above."
|
|
3549
3947
|
});
|
|
3550
3948
|
}
|
|
3551
3949
|
})
|
|
@@ -3561,7 +3959,7 @@ function getWorkflow(id) {
|
|
|
3561
3959
|
}
|
|
3562
3960
|
|
|
3563
3961
|
// src/main.tsx
|
|
3564
|
-
import { jsx as
|
|
3962
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
3565
3963
|
var requestedId = process.argv[2] ?? defaultWorkflow.id;
|
|
3566
3964
|
var workflow = getWorkflow(requestedId);
|
|
3567
3965
|
if (!workflow) {
|
|
@@ -3570,7 +3968,7 @@ if (!workflow) {
|
|
|
3570
3968
|
process.exit(1);
|
|
3571
3969
|
}
|
|
3572
3970
|
var store = useWizard.getState();
|
|
3573
|
-
var instance = render(/* @__PURE__ */
|
|
3971
|
+
var instance = render(/* @__PURE__ */ jsx13(App, {}), { incrementalRendering: true });
|
|
3574
3972
|
var user = await getUser();
|
|
3575
3973
|
if (!user) {
|
|
3576
3974
|
await instance.waitUntilRenderFlush();
|
|
@@ -3581,7 +3979,7 @@ if (!user) {
|
|
|
3581
3979
|
console.error(err instanceof Error ? err.message : String(err));
|
|
3582
3980
|
process.exit(1);
|
|
3583
3981
|
}
|
|
3584
|
-
instance = render(/* @__PURE__ */
|
|
3982
|
+
instance = render(/* @__PURE__ */ jsx13(App, {}), { incrementalRendering: true });
|
|
3585
3983
|
user = await getUser();
|
|
3586
3984
|
if (!user) {
|
|
3587
3985
|
store.setError(
|