@algolia/wizard 0.7.0 → 0.8.0-rc.53.46

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/main.js +774 -498
  3. package/package.json +1 -3
package/dist/main.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { render } from "ink";
5
5
 
6
6
  // src/ui/App.tsx
7
- import { Box as Box13, Text as Text13, useApp, useInput as useInput6, useWindowSize as useWindowSize7 } from "ink";
7
+ import { Box as Box14, Text as Text14, useApp, useInput as useInput6, useWindowSize as useWindowSize8 } from "ink";
8
8
 
9
9
  // src/core/store.ts
10
10
  import { create } from "zustand";
@@ -12,32 +12,86 @@ import { nanoid } from "nanoid";
12
12
 
13
13
  // src/lib/algoliaCli.ts
14
14
  import { spawn } from "node:child_process";
15
- import { createRequire } from "node:module";
16
- var require2 = createRequire(import.meta.url);
17
- function algoliaCliEntry() {
18
- return require2.resolve("@algolia/cli/bin/run.js");
15
+ import { z } from "zod";
16
+ function npxArgs(args) {
17
+ return ["--yes", "@algolia/cli@latest", ...args];
19
18
  }
20
- function runAlgoliaCli(args) {
19
+ var shell = process.platform === "win32";
20
+ function lineSplitter(emit) {
21
+ let buffer = "";
22
+ return {
23
+ push(chunk) {
24
+ buffer += chunk;
25
+ const lines = buffer.split("\n");
26
+ buffer = lines.pop() ?? "";
27
+ for (const line of lines) emit(line.replace(/\r$/, ""));
28
+ },
29
+ flush() {
30
+ if (buffer) emit(buffer.replace(/\r$/, ""));
31
+ buffer = "";
32
+ }
33
+ };
34
+ }
35
+ var wizardSink = (stream, line) => {
36
+ if (!line.trim()) return;
37
+ useWizard.getState().pushCliOutput(stream, line);
38
+ };
39
+ var stderrSink = (stream, line) => {
40
+ if (stream === "stdout") return;
41
+ wizardSink(stream, line);
42
+ };
43
+ function runAlgoliaCli(args, { onOutput } = {}) {
44
+ const store = useWizard.getState();
45
+ const logId = store.logStart("tool", `algolia ${args.join(" ")}`);
21
46
  return new Promise((resolve4, reject) => {
22
- const child = spawn(process.execPath, [algoliaCliEntry(), ...args]);
47
+ const child = spawn("npx", npxArgs(args), { shell });
23
48
  let stdout = "";
24
49
  let stderr = "";
25
- child.stdout.on("data", (chunk) => stdout += chunk);
26
- child.stderr.on("data", (chunk) => stderr += chunk);
50
+ const splitters = {
51
+ stdout: lineSplitter((line) => onOutput?.("stdout", line)),
52
+ stderr: lineSplitter((line) => onOutput?.("stderr", line))
53
+ };
54
+ child.stdout.on("data", (chunk) => {
55
+ const text = String(chunk);
56
+ stdout += text;
57
+ splitters.stdout.push(text);
58
+ });
59
+ child.stderr.on("data", (chunk) => {
60
+ const text = String(chunk);
61
+ stderr += text;
62
+ splitters.stderr.push(text);
63
+ });
27
64
  child.on("error", reject);
28
65
  child.on("close", (code) => {
66
+ splitters.stdout.flush();
67
+ splitters.stderr.flush();
29
68
  if (code === 0) {
30
69
  resolve4(stdout);
31
70
  } else {
32
- const detail = stderr.trim() || stdout.trim();
71
+ const failed = stderr.trim();
72
+ let detail = "";
73
+ if (failed) {
74
+ detail = `: ${failed}`;
75
+ } else if (stdout.trim()) {
76
+ detail = " (no stderr; stdout withheld \u2014 it may contain credentials)";
77
+ }
33
78
  reject(
34
79
  new Error(
35
- `Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail ? `: ${detail}` : ""}`
80
+ `Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail}`
36
81
  )
37
82
  );
38
83
  }
39
84
  });
40
- });
85
+ }).then(
86
+ (out) => {
87
+ useWizard.getState().logEnd(logId, "success");
88
+ return out;
89
+ },
90
+ (err) => {
91
+ useWizard.getState().logEnd(logId, "error");
92
+ throw err;
93
+ }
94
+ );
41
95
  }
42
96
  async function getUser() {
43
97
  let raw;
@@ -52,19 +106,23 @@ async function getUser() {
52
106
  return null;
53
107
  }
54
108
  }
55
- function runAuthLogin() {
56
- return new Promise((resolve4, reject) => {
57
- const child = spawn(
58
- process.execPath,
59
- [algoliaCliEntry(), "auth", "login", "--default"],
60
- { stdio: "inherit" }
61
- );
62
- child.on("error", reject);
63
- child.on("close", (code) => {
64
- if (code === 0) resolve4();
65
- else reject(new Error(`Algolia authentication failed (exit ${code}).`));
66
- });
109
+ var loginResultSchema = z.object({
110
+ success: z.boolean(),
111
+ email: z.string().optional()
112
+ });
113
+ async function runAuthLogin() {
114
+ const raw = await runAlgoliaCli(["auth", "login", "--non-interactive"], {
115
+ onOutput: stderrSink
67
116
  });
117
+ let parsed;
118
+ try {
119
+ parsed = loginResultSchema.safeParse(JSON.parse(raw));
120
+ } catch {
121
+ parsed = void 0;
122
+ }
123
+ if (parsed?.success && !parsed.data.success) {
124
+ throw new Error("Algolia sign-in did not report success.");
125
+ }
68
126
  }
69
127
 
70
128
  // src/lib/auth.ts
@@ -171,6 +229,7 @@ function describeInputValue(value) {
171
229
  return Array.isArray(value) ? value.join(", ") : value;
172
230
  }
173
231
  var NOTICE_INTERVAL_MS = 2e3;
232
+ var CLI_OUTPUT_LIMIT = 200;
174
233
  var useWizard = create((set, get) => ({
175
234
  phase: "idle",
176
235
  homeScreen: "home",
@@ -182,10 +241,18 @@ var useWizard = create((set, get) => ({
182
241
  notices: [],
183
242
  _noticeQueue: [],
184
243
  _noticeTimer: null,
244
+ cliOutput: [],
245
+ targetIndex: null,
185
246
  logs: [],
186
247
  error: null,
187
248
  inputReq: null,
188
249
  _resolve: null,
250
+ // Brackets a CLI subprocess that needs the screen. Sign-in happens after the
251
+ // welcome screen's enter, so `endAuth` lands on 'preflight', not 'idle':
252
+ // returning to 'idle' would put the welcome screen back up and ask the user
253
+ // to confirm the run a second time.
254
+ beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
255
+ endAuth: () => set((s) => s.phase === "authenticating" ? { phase: "preflight" } : {}),
189
256
  // Advances past the welcome screen. Only meaningful from 'idle' — once the
190
257
  // workflow is running there's nothing left to confirm.
191
258
  // Reset `homeScreen` so preflight shows Welcome, not the Learn more sub-view.
@@ -220,7 +287,13 @@ var useWizard = create((set, get) => ({
220
287
  syncSteps: (steps, currentStepIndex) => set({ steps, currentStepIndex }),
221
288
  setActiveStep: (index) => {
222
289
  get()._clearNoticeQueue();
223
- set({ phase: "running", currentStepIndex: index, output: "", notices: [] });
290
+ set({
291
+ phase: "running",
292
+ currentStepIndex: index,
293
+ output: "",
294
+ notices: [],
295
+ cliOutput: []
296
+ });
224
297
  },
225
298
  setUser: (user) => set({ user }),
226
299
  appendToken: (text) => set((s) => ({ output: s.output + text })),
@@ -261,6 +334,16 @@ var useWizard = create((set, get) => ({
261
334
  get()._clearNoticeQueue();
262
335
  set({ notices: [] });
263
336
  },
337
+ // Unthrottled, unlike `pushNotice`: these lines arrive at whatever rate the
338
+ // subprocess emits them, and holding them back would land output after the
339
+ // command it belongs to has already exited.
340
+ pushCliOutput: (stream, text) => set((s) => ({
341
+ cliOutput: [...s.cliOutput, { id: nanoid(), stream, text }].slice(
342
+ -CLI_OUTPUT_LIMIT
343
+ )
344
+ })),
345
+ clearCliOutput: () => set({ cliOutput: [] }),
346
+ setTargetIndex: (index) => set({ targetIndex: index }),
264
347
  logStart: (kind, name, input) => {
265
348
  const id = nanoid();
266
349
  set((s) => ({
@@ -305,6 +388,8 @@ var useWizard = create((set, get) => ({
305
388
  currentStepIndex: 0,
306
389
  output: "",
307
390
  notices: [],
391
+ cliOutput: [],
392
+ targetIndex: null,
308
393
  logs: [],
309
394
  error: null,
310
395
  inputReq: null,
@@ -313,16 +398,100 @@ var useWizard = create((set, get) => ({
313
398
  }
314
399
  }));
315
400
 
401
+ // src/ui/CliOutput.tsx
402
+ import { Box, Text, useWindowSize } from "ink";
403
+
404
+ // src/ui/theme.ts
405
+ var MARKER = {
406
+ pending: "\u25CB",
407
+ running: "\u25D0",
408
+ done: "\u2713",
409
+ error: "\u2716"
410
+ };
411
+ var BRAND = "#003DFF";
412
+ var SECONDARY = "#5468FF";
413
+ var DANGER = "#F86E7E";
414
+ var COLORS = {
415
+ brand: BRAND,
416
+ primary: "#E6EDF3",
417
+ secondary: SECONDARY,
418
+ strong: "#FFFFFF",
419
+ muted: "#8B949E",
420
+ dim: "#484F58",
421
+ highlight: { bg: "#12331C", fg: "#4ADE80" },
422
+ badge: "#E3B341",
423
+ danger: DANGER,
424
+ success: "#4ADE80",
425
+ bg: {
426
+ main: "#0B0E14",
427
+ sidebar: "#14171E"
428
+ },
429
+ border: "#30363D",
430
+ accent: "#76A0FF",
431
+ status: {
432
+ pending: "gray",
433
+ running: "#76A0FF",
434
+ done: "#4ADE80",
435
+ error: DANGER
436
+ }
437
+ };
438
+
439
+ // src/ui/CliOutput.tsx
440
+ import { jsxs } from "react/jsx-runtime";
441
+ var CLI_MARKER = "\u203A";
442
+ var RESERVED_ROWS = 16;
443
+ var MAX_ROWS = 12;
444
+ var PANEL_TEXT_WIDTH = 45;
445
+ var URL_PATTERN = /https?:\/\//;
446
+ function rowCost(text) {
447
+ return URL_PATTERN.test(text) ? Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH)) : 1;
448
+ }
449
+ function CliOutput() {
450
+ const cliOutput = useWizard((s) => s.cliOutput);
451
+ const { rows } = useWindowSize();
452
+ if (!cliOutput.length) return null;
453
+ const rowBudget = Math.min(Math.max(rows - RESERVED_ROWS, 3), MAX_ROWS);
454
+ const visible = [];
455
+ let usedRows = 0;
456
+ for (let i = cliOutput.length - 1; i >= 0; i--) {
457
+ const cost = rowCost(cliOutput[i].text);
458
+ if (usedRows + cost > rowBudget && visible.length > 0) break;
459
+ visible.unshift(cliOutput[i]);
460
+ usedRows += cost;
461
+ }
462
+ const hidden = cliOutput.length - visible.length;
463
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
464
+ hidden > 0 && /* @__PURE__ */ jsxs(Text, { color: COLORS.dim, children: [
465
+ "\u2191 ",
466
+ hidden,
467
+ " earlier line(s)"
468
+ ] }),
469
+ visible.map((line) => /* @__PURE__ */ jsxs(
470
+ Text,
471
+ {
472
+ color: line.stream === "stderr" ? COLORS.muted : COLORS.dim,
473
+ wrap: URL_PATTERN.test(line.text) ? "wrap" : "truncate",
474
+ children: [
475
+ CLI_MARKER,
476
+ " ",
477
+ line.text
478
+ ]
479
+ },
480
+ line.id
481
+ ))
482
+ ] });
483
+ }
484
+
316
485
  // src/ui/Notices.tsx
317
- import { Box as Box2, Text as Text2, useWindowSize as useWindowSize2 } from "ink";
486
+ import { Box as Box3, Text as Text3, useWindowSize as useWindowSize3 } from "ink";
318
487
  import { useEffect as useEffect2, useState as useState2 } from "react";
319
488
 
320
489
  // src/ui/Table.tsx
321
- import { Box, Text, measureElement, useWindowSize } from "ink";
490
+ import { Box as Box2, Text as Text2, measureElement, useWindowSize as useWindowSize2 } from "ink";
322
491
  import { useEffect, useRef, useState } from "react";
323
492
  import { jsx } from "react/jsx-runtime";
324
493
  function Table({ columns, rows }) {
325
- const { columns: termCols } = useWindowSize();
494
+ const { columns: termCols } = useWindowSize2();
326
495
  const ref = useRef(null);
327
496
  const [width, setWidth] = useState(0);
328
497
  useEffect(() => {
@@ -330,7 +499,7 @@ function Table({ columns, rows }) {
330
499
  }, [termCols, columns, rows]);
331
500
  if (rows.length === 0) return null;
332
501
  const lines = formatTable(columns, rows, width || void 0);
333
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text, { wrap: "truncate", children: line }, `tbl-${i}`)) });
502
+ return /* @__PURE__ */ jsx(Box2, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text2, { wrap: "truncate", children: line }, `tbl-${i}`)) });
334
503
  }
335
504
  function formatTable(columns, rows, width) {
336
505
  const natural = columns.map(
@@ -370,48 +539,13 @@ function resize(widths, budget) {
370
539
  }
371
540
  var truncate = (s, width) => s.length <= width ? s : width <= 1 ? s.slice(0, width) : `${s.slice(0, width - 1)}\u2026`;
372
541
 
373
- // src/ui/theme.ts
374
- var MARKER = {
375
- pending: "\u25CB",
376
- running: "\u25D0",
377
- done: "\u2713",
378
- error: "\u2716"
379
- };
380
- var BRAND = "#003DFF";
381
- var SECONDARY = "#5468FF";
382
- var DANGER = "#F86E7E";
383
- var COLORS = {
384
- brand: BRAND,
385
- primary: "#E6EDF3",
386
- secondary: SECONDARY,
387
- strong: "#FFFFFF",
388
- muted: "#8B949E",
389
- dim: "#484F58",
390
- highlight: { bg: "#12331C", fg: "#4ADE80" },
391
- badge: "#E3B341",
392
- danger: DANGER,
393
- success: "#4ADE80",
394
- bg: {
395
- main: "#0B0E14",
396
- sidebar: "#14171E"
397
- },
398
- border: "#30363D",
399
- accent: "#76A0FF",
400
- status: {
401
- pending: "gray",
402
- running: "#76A0FF",
403
- done: "#4ADE80",
404
- error: DANGER
405
- }
406
- };
407
-
408
542
  // src/ui/Notices.tsx
409
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
543
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
410
544
  var AGENT_MARKER = "\u2726";
411
- var RESERVED_ROWS = 14;
412
- var PANEL_TEXT_WIDTH = 45;
545
+ var RESERVED_ROWS2 = 14;
546
+ var PANEL_TEXT_WIDTH2 = 45;
413
547
  function messageLineCount(text) {
414
- return Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH));
548
+ return Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH2));
415
549
  }
416
550
  function noticeLineCount(notice) {
417
551
  const messageLines = (notice.messages ?? []).reduce((sum, m) => {
@@ -422,7 +556,7 @@ function noticeLineCount(notice) {
422
556
  return messageLines + tableLines;
423
557
  }
424
558
  function fitVisibleNotices(notices, windowRows) {
425
- const budget = Math.max(windowRows - RESERVED_ROWS, 3);
559
+ const budget = Math.max(windowRows - RESERVED_ROWS2, 3);
426
560
  let used = 0;
427
561
  let count = 0;
428
562
  for (let i = notices.length - 1; i >= 0; i--) {
@@ -455,7 +589,7 @@ function parseHex(hex) {
455
589
  }
456
590
  function Notices() {
457
591
  const notices = useWizard((s) => s.notices);
458
- const { rows: windowRows } = useWindowSize2();
592
+ const { rows: windowRows } = useWindowSize3();
459
593
  const visible = fitVisibleNotices(notices, windowRows);
460
594
  const [pulseStep, setPulseStep] = useState2(0);
461
595
  useEffect2(() => {
@@ -472,14 +606,14 @@ function Notices() {
472
606
  }, []);
473
607
  if (!visible.length) return null;
474
608
  const pulseColor = PULSE_COLORS[pulseStep];
475
- return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
609
+ return /* @__PURE__ */ jsx2(Box3, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
476
610
  const isLatest = i === visible.length - 1;
477
- return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", children: [
611
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
478
612
  notice.messages?.map((m, j) => {
479
613
  const line = typeof m === "string" ? { text: m } : m;
480
614
  const prefix = j === 0 ? `${AGENT_MARKER} ` : " ";
481
- return /* @__PURE__ */ jsxs(
482
- Text2,
615
+ return /* @__PURE__ */ jsxs2(
616
+ Text3,
483
617
  {
484
618
  color: isLatest && !line.color ? pulseColor : line.color ?? COLORS.dim,
485
619
  bold: line.bold,
@@ -497,37 +631,37 @@ function Notices() {
497
631
  }
498
632
 
499
633
  // src/ui/PromptInput.tsx
500
- import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
634
+ import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
501
635
  import TextInput from "ink-text-input";
502
636
  import { useState as useState4 } from "react";
503
637
 
504
638
  // src/ui/NextAction.tsx
505
- import { Box as Box3, Text as Text3 } from "ink";
506
- import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
639
+ import { Box as Box4, Text as Text4 } from "ink";
640
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
507
641
  function NextAction({
508
642
  action,
509
643
  keyHint,
510
644
  hierarchy = "primary"
511
645
  }) {
512
- return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "row", gap: 1, children: [
513
- hierarchy === "primary" && /* @__PURE__ */ jsx3(Text3, { color: COLORS.success, bold: true, children: `> ${action}` }),
514
- hierarchy === "secondary" && /* @__PURE__ */ jsxs2(Fragment, { children: [
515
- /* @__PURE__ */ jsx3(Text3, { color: COLORS.success, bold: true, children: `>` }),
516
- /* @__PURE__ */ jsx3(Text3, { color: COLORS.primary, bold: true, children: action })
646
+ return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "row", gap: 1, children: [
647
+ hierarchy === "primary" && /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `> ${action}` }),
648
+ hierarchy === "secondary" && /* @__PURE__ */ jsxs3(Fragment, { children: [
649
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `>` }),
650
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, bold: true, children: action })
517
651
  ] }),
518
- /* @__PURE__ */ jsxs2(Box3, { children: [
519
- /* @__PURE__ */ jsx3(Text3, { color: COLORS.muted, children: "press " }),
520
- /* @__PURE__ */ jsx3(Text3, { color: COLORS.muted, children: `[` }),
521
- /* @__PURE__ */ jsx3(Text3, { color: COLORS.primary, children: keyHint }),
522
- /* @__PURE__ */ jsx3(Text3, { color: COLORS.muted, children: `]` })
652
+ /* @__PURE__ */ jsxs3(Box4, { children: [
653
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: "press " }),
654
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `[` }),
655
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, children: keyHint }),
656
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `]` })
523
657
  ] })
524
658
  ] });
525
659
  }
526
660
 
527
661
  // src/ui/SelectPrompt.tsx
528
- import { Box as Box4, Text as Text4, measureElement as measureElement2, useInput, useWindowSize as useWindowSize3 } from "ink";
662
+ import { Box as Box5, Text as Text5, measureElement as measureElement2, useInput, useWindowSize as useWindowSize4 } from "ink";
529
663
  import { useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
530
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
664
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
531
665
  var CANCEL = "cancel";
532
666
  var ARROW_WIDTH = 4;
533
667
  var COLUMN_GAP = 2;
@@ -564,7 +698,7 @@ function SelectPrompt({
564
698
  if (multi) hints.push({ key: "[space]", label: "select" });
565
699
  hints.push({ key: "[enter]", label: "confirm" });
566
700
  const containerRef = useRef2(null);
567
- const { columns } = useWindowSize3();
701
+ const { columns } = useWindowSize4();
568
702
  const [width, setWidth] = useState3(columns);
569
703
  useLayoutEffect(() => {
570
704
  if (containerRef.current) {
@@ -609,53 +743,53 @@ function SelectPrompt({
609
743
  }
610
744
  }
611
745
  });
612
- return /* @__PURE__ */ jsx4(Box4, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 1, width, children: [
613
- error && /* @__PURE__ */ jsx4(Text4, { color: COLORS.danger, children: error }),
614
- messages?.map((m, i) => /* @__PURE__ */ jsx4(Text4, { color: COLORS.muted, children: m }, `msg-${i}`)),
746
+ return /* @__PURE__ */ jsx4(Box5, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, width, children: [
747
+ error && /* @__PURE__ */ jsx4(Text5, { color: COLORS.danger, children: error }),
748
+ messages?.map((m, i) => /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
615
749
  table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
616
- /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
617
- question && /* @__PURE__ */ jsx4(Text4, { color: COLORS.muted, children: question }),
618
- helpText && /* @__PURE__ */ jsx4(Text4, { color: COLORS.dim, children: helpText })
750
+ /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
751
+ question && /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: question }),
752
+ helpText && /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: helpText })
619
753
  ] }),
620
- /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: rows.map((option, i) => {
754
+ /* @__PURE__ */ jsx4(Box5, { flexDirection: "column", children: rows.map((option, i) => {
621
755
  const highlighted = i === index;
622
756
  const isCancel = i === cancelIndex;
623
757
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
624
758
  const sec = isCancel ? void 0 : secondary?.[i];
625
759
  const labelColor = highlighted ? COLORS.highlight.fg : void 0;
626
- const label = /* @__PURE__ */ jsxs3(Text4, { color: labelColor, wrap: "truncate", children: [
760
+ const label = /* @__PURE__ */ jsxs4(Text5, { color: labelColor, wrap: "truncate", children: [
627
761
  highlighted ? "\u276F " : " ",
628
762
  bullet,
629
763
  option
630
764
  ] });
631
765
  const isText = sec?.kind === "text";
632
- return /* @__PURE__ */ jsxs3(
633
- Box4,
766
+ return /* @__PURE__ */ jsxs4(
767
+ Box5,
634
768
  {
635
769
  width: isText ? "100%" : barWidth,
636
770
  paddingX: 1,
637
771
  paddingY: 1,
638
772
  backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
639
773
  children: [
640
- /* @__PURE__ */ jsx4(Box4, { width: isText ? labelWidth : barLabelWidth, children: label }),
641
- isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box4, { width: textWidth, children: /* @__PURE__ */ jsx4(
642
- Text4,
774
+ /* @__PURE__ */ jsx4(Box5, { width: isText ? labelWidth : barLabelWidth, children: label }),
775
+ isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box5, { width: textWidth, children: /* @__PURE__ */ jsx4(
776
+ Text5,
643
777
  {
644
778
  wrap: "truncate",
645
779
  color: highlighted ? COLORS.primary : COLORS.muted,
646
780
  children: sec.value
647
781
  }
648
782
  ) }),
649
- sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box4, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text4, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
783
+ sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box5, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text5, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
650
784
  ]
651
785
  },
652
786
  `row-${i}`
653
787
  );
654
788
  }) }),
655
- /* @__PURE__ */ jsx4(Text4, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs3(Text4, { children: [
789
+ /* @__PURE__ */ jsx4(Text5, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
656
790
  i > 0 ? " " : "",
657
- /* @__PURE__ */ jsx4(Text4, { color: COLORS.primary, children: key }),
658
- /* @__PURE__ */ jsxs3(Text4, { color: COLORS.dim, children: [
791
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: key }),
792
+ /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
659
793
  " ",
660
794
  label
661
795
  ] })
@@ -664,7 +798,7 @@ function SelectPrompt({
664
798
  }
665
799
 
666
800
  // src/ui/PromptInput.tsx
667
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
801
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
668
802
  var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
669
803
  function EnterToContinuePrompt({
670
804
  question,
@@ -675,10 +809,10 @@ function EnterToContinuePrompt({
675
809
  if (key.return) onDecide(true);
676
810
  else if (key.escape) onDecide(false);
677
811
  });
678
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
679
- messages?.map((m, i) => /* @__PURE__ */ jsx5(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
680
- question && /* @__PURE__ */ jsx5(Text5, { color: COLORS.primary, children: question }),
681
- /* @__PURE__ */ jsxs4(Box5, { gap: 1, flexDirection: "column", children: [
812
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
813
+ messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
814
+ question && /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, children: question }),
815
+ /* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
682
816
  /* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
683
817
  /* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
684
818
  ] })
@@ -688,11 +822,11 @@ function PromptInput() {
688
822
  const { phase, inputReq, submitInput } = useWizard();
689
823
  const [draft, setDraft] = useState4("");
690
824
  if (phase === "done" || phase === "error") {
691
- return /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
825
+ return /* @__PURE__ */ jsx5(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
692
826
  }
693
827
  if (phase !== "awaitingInput" || !inputReq) return null;
694
828
  if (inputReq.promptType === "multipleChoice") {
695
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
829
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
696
830
  SelectPrompt,
697
831
  {
698
832
  question: inputReq.prompt,
@@ -709,7 +843,7 @@ function PromptInput() {
709
843
  ) });
710
844
  }
711
845
  if (inputReq.promptType === "multiSelect") {
712
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
846
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
713
847
  SelectPrompt,
714
848
  {
715
849
  multi: true,
@@ -724,7 +858,7 @@ function PromptInput() {
724
858
  ) });
725
859
  }
726
860
  if (inputReq.promptType === "notice") {
727
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
861
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
728
862
  SelectPrompt,
729
863
  {
730
864
  question: inputReq.prompt,
@@ -746,7 +880,7 @@ function PromptInput() {
746
880
  }
747
881
  if (inputReq.promptType === "acceptReject") {
748
882
  const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
749
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
883
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
750
884
  SelectPrompt,
751
885
  {
752
886
  question: inputReq.prompt,
@@ -757,11 +891,11 @@ function PromptInput() {
757
891
  }
758
892
  ) });
759
893
  }
760
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
761
- inputReq.error && /* @__PURE__ */ jsx5(Text5, { color: COLORS.danger, children: inputReq.error }),
762
- inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
763
- /* @__PURE__ */ jsxs4(Box5, { children: [
764
- /* @__PURE__ */ jsxs4(Text5, { color: COLORS.primary, children: [
894
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
895
+ inputReq.error && /* @__PURE__ */ jsx5(Text6, { color: COLORS.danger, children: inputReq.error }),
896
+ inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
897
+ /* @__PURE__ */ jsxs5(Box6, { children: [
898
+ /* @__PURE__ */ jsxs5(Text6, { color: COLORS.primary, children: [
765
899
  inputReq.prompt,
766
900
  " "
767
901
  ] }),
@@ -783,7 +917,7 @@ function PromptInput() {
783
917
  // src/ui/Welcome.tsx
784
918
  import { dirname as dirname2, join as join3 } from "node:path";
785
919
  import { fileURLToPath } from "node:url";
786
- import { Box as Box6, Spacer, Text as Text6, useInput as useInput3, useWindowSize as useWindowSize4 } from "ink";
920
+ import { Box as Box7, Spacer, Text as Text7, useInput as useInput3, useWindowSize as useWindowSize5 } from "ink";
787
921
 
788
922
  // src/ui/copy/welcome.ts
789
923
  var sidebarItems = [
@@ -811,27 +945,27 @@ var sidebarItems = [
811
945
 
812
946
  // src/ui/Welcome.tsx
813
947
  import Image, { InkPictureProvider } from "ink-picture";
814
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
948
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
815
949
  var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
816
950
  function SidebarItem({
817
951
  title,
818
952
  description
819
953
  }) {
820
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
821
- /* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
822
- /* @__PURE__ */ jsx6(Text6, { color: COLORS.success, children: "\u2192" }),
823
- /* @__PURE__ */ jsx6(Text6, { color: COLORS.strong, bold: true, children: title })
954
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
955
+ /* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
956
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.success, children: "\u2192" }),
957
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.strong, bold: true, children: title })
824
958
  ] }),
825
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", gap: 2, children: [
959
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 2, children: [
826
960
  /* @__PURE__ */ jsx6(Spacer, {}),
827
- /* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: description })
961
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: description })
828
962
  ] })
829
963
  ] });
830
964
  }
831
965
  function Welcome() {
832
966
  const confirmStart = useWizard((s) => s.confirmStart);
833
967
  const openLearnMore = useWizard((s) => s.openLearnMore);
834
- const { rows } = useWindowSize4();
968
+ const { rows } = useWindowSize5();
835
969
  useInput3((input, key) => {
836
970
  if (key.return) confirmStart();
837
971
  else if (input === "i") openLearnMore();
@@ -850,15 +984,15 @@ function Welcome() {
850
984
  if (rows < 30) {
851
985
  layout = scales["small"];
852
986
  }
853
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
987
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
854
988
  /* @__PURE__ */ jsx6(
855
- Box6,
989
+ Box7,
856
990
  {
857
991
  paddingY: layout.main.padding.y,
858
992
  paddingX: layout.main.padding.x,
859
993
  flexDirection: "column",
860
994
  justifyContent: "center",
861
- children: /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 2, children: [
995
+ children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 2, children: [
862
996
  /* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
863
997
  Image,
864
998
  {
@@ -870,16 +1004,16 @@ function Welcome() {
870
1004
  protocol: "halfBlock"
871
1005
  }
872
1006
  ) }),
873
- /* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
874
- /* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
1007
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
1008
+ /* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
875
1009
  /* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
876
1010
  /* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
877
1011
  ] })
878
1012
  ] })
879
1013
  }
880
1014
  ),
881
- /* @__PURE__ */ jsxs5(
882
- Box6,
1015
+ /* @__PURE__ */ jsxs6(
1016
+ Box7,
883
1017
  {
884
1018
  backgroundColor: COLORS.bg.sidebar,
885
1019
  width: 40,
@@ -889,7 +1023,7 @@ function Welcome() {
889
1023
  flexDirection: "column",
890
1024
  justifyContent: "center",
891
1025
  children: [
892
- /* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
1026
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
893
1027
  sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
894
1028
  ]
895
1029
  }
@@ -899,7 +1033,7 @@ function Welcome() {
899
1033
 
900
1034
  // src/ui/LearnMore.tsx
901
1035
  import { Fragment as Fragment2 } from "react";
902
- import { Box as Box7, Text as Text7, useInput as useInput4, useWindowSize as useWindowSize5 } from "ink";
1036
+ import { Box as Box8, Text as Text8, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
903
1037
 
904
1038
  // src/ui/copy/learn-more.ts
905
1039
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -936,7 +1070,7 @@ var policyLinks = [
936
1070
  ];
937
1071
 
938
1072
  // src/ui/LearnMore.tsx
939
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1073
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
940
1074
  var TAG_COLORS = {
941
1075
  READ: COLORS.success,
942
1076
  WRITE: COLORS.badge,
@@ -952,25 +1086,25 @@ function NeverLine({
952
1086
  }) {
953
1087
  const used = segments.reduce((n, s) => n + s.text.length, 0);
954
1088
  const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
955
- return /* @__PURE__ */ jsxs6(Text7, { children: [
956
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: "\u2502" }),
1089
+ return /* @__PURE__ */ jsxs7(Text8, { children: [
1090
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" }),
957
1091
  " ".repeat(NEVER_BOX_PAD_X),
958
- segments.map((s, i) => /* @__PURE__ */ jsx7(Text7, { color: s.color, bold: s.bold, children: s.text }, i)),
1092
+ segments.map((s, i) => /* @__PURE__ */ jsx7(Text8, { color: s.color, bold: s.bold, children: s.text }, i)),
959
1093
  " ".repeat(rightPad),
960
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: "\u2502" })
1094
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" })
961
1095
  ] });
962
1096
  }
963
1097
  function LearnMore() {
964
1098
  const confirmStart = useWizard((s) => s.confirmStart);
965
1099
  const backToHome = useWizard((s) => s.backToHome);
966
- const { columns } = useWindowSize5();
1100
+ const { columns } = useWindowSize6();
967
1101
  const dividerWidth = Math.max(0, columns - PADDING_X * 2);
968
1102
  useInput4((_input, key) => {
969
1103
  if (key.escape) backToHome();
970
1104
  else if (key.return) confirmStart();
971
1105
  });
972
- return /* @__PURE__ */ jsxs6(
973
- Box7,
1106
+ return /* @__PURE__ */ jsxs7(
1107
+ Box8,
974
1108
  {
975
1109
  flexDirection: "column",
976
1110
  paddingX: PADDING_X,
@@ -978,20 +1112,20 @@ function LearnMore() {
978
1112
  width: "100%",
979
1113
  gap: 1,
980
1114
  children: [
981
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
982
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: accessIntro }),
983
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", marginTop: 1, children: [
984
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
985
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, marginTop: 1, children: [
986
- /* @__PURE__ */ jsx7(Box7, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text7, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
987
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsxs6(Text7, { children: [
988
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.strong, bold: true, children: item.title }),
989
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
1115
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
1116
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: accessIntro }),
1117
+ /* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", marginTop: 1, children: [
1118
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
1119
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, marginTop: 1, children: [
1120
+ /* @__PURE__ */ jsx7(Box8, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text8, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
1121
+ /* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text8, { children: [
1122
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: item.title }),
1123
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
990
1124
  ] }) })
991
1125
  ] })
992
1126
  ] }, item.tag)) }),
993
- /* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "column", children: [
994
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
1127
+ /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", children: [
1128
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
995
1129
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
996
1130
  /* @__PURE__ */ jsx7(
997
1131
  NeverLine,
@@ -1000,7 +1134,7 @@ function LearnMore() {
1000
1134
  segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
1001
1135
  }
1002
1136
  ),
1003
- neverItems.map((item) => /* @__PURE__ */ jsxs6(Fragment2, { children: [
1137
+ neverItems.map((item) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
1004
1138
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1005
1139
  /* @__PURE__ */ jsx7(
1006
1140
  NeverLine,
@@ -1015,23 +1149,23 @@ function LearnMore() {
1015
1149
  )
1016
1150
  ] }, item)),
1017
1151
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1018
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1152
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1019
1153
  ] }),
1020
- /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
1021
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1022
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.accent, children: link.url })
1154
+ /* @__PURE__ */ jsx7(Box8, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1155
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1156
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.accent, children: link.url })
1023
1157
  ] }, link.label)) }),
1024
- /* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1025
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
1026
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "[" }),
1027
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.primary, children: "esc" }),
1028
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "] back" })
1158
+ /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1159
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1160
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
1161
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "esc" }),
1162
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "] back" })
1029
1163
  ] }),
1030
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
1031
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "[" }),
1032
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.primary, children: "enter" }),
1033
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "]" }),
1034
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.success, bold: true, children: "start wizard" })
1164
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1165
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
1166
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "enter" }),
1167
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "]" }),
1168
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.success, bold: true, children: "start wizard" })
1035
1169
  ] })
1036
1170
  ] })
1037
1171
  ]
@@ -1040,10 +1174,10 @@ function LearnMore() {
1040
1174
  }
1041
1175
 
1042
1176
  // src/ui/Sidebar.tsx
1043
- import { Box as Box10, Text as Text10 } from "ink";
1177
+ import { Box as Box11, Text as Text11 } from "ink";
1044
1178
 
1045
1179
  // src/ui/Steps.tsx
1046
- import { Box as Box8, Text as Text8 } from "ink";
1180
+ import { Box as Box9, Text as Text9 } from "ink";
1047
1181
  import Spinner from "ink-spinner";
1048
1182
 
1049
1183
  // src/core/persistence.ts
@@ -1072,11 +1206,11 @@ async function clearWorkflowState(workflowId) {
1072
1206
  }
1073
1207
 
1074
1208
  // src/ui/Steps.tsx
1075
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1209
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1076
1210
  function Steps() {
1077
1211
  const { steps } = useWizard();
1078
1212
  const visibleSteps = steps.filter(isStepVisible);
1079
- 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: [
1213
+ return /* @__PURE__ */ jsx8(Box9, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx8(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status[s.status], children: [
1080
1214
  s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
1081
1215
  " ",
1082
1216
  s.title
@@ -1086,7 +1220,7 @@ function CurrentStep() {
1086
1220
  const { steps } = useWizard();
1087
1221
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
1088
1222
  if (!currentStep) return null;
1089
- return /* @__PURE__ */ jsxs7(Text8, { color: COLORS.status.running, children: [
1223
+ return /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status.running, children: [
1090
1224
  /* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
1091
1225
  " ",
1092
1226
  ` ${currentStep.title}`
@@ -1094,19 +1228,19 @@ function CurrentStep() {
1094
1228
  }
1095
1229
 
1096
1230
  // src/ui/Progress.tsx
1097
- import { Box as Box9, Text as Text9 } from "ink";
1098
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1231
+ import { Box as Box10, Text as Text10 } from "ink";
1232
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1099
1233
  function Progress() {
1100
1234
  const { steps, currentStepIndex } = useWizard();
1101
1235
  const visibleSteps = steps.filter(isStepVisible);
1102
1236
  if (visibleSteps.length === 0) return null;
1103
1237
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
1104
1238
  const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
1105
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1106
- /* @__PURE__ */ jsx9(Text9, { color: COLORS.muted, children: "STEP" }),
1107
- /* @__PURE__ */ jsx9(Text9, { bold: true, children: activeStepNumber }),
1108
- /* @__PURE__ */ jsx9(Text9, { bold: true, children: "/" }),
1109
- /* @__PURE__ */ jsx9(Text9, { bold: true, children: visibleSteps.length })
1239
+ return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
1240
+ /* @__PURE__ */ jsx9(Text10, { color: COLORS.muted, children: "STEP" }),
1241
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: activeStepNumber }),
1242
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: "/" }),
1243
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: visibleSteps.length })
1110
1244
  ] });
1111
1245
  }
1112
1246
 
@@ -1117,10 +1251,10 @@ var sidebarCommands = [
1117
1251
  ];
1118
1252
 
1119
1253
  // src/ui/Sidebar.tsx
1120
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1254
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1121
1255
  function Sidebar() {
1122
- return /* @__PURE__ */ jsxs9(
1123
- Box10,
1256
+ return /* @__PURE__ */ jsxs10(
1257
+ Box11,
1124
1258
  {
1125
1259
  backgroundColor: "#14171E",
1126
1260
  width: 30,
@@ -1129,16 +1263,16 @@ function Sidebar() {
1129
1263
  flexDirection: "column",
1130
1264
  justifyContent: "space-between",
1131
1265
  children: [
1132
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
1133
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "PROGRESS" }),
1266
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
1267
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: "PROGRESS" }),
1134
1268
  /* @__PURE__ */ jsx10(Steps, {})
1135
1269
  ] }),
1136
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
1270
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
1137
1271
  /* @__PURE__ */ jsx10(Progress, {}),
1138
- /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: sidebarCommands.map((c) => {
1139
- return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
1140
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1141
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: c.description })
1272
+ /* @__PURE__ */ jsx10(Box11, { flexDirection: "column", children: sidebarCommands.map((c) => {
1273
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
1274
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1275
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: c.description })
1142
1276
  ] });
1143
1277
  }) })
1144
1278
  ] })
@@ -1148,12 +1282,12 @@ function Sidebar() {
1148
1282
  }
1149
1283
 
1150
1284
  // src/ui/Ribbon.tsx
1151
- import { Box as Box11, Text as Text11 } from "ink";
1152
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1285
+ import { Box as Box12, Text as Text12 } from "ink";
1286
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1153
1287
  function Ribbon() {
1154
1288
  const firstCommand = sidebarCommands[0];
1155
- return /* @__PURE__ */ jsxs10(
1156
- Box11,
1289
+ return /* @__PURE__ */ jsxs11(
1290
+ Box12,
1157
1291
  {
1158
1292
  backgroundColor: "#14171E",
1159
1293
  flexDirection: "row",
@@ -1163,9 +1297,9 @@ function Ribbon() {
1163
1297
  children: [
1164
1298
  /* @__PURE__ */ jsx11(Progress, {}),
1165
1299
  /* @__PURE__ */ jsx11(CurrentStep, {}),
1166
- /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
1167
- /* @__PURE__ */ jsx11(Text11, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1168
- /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: firstCommand.description })
1300
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
1301
+ /* @__PURE__ */ jsx11(Text12, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1302
+ /* @__PURE__ */ jsx11(Text12, { color: COLORS.muted, children: firstCommand.description })
1169
1303
  ] })
1170
1304
  ]
1171
1305
  }
@@ -1177,8 +1311,8 @@ import { useState as useState6 } from "react";
1177
1311
 
1178
1312
  // src/ui/Logs.tsx
1179
1313
  import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState5 } from "react";
1180
- import { Box as Box12, Text as Text12, measureElement as measureElement3, useInput as useInput5, useWindowSize as useWindowSize6 } from "ink";
1181
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1314
+ import { Box as Box13, Text as Text13, measureElement as measureElement3, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
1315
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1182
1316
  var KIND_COLOR = {
1183
1317
  tool: COLORS.primary,
1184
1318
  prompt: COLORS.badge
@@ -1208,7 +1342,7 @@ function formatTimestamp(ms) {
1208
1342
  }
1209
1343
  function Logs() {
1210
1344
  const logs = useWizard((s) => s.logs);
1211
- const { rows, columns } = useWindowSize6();
1345
+ const { rows, columns } = useWindowSize7();
1212
1346
  const viewportRef = useRef3(null);
1213
1347
  const [viewportHeight, setViewportHeight] = useState5(0);
1214
1348
  const [viewportWidth, setViewportWidth] = useState5(0);
@@ -1245,10 +1379,10 @@ function Logs() {
1245
1379
  const visible = logs.slice(scrollOffset, scrollOffset + capacity);
1246
1380
  const hiddenAbove = scrollOffset;
1247
1381
  const hiddenBelow = logs.length - scrollOffset - visible.length;
1248
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1249
- logs.length === 0 && /* @__PURE__ */ jsx12(Text12, { color: COLORS.dim, children: "No logs yet." }),
1250
- /* @__PURE__ */ jsxs11(Box12, { ref: viewportRef, flexDirection: "column", flexGrow: 1, children: [
1251
- hiddenAbove > 0 && /* @__PURE__ */ jsxs11(Text12, { color: COLORS.dim, children: [
1382
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1383
+ logs.length === 0 && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "No logs yet." }),
1384
+ /* @__PURE__ */ jsxs12(Box13, { ref: viewportRef, flexDirection: "column", flexGrow: 1, children: [
1385
+ hiddenAbove > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
1252
1386
  "\u2191 ",
1253
1387
  hiddenAbove,
1254
1388
  " more"
@@ -1263,20 +1397,20 @@ function Logs() {
1263
1397
  const name = truncate2(entry.name, budget);
1264
1398
  budget -= name.length;
1265
1399
  const preview = rawPreview ? truncate2(rawPreview, budget) : "";
1266
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: ROW_GAP, children: [
1267
- /* @__PURE__ */ jsx12(Text12, { color: COLORS.dim, children: timestamp }),
1268
- /* @__PURE__ */ jsx12(Text12, { color: logNameColor(entry), wrap: "truncate", children: name }),
1269
- preview && /* @__PURE__ */ jsx12(Text12, { color: COLORS.dim, wrap: "truncate", children: preview }),
1270
- durationText && /* @__PURE__ */ jsx12(Text12, { color: COLORS.dim, children: durationText })
1400
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: ROW_GAP, children: [
1401
+ /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: timestamp }),
1402
+ /* @__PURE__ */ jsx12(Text13, { color: logNameColor(entry), wrap: "truncate", children: name }),
1403
+ preview && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, wrap: "truncate", children: preview }),
1404
+ durationText && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: durationText })
1271
1405
  ] }, entry.id);
1272
1406
  }),
1273
- hiddenBelow > 0 && /* @__PURE__ */ jsxs11(Text12, { color: COLORS.dim, children: [
1407
+ hiddenBelow > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
1274
1408
  "\u2193 ",
1275
1409
  hiddenBelow,
1276
1410
  " more"
1277
1411
  ] })
1278
1412
  ] }),
1279
- /* @__PURE__ */ jsx12(Text12, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1413
+ /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1280
1414
  ] });
1281
1415
  }
1282
1416
 
@@ -1468,11 +1602,11 @@ function track(event, payload) {
1468
1602
  }
1469
1603
 
1470
1604
  // src/ui/App.tsx
1471
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1605
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1472
1606
  function App() {
1473
1607
  const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
1474
1608
  const { exit } = useApp();
1475
- const { columns, rows } = useWindowSize7();
1609
+ const { columns, rows } = useWindowSize8();
1476
1610
  const [showLogs, setShowLogs] = useState6(false);
1477
1611
  const finished = phase === "done" || phase === "error";
1478
1612
  const currentStep = steps[currentStepIndex];
@@ -1485,7 +1619,7 @@ function App() {
1485
1619
  { isActive: finished }
1486
1620
  );
1487
1621
  useInput6((_input, key) => {
1488
- if (phase === "idle" || phase === "preflight") return;
1622
+ if (phase === "idle" || phase === "authenticating") return;
1489
1623
  if (key.tab) {
1490
1624
  setShowLogs(!showLogs);
1491
1625
  track("AI Wizard Interaction", {
@@ -1495,7 +1629,7 @@ function App() {
1495
1629
  });
1496
1630
  }
1497
1631
  });
1498
- const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1632
+ const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1499
1633
  useInput6((_input, key) => {
1500
1634
  if (escOwnedElsewhere) return;
1501
1635
  if (key.escape) {
@@ -1508,53 +1642,67 @@ function App() {
1508
1642
  exit();
1509
1643
  }
1510
1644
  });
1511
- const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1645
+ const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1512
1646
  const flexDirection = columns > 90 ? "row" : "column";
1513
1647
  const showSidebar = flexDirection === "row";
1514
- return /* @__PURE__ */ jsxs12(
1515
- Box13,
1516
- {
1517
- backgroundColor: COLORS.bg.main,
1518
- flexDirection: "row",
1519
- width: columns,
1520
- minHeight: rows,
1521
- children: [
1522
- mainWindowVisible && /* @__PURE__ */ jsxs12(
1523
- Box13,
1524
- {
1525
- flexDirection,
1526
- width: "100%",
1527
- justifyContent: "space-between",
1528
- children: [
1529
- showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
1530
- /* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
1531
- /* @__PURE__ */ jsxs12(
1532
- Box13,
1533
- {
1534
- flexDirection: "column",
1535
- paddingX: 4,
1536
- paddingY: 2,
1537
- width: showSidebar ? 70 : "100%",
1538
- flexGrow: showSidebar ? 1 : 0,
1539
- children: [
1540
- /* @__PURE__ */ jsx13(Notices, {}),
1541
- /* @__PURE__ */ jsx13(PromptInput, {}),
1542
- phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1543
- phase === "error" && error && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status.error, children: [
1544
- "\u2716 ",
1545
- error
1546
- ] }) })
1547
- ]
1548
- }
1549
- )
1550
- ),
1551
- showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1552
- ]
1553
- }
1554
- ),
1555
- (phase === "idle" || phase === "preflight") && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1556
- ]
1557
- }
1648
+ return (
1649
+ /* Exactly the viewport, clipped — never `minHeight`, which lets the frame
1650
+ grow past the terminal. Ink then abandons diffing to clear and repaint
1651
+ the whole screen, and the scrolling that frame causes throws off its
1652
+ cursor arithmetic: flicker and leftover rows, worst when a burst of CLI
1653
+ output is swapped out. Clipping drops the bottom of an over-tall frame;
1654
+ the per-panel row budgets are what keep it from coming to that. */
1655
+ /* @__PURE__ */ jsxs13(
1656
+ Box14,
1657
+ {
1658
+ backgroundColor: COLORS.bg.main,
1659
+ flexDirection: "row",
1660
+ width: columns,
1661
+ height: rows,
1662
+ overflow: "hidden",
1663
+ children: [
1664
+ mainWindowVisible && /* @__PURE__ */ jsxs13(
1665
+ Box14,
1666
+ {
1667
+ flexDirection,
1668
+ width: "100%",
1669
+ justifyContent: "space-between",
1670
+ children: [
1671
+ showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
1672
+ /* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
1673
+ /* @__PURE__ */ jsxs13(
1674
+ Box14,
1675
+ {
1676
+ flexDirection: "column",
1677
+ paddingX: 4,
1678
+ paddingY: 2,
1679
+ width: showSidebar ? 70 : "100%",
1680
+ flexGrow: showSidebar ? 1 : 0,
1681
+ children: [
1682
+ phase === "authenticating" && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginBottom: 1, children: [
1683
+ /* @__PURE__ */ jsx13(Text14, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
1684
+ /* @__PURE__ */ jsx13(Text14, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
1685
+ ] }),
1686
+ /* @__PURE__ */ jsx13(CliOutput, {}),
1687
+ /* @__PURE__ */ jsx13(Notices, {}),
1688
+ /* @__PURE__ */ jsx13(PromptInput, {}),
1689
+ phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1690
+ phase === "error" && error && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.error, children: [
1691
+ "\u2716 ",
1692
+ error
1693
+ ] }) })
1694
+ ]
1695
+ }
1696
+ )
1697
+ ),
1698
+ showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1699
+ ]
1700
+ }
1701
+ ),
1702
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1703
+ ]
1704
+ }
1705
+ )
1558
1706
  );
1559
1707
  }
1560
1708
 
@@ -1790,61 +1938,138 @@ async function runWorkflow(workflow, appId) {
1790
1938
  }
1791
1939
  }
1792
1940
 
1793
- // src/lib/algoliaProfile.ts
1794
- import { readFile as readFile3 } from "node:fs/promises";
1795
- import { createRequire as createRequire2 } from "node:module";
1796
- import { homedir as homedir2 } from "node:os";
1797
- import { join as join6 } from "node:path";
1798
- import { parse as parseToml } from "toml";
1799
- var require3 = createRequire2(import.meta.url);
1800
- function configPath() {
1801
- const base = process.env.XDG_CONFIG_HOME || join6(homedir2(), ".config");
1802
- return join6(base, "algolia", "config.toml");
1803
- }
1804
- function profilesFromConfig(tomlText) {
1805
- let parsed;
1941
+ // src/lib/algoliaApp.ts
1942
+ import { z as z4 } from "zod";
1943
+ var applicationSchema = z4.object({
1944
+ id: z4.string().min(1),
1945
+ name: z4.string().default(""),
1946
+ plan: z4.string().optional()
1947
+ });
1948
+ var listSchema = z4.array(
1949
+ z4.object({
1950
+ id: z4.string().min(1),
1951
+ name: z4.string().default(""),
1952
+ plan_label: z4.string().optional()
1953
+ }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
1954
+ );
1955
+ async function currentApplication() {
1956
+ let raw;
1806
1957
  try {
1807
- parsed = parseToml(tomlText);
1958
+ raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1808
1959
  } catch {
1809
- return [];
1960
+ return null;
1961
+ }
1962
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1963
+ return parsed.success ? parsed.data : null;
1964
+ }
1965
+ async function requireApplication() {
1966
+ const app = await currentApplication();
1967
+ if (!app) {
1968
+ throw new Error(
1969
+ "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1970
+ );
1971
+ }
1972
+ return app;
1973
+ }
1974
+ async function listApplications() {
1975
+ const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1976
+ const parsed = listSchema.safeParse(parseJson(raw));
1977
+ if (!parsed.success) {
1978
+ throw new Error("Could not read the list of Algolia applications.");
1979
+ }
1980
+ return parsed.data;
1981
+ }
1982
+ async function selectApplication(id) {
1983
+ const raw = await runAlgoliaCli(
1984
+ ["application", "select", "--non-interactive", "--app-id", id],
1985
+ { onOutput: stderrSink }
1986
+ );
1987
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1988
+ if (!parsed.success) {
1989
+ throw new Error(
1990
+ `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
1991
+ );
1810
1992
  }
1811
- const profiles = Object.entries(parsed).filter(
1812
- ([, t]) => typeof t.application_id === "string" && typeof t.api_key === "string"
1813
- ).map(([name, t]) => ({
1814
- name,
1815
- appId: t.application_id,
1816
- apiKey: t.api_key,
1817
- isDefault: t.default === true
1818
- }));
1819
- profiles.sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
1820
- return profiles.map(({ name, appId, apiKey }) => ({ name, appId, apiKey }));
1821
- }
1822
- async function loadActiveProfile() {
1823
- let profiles;
1993
+ return parsed.data;
1994
+ }
1995
+ function parseJson(text) {
1824
1996
  try {
1825
- profiles = profilesFromConfig(await readFile3(configPath(), "utf8"));
1997
+ return JSON.parse(text);
1826
1998
  } catch {
1827
- profiles = [];
1999
+ return void 0;
1828
2000
  }
1829
- const profile = profiles[0];
1830
- if (!profile) {
2001
+ }
2002
+
2003
+ // src/lib/algoliaAppPicker.ts
2004
+ function secondaryFor(app) {
2005
+ return app.plan ? { kind: "badge", value: app.plan } : void 0;
2006
+ }
2007
+ function labelFor(app) {
2008
+ return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
2009
+ }
2010
+ function selectAndReport(app) {
2011
+ useWizard.getState().pushCliOutput(
2012
+ "stdout",
2013
+ `Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
2014
+ );
2015
+ return selectApplication(app.id);
2016
+ }
2017
+ async function promptForApplication() {
2018
+ const store = useWizard.getState();
2019
+ const apps = await listApplications();
2020
+ if (apps.length === 0) {
1831
2021
  throw new Error(
1832
- "No Algolia profile is configured. Run `npx @algolia/cli auth login` to authenticate."
2022
+ "This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
1833
2023
  );
1834
2024
  }
1835
- return profile;
2025
+ if (apps.length === 1) {
2026
+ const only = apps[0];
2027
+ logger.info(
2028
+ { app: only.id },
2029
+ "single application on the account; selecting it"
2030
+ );
2031
+ return selectAndReport(only);
2032
+ }
2033
+ const messages = ["Which Algolia application should the wizard work in?"];
2034
+ for (; ; ) {
2035
+ const choice = await store.requestUserInput({
2036
+ prompt: "Select an application",
2037
+ promptType: "multipleChoice",
2038
+ options: apps.map(labelFor),
2039
+ secondary: apps.map(secondaryFor),
2040
+ messages
2041
+ });
2042
+ const chosen = apps.find((app) => labelFor(app) === choice);
2043
+ if (!chosen) {
2044
+ throw new Error("Application picker received an unexpected selection");
2045
+ }
2046
+ try {
2047
+ return await selectAndReport(chosen);
2048
+ } catch (err) {
2049
+ logger.warn(
2050
+ { app: chosen.id, err: err.message },
2051
+ "application select failed; re-prompting"
2052
+ );
2053
+ messages.push(
2054
+ `Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
2055
+ );
2056
+ }
2057
+ }
2058
+ }
2059
+ async function ensureApplication() {
2060
+ return await currentApplication() ?? await promptForApplication();
1836
2061
  }
1837
2062
 
1838
2063
  // src/workflows/default.ts
1839
- import { z as z25 } from "zod";
2064
+ import { z as z27 } from "zod";
1840
2065
 
1841
2066
  // src/actions/listIndices.ts
1842
- import { z as z3 } from "zod";
1843
- var indicesListSchema = z3.object({
1844
- items: z3.array(
1845
- z3.object({
1846
- name: z3.string(),
1847
- entries: z3.number().default(0)
2067
+ import { z as z5 } from "zod";
2068
+ var indicesListSchema = z5.object({
2069
+ items: z5.array(
2070
+ z5.object({
2071
+ name: z5.string(),
2072
+ entries: z5.number().default(0)
1848
2073
  })
1849
2074
  )
1850
2075
  });
@@ -1915,12 +2140,12 @@ import "zod";
1915
2140
 
1916
2141
  // src/lib/tools/listFiles.ts
1917
2142
  import { tool } from "ai";
1918
- import z4 from "zod";
2143
+ import z6 from "zod";
1919
2144
  import { readdir } from "node:fs/promises";
1920
2145
 
1921
2146
  // src/lib/tools/path.ts
1922
2147
  import { lstat } from "node:fs/promises";
1923
- import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join7, sep } from "node:path";
2148
+ import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join6, sep } from "node:path";
1924
2149
  function resolveInRoot(ctx, path) {
1925
2150
  const target = resolve2(ctx.cwd, path);
1926
2151
  const rel = relative(ctx.root, target);
@@ -1936,7 +2161,7 @@ async function hasSymlinkParent(ctx, target) {
1936
2161
  let current = ctx.root;
1937
2162
  const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
1938
2163
  for (const part of parts) {
1939
- current = join7(current, part);
2164
+ current = join6(current, part);
1940
2165
  try {
1941
2166
  if ((await lstat(current)).isSymbolicLink()) return true;
1942
2167
  } catch (err) {
@@ -1951,7 +2176,7 @@ async function hasSymlinkParent(ctx, target) {
1951
2176
  function listFilesTool(ctx) {
1952
2177
  return tool({
1953
2178
  description: "List files in the current working directory",
1954
- inputSchema: z4.object(),
2179
+ inputSchema: z6.object(),
1955
2180
  execute: async () => {
1956
2181
  logger.info("called listFiles tool");
1957
2182
  if (++ctx.counts.list > ctx.limits.list) {
@@ -1967,13 +2192,13 @@ function listFilesTool(ctx) {
1967
2192
 
1968
2193
  // src/lib/tools/changeDirectory.ts
1969
2194
  import { tool as tool2 } from "ai";
1970
- import z5 from "zod";
2195
+ import z7 from "zod";
1971
2196
  import { stat } from "node:fs/promises";
1972
2197
  function changeDirectoryTool(ctx) {
1973
2198
  return tool2({
1974
2199
  description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
1975
- inputSchema: z5.object({
1976
- path: z5.string().describe("Directory to change into")
2200
+ inputSchema: z7.object({
2201
+ path: z7.string().describe("Directory to change into")
1977
2202
  }),
1978
2203
  execute: async ({ path }) => {
1979
2204
  logger.info({ path }, "called changeDirectory tool");
@@ -1995,13 +2220,13 @@ function changeDirectoryTool(ctx) {
1995
2220
 
1996
2221
  // src/lib/tools/reportStatus.ts
1997
2222
  import { tool as tool3 } from "ai";
1998
- import z6 from "zod";
2223
+ import z8 from "zod";
1999
2224
  function reportStatusTool(output) {
2000
2225
  return tool3({
2001
2226
  description: "Report the status of your execution. Return a reason in case of failure.",
2002
- inputSchema: z6.object({
2003
- status: z6.enum(["success", "fail"]),
2004
- reason: z6.string().optional(),
2227
+ inputSchema: z8.object({
2228
+ status: z8.enum(["success", "fail"]),
2229
+ reason: z8.string().optional(),
2005
2230
  output
2006
2231
  }),
2007
2232
  execute: async ({ status, reason, output: output2 }) => {
@@ -2013,8 +2238,8 @@ function reportStatusTool(output) {
2013
2238
 
2014
2239
  // src/lib/tools/readFile.ts
2015
2240
  import { tool as tool4 } from "ai";
2016
- import z7 from "zod";
2017
- import { readFile as readFile4 } from "node:fs/promises";
2241
+ import z9 from "zod";
2242
+ import { readFile as readFile3 } from "node:fs/promises";
2018
2243
 
2019
2244
  // src/lib/tools/env.ts
2020
2245
  import { basename } from "node:path";
@@ -2041,8 +2266,8 @@ function redactEnvValues(content) {
2041
2266
  function readFileTool(ctx) {
2042
2267
  return tool4({
2043
2268
  description: "Read the contents of a file at the given path",
2044
- inputSchema: z7.object({
2045
- filePath: z7.string().describe("Path to the file to read")
2269
+ inputSchema: z9.object({
2270
+ filePath: z9.string().describe("Path to the file to read")
2046
2271
  }),
2047
2272
  execute: async ({ filePath }) => {
2048
2273
  if (++ctx.counts.read > ctx.limits.read) {
@@ -2052,7 +2277,7 @@ function readFileTool(ctx) {
2052
2277
  const resolved = resolveInRoot(ctx, filePath);
2053
2278
  if (!resolved.ok) return resolved.error;
2054
2279
  try {
2055
- const content = await readFile4(resolved.target, "utf8");
2280
+ const content = await readFile3(resolved.target, "utf8");
2056
2281
  return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
2057
2282
  } catch (err) {
2058
2283
  return `Error reading ${filePath}: ${err.message}`;
@@ -2063,15 +2288,15 @@ function readFileTool(ctx) {
2063
2288
 
2064
2289
  // src/lib/tools/writeFile.ts
2065
2290
  import { tool as tool5 } from "ai";
2066
- import z8 from "zod";
2291
+ import z10 from "zod";
2067
2292
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2068
2293
  import { dirname as dirname4 } from "node:path";
2069
2294
  function writeFileTool(ctx) {
2070
2295
  return tool5({
2071
2296
  description: "Write content to a file at the given path, overwriting it. To set Algolia credentials in an env file, use writeCredentials instead of this tool.",
2072
- inputSchema: z8.object({
2073
- filePath: z8.string().describe("Path to the file to write"),
2074
- content: z8.string().describe("Content to write to the file")
2297
+ inputSchema: z10.object({
2298
+ filePath: z10.string().describe("Path to the file to write"),
2299
+ content: z10.string().describe("Content to write to the file")
2075
2300
  }),
2076
2301
  execute: async ({ filePath, content }) => {
2077
2302
  logger.info({ filePath }, "called writeFile tool");
@@ -2096,9 +2321,95 @@ function writeFileTool(ctx) {
2096
2321
 
2097
2322
  // src/lib/tools/writeAlgoliaCredentials.ts
2098
2323
  import { tool as tool6 } from "ai";
2099
- import z9 from "zod";
2100
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
2324
+ import z12 from "zod";
2325
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
2101
2326
  import { dirname as dirname5 } from "node:path";
2327
+
2328
+ // src/lib/algoliaApiKey.ts
2329
+ import { z as z11 } from "zod";
2330
+ var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
2331
+ var WRITE_ACLS = [
2332
+ "addObject",
2333
+ "deleteObject",
2334
+ "settings",
2335
+ "editSettings",
2336
+ "listIndexes"
2337
+ ];
2338
+ var WRITE_ACL_SET = new Set(WRITE_ACLS);
2339
+ var apiKeySchema = z11.object({
2340
+ value: z11.string().min(1),
2341
+ acl: z11.array(z11.string()).default([]),
2342
+ indexes: z11.array(z11.string()).default([])
2343
+ });
2344
+ var apiKeyListSchema = z11.object({
2345
+ items: z11.array(apiKeySchema).optional(),
2346
+ keys: z11.array(apiKeySchema).optional()
2347
+ }).transform((o) => o.items ?? o.keys ?? []);
2348
+ var createdKeySchema = z11.object({
2349
+ key: z11.string().min(1).optional(),
2350
+ value: z11.string().min(1).optional()
2351
+ });
2352
+ function canReuse(key, index) {
2353
+ return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
2354
+ }
2355
+ async function createSearchKey(index) {
2356
+ const stdout = await runAlgoliaCli([
2357
+ "apikeys",
2358
+ "create",
2359
+ "--indices",
2360
+ index,
2361
+ "--acl",
2362
+ "search,browse",
2363
+ "--description",
2364
+ `wizard search-only key for ${index}`,
2365
+ "-o",
2366
+ "json"
2367
+ ]);
2368
+ const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
2369
+ const created = key ?? value;
2370
+ if (!created) throw new Error("apikeys create returned no key value");
2371
+ return created;
2372
+ }
2373
+ function canReuseForWrites(key, index) {
2374
+ return WRITE_ACLS.every((acl) => key.acl.includes(acl)) && key.acl.every((acl) => WRITE_ACL_SET.has(acl)) && key.indexes.includes(index);
2375
+ }
2376
+ async function resolveWriteKey(index) {
2377
+ const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2378
+ const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key2) => canReuseForWrites(key2, index))?.value;
2379
+ if (existing) {
2380
+ logger.info({ index }, "reusing existing write API key");
2381
+ return existing;
2382
+ }
2383
+ logger.info({ index }, "no reusable write key found; creating one");
2384
+ const created = await runAlgoliaCli([
2385
+ "apikeys",
2386
+ "create",
2387
+ "--indices",
2388
+ index,
2389
+ "--acl",
2390
+ WRITE_ACLS.join(","),
2391
+ "--description",
2392
+ `wizard write key for ${index}`,
2393
+ "-o",
2394
+ "json"
2395
+ ]);
2396
+ const { key, value } = createdKeySchema.parse(JSON.parse(created));
2397
+ const writeKey = key ?? value;
2398
+ if (!writeKey) throw new Error("apikeys create returned no key value");
2399
+ return writeKey;
2400
+ }
2401
+ async function resolveSearchOnlyKey(index) {
2402
+ const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2403
+ const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
2404
+ if (existing) {
2405
+ logger.info({ index }, "reusing existing search-only API key");
2406
+ return existing;
2407
+ }
2408
+ logger.info({ index }, "no reusable search-only key found; creating one");
2409
+ return createSearchKey(index);
2410
+ }
2411
+
2412
+ // src/lib/tools/writeAlgoliaCredentials.ts
2102
2413
  var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
2103
2414
  var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
2104
2415
  function appendEnv(content, entries) {
@@ -2112,9 +2423,9 @@ function hasEnv(content, name) {
2112
2423
  }
2113
2424
  function writeCredentialsTool(ctx) {
2114
2425
  return tool6({
2115
- description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) into the given env file. The credentials are read from the local Algolia CLI profile; you only pass the path to the env file (e.g. ".env"). If the file already defines ${APP_ID_VAR} or ${API_KEY_VAR}, the write is skipped and existing values are left untouched.`,
2116
- inputSchema: z9.object({
2117
- filePath: z9.string().describe(
2426
+ description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file (e.g. ".env"). If the file already defines ${APP_ID_VAR} or ${API_KEY_VAR}, the write is skipped and existing values are left untouched.`,
2427
+ inputSchema: z12.object({
2428
+ filePath: z12.string().describe(
2118
2429
  'Path to the env file to write credentials into (e.g. ".env")'
2119
2430
  )
2120
2431
  }),
@@ -2122,11 +2433,17 @@ function writeCredentialsTool(ctx) {
2122
2433
  logger.info({ filePath }, "called writeCredentials tool");
2123
2434
  const resolved = resolveInRoot(ctx, filePath);
2124
2435
  if (resolved.ok === false) return resolved.error;
2125
- let profile;
2436
+ const targetIndex = useWizard.getState().targetIndex;
2437
+ if (!targetIndex) {
2438
+ return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
2439
+ }
2440
+ let appId;
2441
+ let writeKey;
2126
2442
  try {
2127
- profile = await loadActiveProfile();
2128
- } catch {
2129
- return "Error: no Algolia profile is configured, so credentials cannot be written. Ask the user to authenticate with the Algolia CLI first.";
2443
+ appId = (await requireApplication()).id;
2444
+ writeKey = await resolveWriteKey(targetIndex);
2445
+ } catch (err) {
2446
+ return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
2130
2447
  }
2131
2448
  try {
2132
2449
  if (await hasSymlinkParent(ctx, resolved.target)) {
@@ -2134,7 +2451,7 @@ function writeCredentialsTool(ctx) {
2134
2451
  }
2135
2452
  let existing = "";
2136
2453
  try {
2137
- existing = await readFile5(resolved.target, "utf8");
2454
+ existing = await readFile4(resolved.target, "utf8");
2138
2455
  } catch (err) {
2139
2456
  if (err.code !== "ENOENT") throw err;
2140
2457
  }
@@ -2145,8 +2462,8 @@ function writeCredentialsTool(ctx) {
2145
2462
  return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
2146
2463
  }
2147
2464
  const envWithCredentials = appendEnv(existing, [
2148
- [APP_ID_VAR, profile.appId],
2149
- [API_KEY_VAR, profile.apiKey]
2465
+ [APP_ID_VAR, appId],
2466
+ [API_KEY_VAR, writeKey]
2150
2467
  ]);
2151
2468
  await mkdir4(dirname5(resolved.target), { recursive: true });
2152
2469
  await writeFile4(resolved.target, envWithCredentials, "utf8");
@@ -2160,16 +2477,16 @@ function writeCredentialsTool(ctx) {
2160
2477
 
2161
2478
  // src/lib/tools/searchFiles.ts
2162
2479
  import { tool as tool7 } from "ai";
2163
- import z10 from "zod";
2164
- import { readdir as readdir2, readFile as readFile6 } from "node:fs/promises";
2165
- import { join as join8 } from "node:path";
2480
+ import z13 from "zod";
2481
+ import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
2482
+ import { join as join7 } from "node:path";
2166
2483
  var MAX_QUERY_LENGTH = 1e3;
2167
2484
  async function walkFiles(dir) {
2168
2485
  const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
2169
2486
  const out = [];
2170
2487
  for (const e of await readdir2(dir, { withFileTypes: true })) {
2171
2488
  if (e.name.startsWith(".") || skip.has(e.name)) continue;
2172
- const full = join8(dir, e.name);
2489
+ const full = join7(dir, e.name);
2173
2490
  if (e.isDirectory()) out.push(...await walkFiles(full));
2174
2491
  else if (e.isFile()) out.push(full);
2175
2492
  }
@@ -2178,9 +2495,9 @@ async function walkFiles(dir) {
2178
2495
  function searchFilesTool(ctx) {
2179
2496
  return tool7({
2180
2497
  description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
2181
- inputSchema: z10.object({
2182
- query: z10.string().describe("JavaScript RegExp pattern to search for"),
2183
- path: z10.string().optional().describe("Directory to search in (default: cwd)")
2498
+ inputSchema: z13.object({
2499
+ query: z13.string().describe("JavaScript RegExp pattern to search for"),
2500
+ path: z13.string().optional().describe("Directory to search in (default: cwd)")
2184
2501
  }),
2185
2502
  execute: async ({ query, path = "." }) => {
2186
2503
  logger.info({ query, path }, "called searchFiles tool");
@@ -2202,7 +2519,7 @@ function searchFilesTool(ctx) {
2202
2519
  for (const file of await walkFiles(resolved.target)) {
2203
2520
  let content;
2204
2521
  try {
2205
- content = await readFile6(file, "utf8");
2522
+ content = await readFile5(file, "utf8");
2206
2523
  } catch {
2207
2524
  continue;
2208
2525
  }
@@ -2224,7 +2541,7 @@ function searchFilesTool(ctx) {
2224
2541
 
2225
2542
  // src/lib/tools/verifyImplementation.ts
2226
2543
  import { tool as tool8 } from "ai";
2227
- import z11 from "zod";
2544
+ import z14 from "zod";
2228
2545
 
2229
2546
  // src/lib/tools/utils/runCommand.ts
2230
2547
  import { spawn as spawn2 } from "node:child_process";
@@ -2246,9 +2563,9 @@ function runCommand(command, args, cwd) {
2246
2563
  }
2247
2564
 
2248
2565
  // src/lib/tools/utils/packageManager.ts
2249
- import { readFile as readFile7 } from "node:fs/promises";
2566
+ import { readFile as readFile6 } from "node:fs/promises";
2250
2567
  import { existsSync } from "node:fs";
2251
- import { join as join9 } from "node:path";
2568
+ import { join as join8 } from "node:path";
2252
2569
  var LOCKFILES = [
2253
2570
  ["pnpm-lock.yaml", "pnpm"],
2254
2571
  ["yarn.lock", "yarn"],
@@ -2257,13 +2574,13 @@ var LOCKFILES = [
2257
2574
  ["package-lock.json", "npm"]
2258
2575
  ];
2259
2576
  async function readPackageJson(cwd = process.cwd()) {
2260
- return JSON.parse(await readFile7(join9(cwd, "package.json"), "utf8"));
2577
+ return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
2261
2578
  }
2262
2579
  function packageManagerFrom(pkg) {
2263
2580
  return pkg.packageManager?.split("@")[0] ?? "npm";
2264
2581
  }
2265
2582
  function packageManagerFromLockfile(cwd) {
2266
- return LOCKFILES.find(([file]) => existsSync(join9(cwd, file)))?.[1];
2583
+ return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
2267
2584
  }
2268
2585
  async function detectPackageManager(cwd) {
2269
2586
  try {
@@ -2304,7 +2621,7 @@ async function runRepoVerificationCheck() {
2304
2621
  function verifyImplementationTool() {
2305
2622
  return tool8({
2306
2623
  description: "Run the repo's mechanical verification check for generated implementation changes. Detects lint/typecheck/check from package.json and returns structured pass/fail evidence for the verifier to interpret.",
2307
- inputSchema: z11.object(),
2624
+ inputSchema: z14.object(),
2308
2625
  execute: async () => {
2309
2626
  logger.info("called verifyImplementation tool");
2310
2627
  return runRepoVerificationCheck();
@@ -2318,7 +2635,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
2318
2635
  import { nanoid as nanoid2 } from "nanoid";
2319
2636
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
2320
2637
  import { dirname as dirname6 } from "node:path";
2321
- import z12 from "zod";
2638
+ import z15 from "zod";
2322
2639
  var DATA_DIR = ".algolia-wizard/data";
2323
2640
  var RECORD_MODEL = "claude-haiku-4-5";
2324
2641
  var MAX_RECORDS = 100;
@@ -2330,17 +2647,17 @@ var anthropic = createAnthropic({
2330
2647
  function generateRecordTool(ctx) {
2331
2648
  return tool9({
2332
2649
  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.",
2333
- inputSchema: z12.object({
2334
- entityName: z12.string().describe("Name of the entity to generate records for."),
2335
- attributes: z12.array(z12.string()).describe("Attribute names each record must contain."),
2336
- count: z12.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2337
- hint: z12.string().optional().describe("Optional context to steer realistic values.")
2650
+ inputSchema: z15.object({
2651
+ entityName: z15.string().describe("Name of the entity to generate records for."),
2652
+ attributes: z15.array(z15.string()).describe("Attribute names each record must contain."),
2653
+ count: z15.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2654
+ hint: z15.string().optional().describe("Optional context to steer realistic values.")
2338
2655
  }),
2339
2656
  execute: async ({ entityName, attributes, count, hint }) => {
2340
2657
  logger.info({ entityName, count }, "called generateRecord tool");
2341
2658
  try {
2342
- const value = z12.union([z12.string(), z12.number(), z12.boolean(), z12.null()]);
2343
- const recordSchema = z12.object(
2659
+ const value = z15.union([z15.string(), z15.number(), z15.boolean(), z15.null()]);
2660
+ const recordSchema = z15.object(
2344
2661
  Object.fromEntries(attributes.map((attr) => [attr, value]))
2345
2662
  );
2346
2663
  const generateBatch = async (batchCount) => {
@@ -2350,8 +2667,8 @@ function generateRecordTool(ctx) {
2350
2667
  const { output } = await generateText({
2351
2668
  model: anthropic(RECORD_MODEL),
2352
2669
  output: Output.object({
2353
- schema: z12.object({
2354
- records: z12.array(recordSchema).length(batchCount)
2670
+ schema: z15.object({
2671
+ records: z15.array(recordSchema).length(batchCount)
2355
2672
  })
2356
2673
  }),
2357
2674
  prompt: [
@@ -2409,12 +2726,12 @@ function generateRecordTool(ctx) {
2409
2726
 
2410
2727
  // src/lib/tools/notifyUser.ts
2411
2728
  import { tool as tool10 } from "ai";
2412
- import z13 from "zod";
2729
+ import z16 from "zod";
2413
2730
  function notifyUserTool() {
2414
2731
  return tool10({
2415
2732
  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.`,
2416
- inputSchema: z13.object({
2417
- message: z13.string().describe(
2733
+ inputSchema: z16.object({
2734
+ message: z16.string().describe(
2418
2735
  "Short, plain-language description of what you are doing now."
2419
2736
  )
2420
2737
  }),
@@ -2594,10 +2911,10 @@ async function runAgent(req) {
2594
2911
  }
2595
2912
 
2596
2913
  // src/actions/detectLanguage.ts
2597
- import z16 from "zod";
2598
- var detectLanguageSchema = z16.object({
2599
- languages: z16.array(z16.object({ name: z16.string(), version: z16.string() })),
2600
- frameworks: z16.array(z16.object({ name: z16.string(), version: z16.string() }))
2914
+ import z19 from "zod";
2915
+ var detectLanguageSchema = z19.object({
2916
+ languages: z19.array(z19.object({ name: z19.string(), version: z19.string() })),
2917
+ frameworks: z19.array(z19.object({ name: z19.string(), version: z19.string() }))
2601
2918
  });
2602
2919
  var detectLanguage = () => runAgent({
2603
2920
  instructions: [
@@ -2615,31 +2932,31 @@ var detectLanguage = () => runAgent({
2615
2932
  });
2616
2933
 
2617
2934
  // src/actions/analyzeCodebase.ts
2618
- import z17 from "zod";
2935
+ import z20 from "zod";
2619
2936
  var READONLY_TOOLS = [
2620
2937
  "listFiles",
2621
2938
  "changeDirectory",
2622
2939
  "readFile",
2623
2940
  "searchFiles"
2624
2941
  ];
2625
- var ingestionAnalysisSchema = z17.object({
2626
- ingestionAnalysis: z17.array(
2627
- z17.object({
2628
- name: z17.string(),
2629
- paths: z17.array(z17.string()),
2942
+ var ingestionAnalysisSchema = z20.object({
2943
+ ingestionAnalysis: z20.array(
2944
+ z20.object({
2945
+ name: z20.string(),
2946
+ paths: z20.array(z20.string()),
2630
2947
  // indexable fields the agent found for this entity
2631
- attributes: z17.array(z17.string())
2948
+ attributes: z20.array(z20.string())
2632
2949
  })
2633
2950
  )
2634
2951
  });
2635
- var searchImplementationAnalysisSchema = z17.object({
2636
- searchImplementationAnalysis: z17.string()
2952
+ var searchImplementationAnalysisSchema = z20.object({
2953
+ searchImplementationAnalysis: z20.string()
2637
2954
  });
2638
- var verificationSchema = z17.object({
2639
- verification: z17.array(z17.string())
2955
+ var verificationSchema = z20.object({
2956
+ verification: z20.array(z20.string())
2640
2957
  });
2641
2958
  var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
2642
- var analyzeCodebaseSchema = z17.object({
2959
+ var analyzeCodebaseSchema = z20.object({
2643
2960
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
2644
2961
  searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
2645
2962
  verification: verificationSchema.shape.verification.optional(),
@@ -2701,7 +3018,7 @@ async function runAnalysis(mode, extraInstructions = []) {
2701
3018
  // package.json
2702
3019
  var package_default = {
2703
3020
  name: "@algolia/wizard",
2704
- version: "0.7.0",
3021
+ version: "0.8.0-rc.53.46",
2705
3022
  description: "Magically implement Algolia functionality in your codebase",
2706
3023
  type: "module",
2707
3024
  engines: {
@@ -2749,7 +3066,6 @@ var package_default = {
2749
3066
  dependencies: {
2750
3067
  "@ai-sdk/anthropic": "^3.0.81",
2751
3068
  "@ai-sdk/openai-compatible": "^2.0.47",
2752
- "@algolia/cli": "^5.11.0",
2753
3069
  "@hono/node-server": "^2.0.10",
2754
3070
  "@mishieck/ink-titled-box": "^0.4.2",
2755
3071
  "@segment/analytics-node": "^3.1.0",
@@ -2764,7 +3080,6 @@ var package_default = {
2764
3080
  nanoid: "^5.1.15",
2765
3081
  pino: "^10.3.1",
2766
3082
  react: "^19.2.7",
2767
- toml: "^4.1.1",
2768
3083
  varlock: "^1.5.1",
2769
3084
  zod: "^4.4.3",
2770
3085
  zustand: "^5.0.14"
@@ -2822,8 +3137,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
2822
3137
  }
2823
3138
 
2824
3139
  // src/actions/confirmLanguage.ts
2825
- import z19 from "zod";
2826
- var confirmLanguageSchema = z19.object({
3140
+ import z22 from "zod";
3141
+ var confirmLanguageSchema = z22.object({
2827
3142
  languages: detectLanguageSchema.shape.languages
2828
3143
  });
2829
3144
  async function confirmLanguage(ctx) {
@@ -2844,8 +3159,8 @@ async function confirmLanguage(ctx) {
2844
3159
  }
2845
3160
 
2846
3161
  // src/actions/confirmFramework.ts
2847
- import z20 from "zod";
2848
- var confirmFrameworkSchema = z20.object({
3162
+ import z23 from "zod";
3163
+ var confirmFrameworkSchema = z23.object({
2849
3164
  frameworks: detectLanguageSchema.shape.frameworks
2850
3165
  });
2851
3166
  var CURATED_FRAMEWORKS = [
@@ -2973,8 +3288,8 @@ async function promptUser(ctx, params) {
2973
3288
  }
2974
3289
 
2975
3290
  // src/actions/confirmEntities.ts
2976
- import z21 from "zod";
2977
- var confirmEntitiesSchema = z21.object({
3291
+ import z24 from "zod";
3292
+ var confirmEntitiesSchema = z24.object({
2978
3293
  // Final detection — the focused re-run may supersede project-scan's.
2979
3294
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
2980
3295
  confirmedEntities: confirmedEntitiesFieldSchema
@@ -3044,15 +3359,15 @@ async function confirmEntities(ctx) {
3044
3359
  }
3045
3360
 
3046
3361
  // src/actions/review.ts
3047
- import { z as z22 } from "zod";
3048
- var reviewSchema = z22.object({
3362
+ import { z as z25 } from "zod";
3363
+ var reviewSchema = z25.object({
3049
3364
  // Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
3050
3365
  // not one entry per workflow step — a step's raw output can be a long,
3051
3366
  // multi-paragraph blob (see implement.ts's summaries.join), and mirroring
3052
3367
  // that 1:1 is what made the old per-step summary an unreadable wall of text.
3053
- summaryPoints: z22.array(z22.string()),
3054
- reviewPrompt: z22.string(),
3055
- nextSteps: z22.array(z22.string())
3368
+ summaryPoints: z25.array(z25.string()),
3369
+ reviewPrompt: z25.string(),
3370
+ nextSteps: z25.array(z25.string())
3056
3371
  });
3057
3372
  function formatCompletedSteps(steps) {
3058
3373
  if (!steps.length) return "(no prior steps completed)";
@@ -3103,16 +3418,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
3103
3418
  };
3104
3419
 
3105
3420
  // src/actions/implement.ts
3106
- import z24 from "zod";
3421
+ import z26 from "zod";
3107
3422
 
3108
3423
  // src/lib/worktree.ts
3109
3424
  import { execFile, spawn as spawn3 } from "node:child_process";
3110
- import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3425
+ import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3111
3426
  import {
3112
3427
  basename as basename2,
3113
3428
  dirname as dirname7,
3114
3429
  isAbsolute as isAbsolute2,
3115
- join as join10,
3430
+ join as join9,
3116
3431
  relative as relative2,
3117
3432
  resolve as resolve3
3118
3433
  } from "node:path";
@@ -3146,7 +3461,7 @@ async function isWorkingTreeDirty(repoRoot) {
3146
3461
  return out.trim().length > 0;
3147
3462
  }
3148
3463
  async function pruneOldWorktrees(repoRoot) {
3149
- const dir = join10(stateDir(repoRoot), "worktrees");
3464
+ const dir = join9(stateDir(repoRoot), "worktrees");
3150
3465
  const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
3151
3466
  for (const slug of stale) {
3152
3467
  const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
@@ -3157,7 +3472,7 @@ async function pruneOldWorktrees(repoRoot) {
3157
3472
  "worktree",
3158
3473
  "remove",
3159
3474
  "--force",
3160
- join10(dir, slug)
3475
+ join9(dir, slug)
3161
3476
  ]);
3162
3477
  await git(["-C", repoRoot, "branch", "-D", branch]);
3163
3478
  } catch (err) {
@@ -3171,7 +3486,7 @@ async function pruneOldWorktrees(repoRoot) {
3171
3486
  async function createWorktree(repoRoot) {
3172
3487
  const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
3173
3488
  const dirSlug = branch.replace(/\//g, "-");
3174
- const path = join10(stateDir(repoRoot), "worktrees", dirSlug);
3489
+ const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
3175
3490
  await git(["-C", repoRoot, "worktree", "prune"]);
3176
3491
  await pruneOldWorktrees(repoRoot);
3177
3492
  await mkdir6(dirname7(path), { recursive: true });
@@ -3291,8 +3606,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
3291
3606
  } catch {
3292
3607
  return { ok: false, reason: `"${sourcePath}" does not exist` };
3293
3608
  }
3294
- const relPath = join10(ingestDir, basename2(source));
3295
- const dest = join10(worktreePath, relPath);
3609
+ const relPath = join9(ingestDir, basename2(source));
3610
+ const dest = join9(worktreePath, relPath);
3296
3611
  try {
3297
3612
  await mkdir6(dirname7(dest), { recursive: true });
3298
3613
  await copyFile(source, dest);
@@ -3308,10 +3623,10 @@ function hasEnvVar(content, name) {
3308
3623
  return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
3309
3624
  }
3310
3625
  async function writeSearchEnvValues(worktreePath, vars) {
3311
- const target = join10(worktreePath, ".env");
3626
+ const target = join9(worktreePath, ".env");
3312
3627
  let existing = "";
3313
3628
  try {
3314
- existing = await readFile8(target, "utf8");
3629
+ existing = await readFile7(target, "utf8");
3315
3630
  } catch (err) {
3316
3631
  if (err.code !== "ENOENT") throw err;
3317
3632
  }
@@ -3379,63 +3694,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
3379
3694
  }
3380
3695
  }
3381
3696
 
3382
- // src/lib/algoliaApiKey.ts
3383
- import { z as z23 } from "zod";
3384
- var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
3385
- var apiKeySchema = z23.object({
3386
- value: z23.string().min(1),
3387
- acl: z23.array(z23.string()).default([]),
3388
- indexes: z23.array(z23.string()).default([])
3389
- });
3390
- var apiKeyListSchema = z23.object({
3391
- items: z23.array(apiKeySchema).optional(),
3392
- keys: z23.array(apiKeySchema).optional()
3393
- }).transform((o) => o.items ?? o.keys ?? []);
3394
- var createdKeySchema = z23.object({
3395
- key: z23.string().min(1).optional(),
3396
- value: z23.string().min(1).optional()
3397
- });
3398
- function canReuse(key, index) {
3399
- return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
3400
- }
3401
- async function createSearchKey(index) {
3402
- const stdout = await runAlgoliaCli([
3403
- "apikeys",
3404
- "create",
3405
- "--indices",
3406
- index,
3407
- "--acl",
3408
- "search,browse",
3409
- "--description",
3410
- `wizard search-only key for ${index}`,
3411
- "-o",
3412
- "json"
3413
- ]);
3414
- const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
3415
- const created = key ?? value;
3416
- if (!created) throw new Error("apikeys create returned no key value");
3417
- return created;
3418
- }
3419
- async function resolveSearchOnlyKey(index) {
3420
- const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
3421
- const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
3422
- if (existing) {
3423
- logger.info({ index }, "reusing existing search-only API key");
3424
- return existing;
3425
- }
3426
- logger.info({ index }, "no reusable search-only key found; creating one");
3427
- return createSearchKey(index);
3428
- }
3429
-
3430
3697
  // src/lib/algoliaDocs.ts
3431
3698
  import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
3432
- import { dirname as dirname8, join as join11 } from "node:path";
3699
+ import { dirname as dirname8, join as join10 } from "node:path";
3433
3700
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3434
- var DOCS_SUBPATH = join11("docs", "algolia-sdk");
3701
+ var DOCS_SUBPATH = join10("docs", "algolia-sdk");
3435
3702
  function findDocsDir() {
3436
3703
  let dir = dirname8(fileURLToPath2(import.meta.url));
3437
3704
  for (; ; ) {
3438
- const candidate = join11(dir, DOCS_SUBPATH);
3705
+ const candidate = join10(dir, DOCS_SUBPATH);
3439
3706
  if (existsSync2(candidate)) return candidate;
3440
3707
  const parent = dirname8(dir);
3441
3708
  if (parent === dir) return void 0;
@@ -3458,7 +3725,7 @@ function loadAlgoliaDoc(language) {
3458
3725
  );
3459
3726
  return "";
3460
3727
  }
3461
- return readFileSync(join11(docsDir, files[0]), "utf8").trim();
3728
+ return readFileSync(join10(docsDir, files[0]), "utf8").trim();
3462
3729
  }
3463
3730
  function getNamedDoc(name, language) {
3464
3731
  const docsDir = findDocsDir();
@@ -3466,7 +3733,7 @@ function getNamedDoc(name, language) {
3466
3733
  logger.warn("docs/algolia-sdk not found");
3467
3734
  return "";
3468
3735
  }
3469
- const file = join11(docsDir, `${name}-${language}.md`);
3736
+ const file = join10(docsDir, `${name}-${language}.md`);
3470
3737
  if (!existsSync2(file)) {
3471
3738
  logger.warn({ name, language }, "named SDK reference not found");
3472
3739
  return "";
@@ -3493,50 +3760,50 @@ function shellQuote(value) {
3493
3760
  }
3494
3761
 
3495
3762
  // src/actions/implement.ts
3496
- var implementSchema = z24.object({
3497
- filesChanged: z24.array(z24.string()),
3498
- summary: z24.string(),
3763
+ var implementSchema = z26.object({
3764
+ filesChanged: z26.array(z26.string()),
3765
+ summary: z26.string(),
3499
3766
  // Absolute path to the throwaway worktree holding the generated changes, so
3500
3767
  // the user can open it (`cd <worktreePath>`) or inspect the diff
3501
3768
  // (`git -C <worktreePath> status/diff`).
3502
- worktreePath: z24.string().optional(),
3503
- ingestCommand: z24.string().optional(),
3769
+ worktreePath: z26.string().optional(),
3770
+ ingestCommand: z26.string().optional(),
3504
3771
  // True when the user accepted the run-now prompt and the wizard executed the
3505
3772
  // ingestion script; downstream steps use this to avoid telling the user to run
3506
3773
  // a script that already ran.
3507
- ingestScriptRan: z24.boolean().optional(),
3774
+ ingestScriptRan: z26.boolean().optional(),
3508
3775
  // Records ingested by the run-now execution, parsed from the script's
3509
3776
  // machine-readable count line; absent when the script didn't run or emitted
3510
3777
  // no parseable count.
3511
- ingestRecordCount: z24.number().optional(),
3778
+ ingestRecordCount: z26.number().optional(),
3512
3779
  // Wall-clock duration of the run-now ingestion execution, in ms.
3513
- ingestDurationMs: z24.number().optional(),
3514
- ingestionSource: z24.enum(["local", "fileUpload", "generated"]),
3780
+ ingestDurationMs: z26.number().optional(),
3781
+ ingestionSource: z26.enum(["local", "fileUpload", "generated"]),
3515
3782
  // Suggested names/values, built from framework detection. The search agent is
3516
3783
  // instructed to rename the prefix if it doesn't match the project's build
3517
3784
  // tool, so the names it actually wrote can differ — treat these as hints, not
3518
3785
  // ground truth (the agent's summary carries the final names).
3519
- searchEnvVars: z24.array(
3520
- z24.object({
3521
- name: z24.string(),
3522
- value: z24.string()
3786
+ searchEnvVars: z26.array(
3787
+ z26.object({
3788
+ name: z26.string(),
3789
+ value: z26.string()
3523
3790
  })
3524
3791
  ).optional()
3525
3792
  });
3526
- var implementationOutputSchema = z24.object({
3527
- summary: z24.string(),
3793
+ var implementationOutputSchema = z26.object({
3794
+ summary: z26.string(),
3528
3795
  // Ingestion only: how to run the generated script, as a structured pair the
3529
3796
  // wizard turns into an argv (`<runtime> <entrypoint>`) — never a free-form
3530
3797
  // command string. `runtime` is constrained to an allowlisted interpreter and
3531
3798
  // `entrypoint` is validated to a worktree-relative path before execution, so
3532
3799
  // the agent cannot inject extra commands or swap the interpreter.
3533
- runtime: z24.enum(INGEST_RUNTIMES).optional(),
3534
- entrypoint: z24.string().optional()
3800
+ runtime: z26.enum(INGEST_RUNTIMES).optional(),
3801
+ entrypoint: z26.string().optional()
3535
3802
  });
3536
- var verificationOutputSchema = z24.object({
3537
- summary: z24.string(),
3538
- sufficient: z24.boolean(),
3539
- additionalInstructions: z24.string().optional()
3803
+ var verificationOutputSchema = z26.object({
3804
+ summary: z26.string(),
3805
+ sufficient: z26.boolean(),
3806
+ additionalInstructions: z26.string().optional()
3540
3807
  });
3541
3808
  var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
3542
3809
  var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
@@ -3650,7 +3917,7 @@ function searchInstructions(input) {
3650
3917
  doc,
3651
3918
  `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.`,
3652
3919
  "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.",
3653
- // appId always resolves (loadActiveProfile throws otherwise); only the
3920
+ // appId always resolves (requireApplication throws otherwise); only the
3654
3921
  // search-only key is best-effort and can fall back to a placeholder.
3655
3922
  `Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
3656
3923
  // Names are fixed, not the agent's to rename: the wizard writes the
@@ -3799,6 +4066,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3799
4066
  }
3800
4067
  }
3801
4068
  const targetIndex = selected?.selection;
4069
+ useWizard.getState().setTargetIndex(targetIndex ?? null);
3802
4070
  await assertGitRepoWithHead(repoRoot);
3803
4071
  if (await isWorkingTreeDirty(repoRoot)) {
3804
4072
  await confirmDirtyWorkingTree(ctx, repoRoot);
@@ -3809,7 +4077,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3809
4077
  let appId;
3810
4078
  let searchKey;
3811
4079
  if (useCases.includes("search")) {
3812
- appId = (await loadActiveProfile()).appId;
4080
+ appId = (await requireApplication()).id;
3813
4081
  try {
3814
4082
  searchKey = await resolveSearchOnlyKey(targetIndex);
3815
4083
  } catch (err) {
@@ -3920,7 +4188,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3920
4188
  messages: []
3921
4189
  }) === true;
3922
4190
  if (runNow) {
3923
- const profile = await loadActiveProfile();
4191
+ const ingestApp = await requireApplication();
4192
+ const writeKey = await resolveWriteKey(targetIndex);
3924
4193
  ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
3925
4194
  const scriptLogId = ctx.logStart("runIngestScript", {
3926
4195
  runtime: ingestRuntime,
@@ -3932,8 +4201,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3932
4201
  ingestRuntime,
3933
4202
  ingestEntrypoint,
3934
4203
  {
3935
- [APP_ID_VAR]: profile.appId,
3936
- [API_KEY_VAR]: profile.apiKey
4204
+ [APP_ID_VAR]: ingestApp.id,
4205
+ [API_KEY_VAR]: writeKey
3937
4206
  }
3938
4207
  );
3939
4208
  ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
@@ -4144,8 +4413,8 @@ var defaultWorkflow = {
4144
4413
  defineStep({
4145
4414
  id: "select-index",
4146
4415
  title: "Set up index",
4147
- outputSchema: z25.object({
4148
- selection: z25.string()
4416
+ outputSchema: z27.object({
4417
+ selection: z27.string()
4149
4418
  }),
4150
4419
  run: (ctx) => selectIndexStep(ctx)
4151
4420
  }),
@@ -4424,7 +4693,7 @@ function parseCliArgs(argv) {
4424
4693
 
4425
4694
  // src/lib/resetState.ts
4426
4695
  import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
4427
- import { join as join12 } from "node:path";
4696
+ import { join as join11 } from "node:path";
4428
4697
  var KEEP = ["wizard.log"];
4429
4698
  async function resetProjectState() {
4430
4699
  const dir = stateDir();
@@ -4436,7 +4705,7 @@ async function resetProjectState() {
4436
4705
  }
4437
4706
  const targets = entries.filter((name) => !KEEP.includes(name));
4438
4707
  await Promise.all(
4439
- targets.map((name) => rm2(join12(dir, name), { recursive: true, force: true }))
4708
+ targets.map((name) => rm2(join11(dir, name), { recursive: true, force: true }))
4440
4709
  );
4441
4710
  return { dir, removed: targets };
4442
4711
  }
@@ -4491,31 +4760,38 @@ ${formatStepList(workflow)}`);
4491
4760
  }
4492
4761
  async function run(workflow) {
4493
4762
  const store = useWizard.getState();
4494
- let instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4763
+ const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4764
+ await store.waitForStart();
4495
4765
  let user = await getUser();
4496
4766
  if (!user) {
4497
- await instance.waitUntilRenderFlush();
4498
- instance.cleanup();
4767
+ store.beginAuth();
4499
4768
  try {
4500
4769
  await runAuthLogin();
4501
4770
  } catch (err) {
4502
- console.error(err instanceof Error ? err.message : String(err));
4771
+ store.setError(err instanceof Error ? err.message : String(err));
4772
+ await instance.waitUntilExit();
4503
4773
  process.exit(1);
4504
4774
  }
4505
- instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4775
+ store.endAuth();
4506
4776
  user = await getUser();
4507
4777
  if (!user) {
4508
4778
  store.setError(
4509
- "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
4779
+ "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
4510
4780
  );
4511
4781
  await instance.waitUntilExit();
4512
4782
  process.exit(1);
4513
4783
  }
4514
4784
  }
4515
4785
  store.setUser(user);
4516
- const profile = await loadActiveProfile();
4517
- await store.waitForStart();
4518
- runWorkflow(workflow, profile?.appId);
4786
+ let app;
4787
+ try {
4788
+ app = await ensureApplication();
4789
+ } catch (err) {
4790
+ store.setError(err instanceof Error ? err.message : String(err));
4791
+ await instance.waitUntilExit();
4792
+ process.exit(1);
4793
+ }
4794
+ runWorkflow(workflow, app.id);
4519
4795
  }
4520
4796
  var started = await startup();
4521
4797
  if (typeof started === "number") {