@algolia/wizard 0.6.0 → 0.7.0-rc.53.39

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 +771 -495
  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 store2 = useWizard.getState();
45
+ const logId = store2.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: (user2) => set({ user: user2 }),
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
 
@@ -1300,11 +1434,11 @@ function track(event, payload) {
1300
1434
  }
1301
1435
 
1302
1436
  // src/ui/App.tsx
1303
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1437
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1304
1438
  function App() {
1305
1439
  const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
1306
1440
  const { exit } = useApp();
1307
- const { columns, rows } = useWindowSize7();
1441
+ const { columns, rows } = useWindowSize8();
1308
1442
  const [showLogs, setShowLogs] = useState6(false);
1309
1443
  const finished = phase === "done" || phase === "error";
1310
1444
  const currentStep = steps[currentStepIndex];
@@ -1317,7 +1451,7 @@ function App() {
1317
1451
  { isActive: finished }
1318
1452
  );
1319
1453
  useInput6((_input, key) => {
1320
- if (phase === "idle" || phase === "preflight") return;
1454
+ if (phase === "idle" || phase === "authenticating") return;
1321
1455
  if (key.tab) {
1322
1456
  setShowLogs(!showLogs);
1323
1457
  track("AI Wizard Interaction", {
@@ -1327,7 +1461,7 @@ function App() {
1327
1461
  });
1328
1462
  }
1329
1463
  });
1330
- const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1464
+ const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1331
1465
  useInput6((_input, key) => {
1332
1466
  if (escOwnedElsewhere) return;
1333
1467
  if (key.escape) {
@@ -1340,53 +1474,67 @@ function App() {
1340
1474
  exit();
1341
1475
  }
1342
1476
  });
1343
- const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1477
+ const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1344
1478
  const flexDirection = columns > 90 ? "row" : "column";
1345
1479
  const showSidebar = flexDirection === "row";
1346
- return /* @__PURE__ */ jsxs12(
1347
- Box13,
1348
- {
1349
- backgroundColor: COLORS.bg.main,
1350
- flexDirection: "row",
1351
- width: columns,
1352
- minHeight: rows,
1353
- children: [
1354
- mainWindowVisible && /* @__PURE__ */ jsxs12(
1355
- Box13,
1356
- {
1357
- flexDirection,
1358
- width: "100%",
1359
- justifyContent: "space-between",
1360
- children: [
1361
- showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
1362
- /* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
1363
- /* @__PURE__ */ jsxs12(
1364
- Box13,
1365
- {
1366
- flexDirection: "column",
1367
- paddingX: 4,
1368
- paddingY: 2,
1369
- width: showSidebar ? 70 : "100%",
1370
- flexGrow: showSidebar ? 1 : 0,
1371
- children: [
1372
- /* @__PURE__ */ jsx13(Notices, {}),
1373
- /* @__PURE__ */ jsx13(PromptInput, {}),
1374
- phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1375
- phase === "error" && error && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status.error, children: [
1376
- "\u2716 ",
1377
- error
1378
- ] }) })
1379
- ]
1380
- }
1381
- )
1382
- ),
1383
- showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1384
- ]
1385
- }
1386
- ),
1387
- (phase === "idle" || phase === "preflight") && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1388
- ]
1389
- }
1480
+ return (
1481
+ /* Exactly the viewport, clipped — never `minHeight`, which lets the frame
1482
+ grow past the terminal. Ink then abandons diffing to clear and repaint
1483
+ the whole screen, and the scrolling that frame causes throws off its
1484
+ cursor arithmetic: flicker and leftover rows, worst when a burst of CLI
1485
+ output is swapped out. Clipping drops the bottom of an over-tall frame;
1486
+ the per-panel row budgets are what keep it from coming to that. */
1487
+ /* @__PURE__ */ jsxs13(
1488
+ Box14,
1489
+ {
1490
+ backgroundColor: COLORS.bg.main,
1491
+ flexDirection: "row",
1492
+ width: columns,
1493
+ height: rows,
1494
+ overflow: "hidden",
1495
+ children: [
1496
+ mainWindowVisible && /* @__PURE__ */ jsxs13(
1497
+ Box14,
1498
+ {
1499
+ flexDirection,
1500
+ width: "100%",
1501
+ justifyContent: "space-between",
1502
+ children: [
1503
+ showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
1504
+ /* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
1505
+ /* @__PURE__ */ jsxs13(
1506
+ Box14,
1507
+ {
1508
+ flexDirection: "column",
1509
+ paddingX: 4,
1510
+ paddingY: 2,
1511
+ width: showSidebar ? 70 : "100%",
1512
+ flexGrow: showSidebar ? 1 : 0,
1513
+ children: [
1514
+ phase === "authenticating" && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginBottom: 1, children: [
1515
+ /* @__PURE__ */ jsx13(Text14, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
1516
+ /* @__PURE__ */ jsx13(Text14, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
1517
+ ] }),
1518
+ /* @__PURE__ */ jsx13(CliOutput, {}),
1519
+ /* @__PURE__ */ jsx13(Notices, {}),
1520
+ /* @__PURE__ */ jsx13(PromptInput, {}),
1521
+ phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1522
+ phase === "error" && error && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.error, children: [
1523
+ "\u2716 ",
1524
+ error
1525
+ ] }) })
1526
+ ]
1527
+ }
1528
+ )
1529
+ ),
1530
+ showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1531
+ ]
1532
+ }
1533
+ ),
1534
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1535
+ ]
1536
+ }
1537
+ )
1390
1538
  );
1391
1539
  }
1392
1540
 
@@ -1784,61 +1932,138 @@ async function runWorkflow(workflow2, appId) {
1784
1932
  }
1785
1933
  }
1786
1934
 
1787
- // src/lib/algoliaProfile.ts
1788
- import { readFile as readFile3 } from "node:fs/promises";
1789
- import { createRequire as createRequire2 } from "node:module";
1790
- import { homedir as homedir2 } from "node:os";
1791
- import { join as join6 } from "node:path";
1792
- import { parse as parseToml } from "toml";
1793
- var require3 = createRequire2(import.meta.url);
1794
- function configPath() {
1795
- const base = process.env.XDG_CONFIG_HOME || join6(homedir2(), ".config");
1796
- return join6(base, "algolia", "config.toml");
1797
- }
1798
- function profilesFromConfig(tomlText) {
1799
- let parsed;
1935
+ // src/lib/algoliaApp.ts
1936
+ import { z as z4 } from "zod";
1937
+ var applicationSchema = z4.object({
1938
+ id: z4.string().min(1),
1939
+ name: z4.string().default(""),
1940
+ plan: z4.string().optional()
1941
+ });
1942
+ var listSchema = z4.array(
1943
+ z4.object({
1944
+ id: z4.string().min(1),
1945
+ name: z4.string().default(""),
1946
+ plan_label: z4.string().optional()
1947
+ }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
1948
+ );
1949
+ async function currentApplication() {
1950
+ let raw;
1800
1951
  try {
1801
- parsed = parseToml(tomlText);
1952
+ raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1802
1953
  } catch {
1803
- return [];
1954
+ return null;
1955
+ }
1956
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1957
+ return parsed.success ? parsed.data : null;
1958
+ }
1959
+ async function requireApplication() {
1960
+ const app2 = await currentApplication();
1961
+ if (!app2) {
1962
+ throw new Error(
1963
+ "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1964
+ );
1965
+ }
1966
+ return app2;
1967
+ }
1968
+ async function listApplications() {
1969
+ const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1970
+ const parsed = listSchema.safeParse(parseJson(raw));
1971
+ if (!parsed.success) {
1972
+ throw new Error("Could not read the list of Algolia applications.");
1973
+ }
1974
+ return parsed.data;
1975
+ }
1976
+ async function selectApplication(id) {
1977
+ const raw = await runAlgoliaCli(
1978
+ ["application", "select", "--non-interactive", "--app-id", id],
1979
+ { onOutput: stderrSink }
1980
+ );
1981
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1982
+ if (!parsed.success) {
1983
+ throw new Error(
1984
+ `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
1985
+ );
1804
1986
  }
1805
- const profiles = Object.entries(parsed).filter(
1806
- ([, t]) => typeof t.application_id === "string" && typeof t.api_key === "string"
1807
- ).map(([name, t]) => ({
1808
- name,
1809
- appId: t.application_id,
1810
- apiKey: t.api_key,
1811
- isDefault: t.default === true
1812
- }));
1813
- profiles.sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
1814
- return profiles.map(({ name, appId, apiKey }) => ({ name, appId, apiKey }));
1815
- }
1816
- async function loadActiveProfile() {
1817
- let profiles;
1987
+ return parsed.data;
1988
+ }
1989
+ function parseJson(text) {
1818
1990
  try {
1819
- profiles = profilesFromConfig(await readFile3(configPath(), "utf8"));
1991
+ return JSON.parse(text);
1820
1992
  } catch {
1821
- profiles = [];
1993
+ return void 0;
1822
1994
  }
1823
- const profile2 = profiles[0];
1824
- if (!profile2) {
1995
+ }
1996
+
1997
+ // src/lib/algoliaAppPicker.ts
1998
+ function secondaryFor(app2) {
1999
+ return app2.plan ? { kind: "badge", value: app2.plan } : void 0;
2000
+ }
2001
+ function labelFor(app2) {
2002
+ return app2.name.trim() ? `${app2.name} \u2014 ${app2.id}` : app2.id;
2003
+ }
2004
+ function selectAndReport(app2) {
2005
+ useWizard.getState().pushCliOutput(
2006
+ "stdout",
2007
+ `Selecting ${labelFor(app2)} \u2014 provisioning its API key\u2026`
2008
+ );
2009
+ return selectApplication(app2.id);
2010
+ }
2011
+ async function promptForApplication() {
2012
+ const store2 = useWizard.getState();
2013
+ const apps = await listApplications();
2014
+ if (apps.length === 0) {
1825
2015
  throw new Error(
1826
- "No Algolia profile is configured. Run `npx @algolia/cli auth login` to authenticate."
2016
+ "This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
2017
+ );
2018
+ }
2019
+ if (apps.length === 1) {
2020
+ const only = apps[0];
2021
+ logger.info(
2022
+ { app: only.id },
2023
+ "single application on the account; selecting it"
1827
2024
  );
2025
+ return selectAndReport(only);
1828
2026
  }
1829
- return profile2;
2027
+ const messages = ["Which Algolia application should the wizard work in?"];
2028
+ for (; ; ) {
2029
+ const choice = await store2.requestUserInput({
2030
+ prompt: "Select an application",
2031
+ promptType: "multipleChoice",
2032
+ options: apps.map(labelFor),
2033
+ secondary: apps.map(secondaryFor),
2034
+ messages
2035
+ });
2036
+ const chosen = apps.find((app2) => labelFor(app2) === choice);
2037
+ if (!chosen) {
2038
+ throw new Error("Application picker received an unexpected selection");
2039
+ }
2040
+ try {
2041
+ return await selectAndReport(chosen);
2042
+ } catch (err) {
2043
+ logger.warn(
2044
+ { app: chosen.id, err: err.message },
2045
+ "application select failed; re-prompting"
2046
+ );
2047
+ messages.push(
2048
+ `Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
2049
+ );
2050
+ }
2051
+ }
2052
+ }
2053
+ async function ensureApplication() {
2054
+ return await currentApplication() ?? await promptForApplication();
1830
2055
  }
1831
2056
 
1832
2057
  // src/workflows/default.ts
1833
- import { z as z25 } from "zod";
2058
+ import { z as z27 } from "zod";
1834
2059
 
1835
2060
  // src/actions/listIndices.ts
1836
- import { z as z3 } from "zod";
1837
- var indicesListSchema = z3.object({
1838
- items: z3.array(
1839
- z3.object({
1840
- name: z3.string(),
1841
- entries: z3.number().default(0)
2061
+ import { z as z5 } from "zod";
2062
+ var indicesListSchema = z5.object({
2063
+ items: z5.array(
2064
+ z5.object({
2065
+ name: z5.string(),
2066
+ entries: z5.number().default(0)
1842
2067
  })
1843
2068
  )
1844
2069
  });
@@ -1909,12 +2134,12 @@ import "zod";
1909
2134
 
1910
2135
  // src/lib/tools/listFiles.ts
1911
2136
  import { tool } from "ai";
1912
- import z4 from "zod";
2137
+ import z6 from "zod";
1913
2138
  import { readdir } from "node:fs/promises";
1914
2139
 
1915
2140
  // src/lib/tools/path.ts
1916
2141
  import { lstat } from "node:fs/promises";
1917
- import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join7, sep } from "node:path";
2142
+ import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join6, sep } from "node:path";
1918
2143
  function resolveInRoot(ctx, path) {
1919
2144
  const target = resolve2(ctx.cwd, path);
1920
2145
  const rel = relative(ctx.root, target);
@@ -1930,7 +2155,7 @@ async function hasSymlinkParent(ctx, target) {
1930
2155
  let current = ctx.root;
1931
2156
  const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
1932
2157
  for (const part of parts) {
1933
- current = join7(current, part);
2158
+ current = join6(current, part);
1934
2159
  try {
1935
2160
  if ((await lstat(current)).isSymbolicLink()) return true;
1936
2161
  } catch (err) {
@@ -1945,7 +2170,7 @@ async function hasSymlinkParent(ctx, target) {
1945
2170
  function listFilesTool(ctx) {
1946
2171
  return tool({
1947
2172
  description: "List files in the current working directory",
1948
- inputSchema: z4.object(),
2173
+ inputSchema: z6.object(),
1949
2174
  execute: async () => {
1950
2175
  logger.info("called listFiles tool");
1951
2176
  if (++ctx.counts.list > ctx.limits.list) {
@@ -1961,13 +2186,13 @@ function listFilesTool(ctx) {
1961
2186
 
1962
2187
  // src/lib/tools/changeDirectory.ts
1963
2188
  import { tool as tool2 } from "ai";
1964
- import z5 from "zod";
2189
+ import z7 from "zod";
1965
2190
  import { stat } from "node:fs/promises";
1966
2191
  function changeDirectoryTool(ctx) {
1967
2192
  return tool2({
1968
2193
  description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
1969
- inputSchema: z5.object({
1970
- path: z5.string().describe("Directory to change into")
2194
+ inputSchema: z7.object({
2195
+ path: z7.string().describe("Directory to change into")
1971
2196
  }),
1972
2197
  execute: async ({ path }) => {
1973
2198
  logger.info({ path }, "called changeDirectory tool");
@@ -1989,13 +2214,13 @@ function changeDirectoryTool(ctx) {
1989
2214
 
1990
2215
  // src/lib/tools/reportStatus.ts
1991
2216
  import { tool as tool3 } from "ai";
1992
- import z6 from "zod";
2217
+ import z8 from "zod";
1993
2218
  function reportStatusTool(output) {
1994
2219
  return tool3({
1995
2220
  description: "Report the status of your execution. Return a reason in case of failure.",
1996
- inputSchema: z6.object({
1997
- status: z6.enum(["success", "fail"]),
1998
- reason: z6.string().optional(),
2221
+ inputSchema: z8.object({
2222
+ status: z8.enum(["success", "fail"]),
2223
+ reason: z8.string().optional(),
1999
2224
  output
2000
2225
  }),
2001
2226
  execute: async ({ status, reason, output: output2 }) => {
@@ -2007,8 +2232,8 @@ function reportStatusTool(output) {
2007
2232
 
2008
2233
  // src/lib/tools/readFile.ts
2009
2234
  import { tool as tool4 } from "ai";
2010
- import z7 from "zod";
2011
- import { readFile as readFile4 } from "node:fs/promises";
2235
+ import z9 from "zod";
2236
+ import { readFile as readFile3 } from "node:fs/promises";
2012
2237
 
2013
2238
  // src/lib/tools/env.ts
2014
2239
  import { basename } from "node:path";
@@ -2035,8 +2260,8 @@ function redactEnvValues(content) {
2035
2260
  function readFileTool(ctx) {
2036
2261
  return tool4({
2037
2262
  description: "Read the contents of a file at the given path",
2038
- inputSchema: z7.object({
2039
- filePath: z7.string().describe("Path to the file to read")
2263
+ inputSchema: z9.object({
2264
+ filePath: z9.string().describe("Path to the file to read")
2040
2265
  }),
2041
2266
  execute: async ({ filePath }) => {
2042
2267
  if (++ctx.counts.read > ctx.limits.read) {
@@ -2046,7 +2271,7 @@ function readFileTool(ctx) {
2046
2271
  const resolved = resolveInRoot(ctx, filePath);
2047
2272
  if (!resolved.ok) return resolved.error;
2048
2273
  try {
2049
- const content = await readFile4(resolved.target, "utf8");
2274
+ const content = await readFile3(resolved.target, "utf8");
2050
2275
  return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
2051
2276
  } catch (err) {
2052
2277
  return `Error reading ${filePath}: ${err.message}`;
@@ -2057,15 +2282,15 @@ function readFileTool(ctx) {
2057
2282
 
2058
2283
  // src/lib/tools/writeFile.ts
2059
2284
  import { tool as tool5 } from "ai";
2060
- import z8 from "zod";
2285
+ import z10 from "zod";
2061
2286
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2062
2287
  import { dirname as dirname4 } from "node:path";
2063
2288
  function writeFileTool(ctx) {
2064
2289
  return tool5({
2065
2290
  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.",
2066
- inputSchema: z8.object({
2067
- filePath: z8.string().describe("Path to the file to write"),
2068
- content: z8.string().describe("Content to write to the file")
2291
+ inputSchema: z10.object({
2292
+ filePath: z10.string().describe("Path to the file to write"),
2293
+ content: z10.string().describe("Content to write to the file")
2069
2294
  }),
2070
2295
  execute: async ({ filePath, content }) => {
2071
2296
  logger.info({ filePath }, "called writeFile tool");
@@ -2090,9 +2315,95 @@ function writeFileTool(ctx) {
2090
2315
 
2091
2316
  // src/lib/tools/writeAlgoliaCredentials.ts
2092
2317
  import { tool as tool6 } from "ai";
2093
- import z9 from "zod";
2094
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
2318
+ import z12 from "zod";
2319
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
2095
2320
  import { dirname as dirname5 } from "node:path";
2321
+
2322
+ // src/lib/algoliaApiKey.ts
2323
+ import { z as z11 } from "zod";
2324
+ var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
2325
+ var WRITE_ACLS = [
2326
+ "addObject",
2327
+ "deleteObject",
2328
+ "settings",
2329
+ "editSettings",
2330
+ "listIndexes"
2331
+ ];
2332
+ var WRITE_ACL_SET = new Set(WRITE_ACLS);
2333
+ var apiKeySchema = z11.object({
2334
+ value: z11.string().min(1),
2335
+ acl: z11.array(z11.string()).default([]),
2336
+ indexes: z11.array(z11.string()).default([])
2337
+ });
2338
+ var apiKeyListSchema = z11.object({
2339
+ items: z11.array(apiKeySchema).optional(),
2340
+ keys: z11.array(apiKeySchema).optional()
2341
+ }).transform((o) => o.items ?? o.keys ?? []);
2342
+ var createdKeySchema = z11.object({
2343
+ key: z11.string().min(1).optional(),
2344
+ value: z11.string().min(1).optional()
2345
+ });
2346
+ function canReuse(key, index) {
2347
+ return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
2348
+ }
2349
+ async function createSearchKey(index) {
2350
+ const stdout = await runAlgoliaCli([
2351
+ "apikeys",
2352
+ "create",
2353
+ "--indices",
2354
+ index,
2355
+ "--acl",
2356
+ "search,browse",
2357
+ "--description",
2358
+ `wizard search-only key for ${index}`,
2359
+ "-o",
2360
+ "json"
2361
+ ]);
2362
+ const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
2363
+ const created = key ?? value;
2364
+ if (!created) throw new Error("apikeys create returned no key value");
2365
+ return created;
2366
+ }
2367
+ function canReuseForWrites(key, index) {
2368
+ return WRITE_ACLS.every((acl) => key.acl.includes(acl)) && key.acl.every((acl) => WRITE_ACL_SET.has(acl)) && key.indexes.includes(index);
2369
+ }
2370
+ async function resolveWriteKey(index) {
2371
+ const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2372
+ const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key2) => canReuseForWrites(key2, index))?.value;
2373
+ if (existing) {
2374
+ logger.info({ index }, "reusing existing write API key");
2375
+ return existing;
2376
+ }
2377
+ logger.info({ index }, "no reusable write key found; creating one");
2378
+ const created = await runAlgoliaCli([
2379
+ "apikeys",
2380
+ "create",
2381
+ "--indices",
2382
+ index,
2383
+ "--acl",
2384
+ WRITE_ACLS.join(","),
2385
+ "--description",
2386
+ `wizard write key for ${index}`,
2387
+ "-o",
2388
+ "json"
2389
+ ]);
2390
+ const { key, value } = createdKeySchema.parse(JSON.parse(created));
2391
+ const writeKey = key ?? value;
2392
+ if (!writeKey) throw new Error("apikeys create returned no key value");
2393
+ return writeKey;
2394
+ }
2395
+ async function resolveSearchOnlyKey(index) {
2396
+ const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2397
+ const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
2398
+ if (existing) {
2399
+ logger.info({ index }, "reusing existing search-only API key");
2400
+ return existing;
2401
+ }
2402
+ logger.info({ index }, "no reusable search-only key found; creating one");
2403
+ return createSearchKey(index);
2404
+ }
2405
+
2406
+ // src/lib/tools/writeAlgoliaCredentials.ts
2096
2407
  var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
2097
2408
  var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
2098
2409
  function appendEnv(content, entries) {
@@ -2106,9 +2417,9 @@ function hasEnv(content, name) {
2106
2417
  }
2107
2418
  function writeCredentialsTool(ctx) {
2108
2419
  return tool6({
2109
- 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.`,
2110
- inputSchema: z9.object({
2111
- filePath: z9.string().describe(
2420
+ 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.`,
2421
+ inputSchema: z12.object({
2422
+ filePath: z12.string().describe(
2112
2423
  'Path to the env file to write credentials into (e.g. ".env")'
2113
2424
  )
2114
2425
  }),
@@ -2116,11 +2427,17 @@ function writeCredentialsTool(ctx) {
2116
2427
  logger.info({ filePath }, "called writeCredentials tool");
2117
2428
  const resolved = resolveInRoot(ctx, filePath);
2118
2429
  if (resolved.ok === false) return resolved.error;
2119
- let profile2;
2430
+ const targetIndex = useWizard.getState().targetIndex;
2431
+ if (!targetIndex) {
2432
+ return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
2433
+ }
2434
+ let appId;
2435
+ let writeKey;
2120
2436
  try {
2121
- profile2 = await loadActiveProfile();
2122
- } catch {
2123
- return "Error: no Algolia profile is configured, so credentials cannot be written. Ask the user to authenticate with the Algolia CLI first.";
2437
+ appId = (await requireApplication()).id;
2438
+ writeKey = await resolveWriteKey(targetIndex);
2439
+ } catch (err) {
2440
+ return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
2124
2441
  }
2125
2442
  try {
2126
2443
  if (await hasSymlinkParent(ctx, resolved.target)) {
@@ -2128,7 +2445,7 @@ function writeCredentialsTool(ctx) {
2128
2445
  }
2129
2446
  let existing = "";
2130
2447
  try {
2131
- existing = await readFile5(resolved.target, "utf8");
2448
+ existing = await readFile4(resolved.target, "utf8");
2132
2449
  } catch (err) {
2133
2450
  if (err.code !== "ENOENT") throw err;
2134
2451
  }
@@ -2139,8 +2456,8 @@ function writeCredentialsTool(ctx) {
2139
2456
  return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
2140
2457
  }
2141
2458
  const envWithCredentials = appendEnv(existing, [
2142
- [APP_ID_VAR, profile2.appId],
2143
- [API_KEY_VAR, profile2.apiKey]
2459
+ [APP_ID_VAR, appId],
2460
+ [API_KEY_VAR, writeKey]
2144
2461
  ]);
2145
2462
  await mkdir4(dirname5(resolved.target), { recursive: true });
2146
2463
  await writeFile4(resolved.target, envWithCredentials, "utf8");
@@ -2154,16 +2471,16 @@ function writeCredentialsTool(ctx) {
2154
2471
 
2155
2472
  // src/lib/tools/searchFiles.ts
2156
2473
  import { tool as tool7 } from "ai";
2157
- import z10 from "zod";
2158
- import { readdir as readdir2, readFile as readFile6 } from "node:fs/promises";
2159
- import { join as join8 } from "node:path";
2474
+ import z13 from "zod";
2475
+ import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
2476
+ import { join as join7 } from "node:path";
2160
2477
  var MAX_QUERY_LENGTH = 1e3;
2161
2478
  async function walkFiles(dir) {
2162
2479
  const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
2163
2480
  const out = [];
2164
2481
  for (const e of await readdir2(dir, { withFileTypes: true })) {
2165
2482
  if (e.name.startsWith(".") || skip.has(e.name)) continue;
2166
- const full = join8(dir, e.name);
2483
+ const full = join7(dir, e.name);
2167
2484
  if (e.isDirectory()) out.push(...await walkFiles(full));
2168
2485
  else if (e.isFile()) out.push(full);
2169
2486
  }
@@ -2172,9 +2489,9 @@ async function walkFiles(dir) {
2172
2489
  function searchFilesTool(ctx) {
2173
2490
  return tool7({
2174
2491
  description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
2175
- inputSchema: z10.object({
2176
- query: z10.string().describe("JavaScript RegExp pattern to search for"),
2177
- path: z10.string().optional().describe("Directory to search in (default: cwd)")
2492
+ inputSchema: z13.object({
2493
+ query: z13.string().describe("JavaScript RegExp pattern to search for"),
2494
+ path: z13.string().optional().describe("Directory to search in (default: cwd)")
2178
2495
  }),
2179
2496
  execute: async ({ query, path = "." }) => {
2180
2497
  logger.info({ query, path }, "called searchFiles tool");
@@ -2196,7 +2513,7 @@ function searchFilesTool(ctx) {
2196
2513
  for (const file of await walkFiles(resolved.target)) {
2197
2514
  let content;
2198
2515
  try {
2199
- content = await readFile6(file, "utf8");
2516
+ content = await readFile5(file, "utf8");
2200
2517
  } catch {
2201
2518
  continue;
2202
2519
  }
@@ -2218,7 +2535,7 @@ function searchFilesTool(ctx) {
2218
2535
 
2219
2536
  // src/lib/tools/verifyImplementation.ts
2220
2537
  import { tool as tool8 } from "ai";
2221
- import z11 from "zod";
2538
+ import z14 from "zod";
2222
2539
 
2223
2540
  // src/lib/tools/utils/runCommand.ts
2224
2541
  import { spawn as spawn2 } from "node:child_process";
@@ -2240,9 +2557,9 @@ function runCommand(command, args, cwd) {
2240
2557
  }
2241
2558
 
2242
2559
  // src/lib/tools/utils/packageManager.ts
2243
- import { readFile as readFile7 } from "node:fs/promises";
2560
+ import { readFile as readFile6 } from "node:fs/promises";
2244
2561
  import { existsSync } from "node:fs";
2245
- import { join as join9 } from "node:path";
2562
+ import { join as join8 } from "node:path";
2246
2563
  var LOCKFILES = [
2247
2564
  ["pnpm-lock.yaml", "pnpm"],
2248
2565
  ["yarn.lock", "yarn"],
@@ -2251,13 +2568,13 @@ var LOCKFILES = [
2251
2568
  ["package-lock.json", "npm"]
2252
2569
  ];
2253
2570
  async function readPackageJson(cwd = process.cwd()) {
2254
- return JSON.parse(await readFile7(join9(cwd, "package.json"), "utf8"));
2571
+ return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
2255
2572
  }
2256
2573
  function packageManagerFrom(pkg) {
2257
2574
  return pkg.packageManager?.split("@")[0] ?? "npm";
2258
2575
  }
2259
2576
  function packageManagerFromLockfile(cwd) {
2260
- return LOCKFILES.find(([file]) => existsSync(join9(cwd, file)))?.[1];
2577
+ return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
2261
2578
  }
2262
2579
  async function detectPackageManager(cwd) {
2263
2580
  try {
@@ -2298,7 +2615,7 @@ async function runRepoVerificationCheck() {
2298
2615
  function verifyImplementationTool() {
2299
2616
  return tool8({
2300
2617
  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.",
2301
- inputSchema: z11.object(),
2618
+ inputSchema: z14.object(),
2302
2619
  execute: async () => {
2303
2620
  logger.info("called verifyImplementation tool");
2304
2621
  return runRepoVerificationCheck();
@@ -2312,7 +2629,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
2312
2629
  import { nanoid as nanoid2 } from "nanoid";
2313
2630
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
2314
2631
  import { dirname as dirname6 } from "node:path";
2315
- import z12 from "zod";
2632
+ import z15 from "zod";
2316
2633
  var DATA_DIR = ".algolia-wizard/data";
2317
2634
  var RECORD_MODEL = "claude-haiku-4-5";
2318
2635
  var MAX_RECORDS = 100;
@@ -2324,17 +2641,17 @@ var anthropic = createAnthropic({
2324
2641
  function generateRecordTool(ctx) {
2325
2642
  return tool9({
2326
2643
  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.",
2327
- inputSchema: z12.object({
2328
- entityName: z12.string().describe("Name of the entity to generate records for."),
2329
- attributes: z12.array(z12.string()).describe("Attribute names each record must contain."),
2330
- count: z12.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2331
- hint: z12.string().optional().describe("Optional context to steer realistic values.")
2644
+ inputSchema: z15.object({
2645
+ entityName: z15.string().describe("Name of the entity to generate records for."),
2646
+ attributes: z15.array(z15.string()).describe("Attribute names each record must contain."),
2647
+ count: z15.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2648
+ hint: z15.string().optional().describe("Optional context to steer realistic values.")
2332
2649
  }),
2333
2650
  execute: async ({ entityName, attributes, count, hint }) => {
2334
2651
  logger.info({ entityName, count }, "called generateRecord tool");
2335
2652
  try {
2336
- const value = z12.union([z12.string(), z12.number(), z12.boolean(), z12.null()]);
2337
- const recordSchema = z12.object(
2653
+ const value = z15.union([z15.string(), z15.number(), z15.boolean(), z15.null()]);
2654
+ const recordSchema = z15.object(
2338
2655
  Object.fromEntries(attributes.map((attr) => [attr, value]))
2339
2656
  );
2340
2657
  const generateBatch = async (batchCount) => {
@@ -2344,8 +2661,8 @@ function generateRecordTool(ctx) {
2344
2661
  const { output } = await generateText({
2345
2662
  model: anthropic(RECORD_MODEL),
2346
2663
  output: Output.object({
2347
- schema: z12.object({
2348
- records: z12.array(recordSchema).length(batchCount)
2664
+ schema: z15.object({
2665
+ records: z15.array(recordSchema).length(batchCount)
2349
2666
  })
2350
2667
  }),
2351
2668
  prompt: [
@@ -2403,12 +2720,12 @@ function generateRecordTool(ctx) {
2403
2720
 
2404
2721
  // src/lib/tools/notifyUser.ts
2405
2722
  import { tool as tool10 } from "ai";
2406
- import z13 from "zod";
2723
+ import z16 from "zod";
2407
2724
  function notifyUserTool() {
2408
2725
  return tool10({
2409
2726
  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.`,
2410
- inputSchema: z13.object({
2411
- message: z13.string().describe(
2727
+ inputSchema: z16.object({
2728
+ message: z16.string().describe(
2412
2729
  "Short, plain-language description of what you are doing now."
2413
2730
  )
2414
2731
  }),
@@ -2588,10 +2905,10 @@ async function runAgent(req) {
2588
2905
  }
2589
2906
 
2590
2907
  // src/actions/detectLanguage.ts
2591
- import z16 from "zod";
2592
- var detectLanguageSchema = z16.object({
2593
- languages: z16.array(z16.object({ name: z16.string(), version: z16.string() })),
2594
- frameworks: z16.array(z16.object({ name: z16.string(), version: z16.string() }))
2908
+ import z19 from "zod";
2909
+ var detectLanguageSchema = z19.object({
2910
+ languages: z19.array(z19.object({ name: z19.string(), version: z19.string() })),
2911
+ frameworks: z19.array(z19.object({ name: z19.string(), version: z19.string() }))
2595
2912
  });
2596
2913
  var detectLanguage = () => runAgent({
2597
2914
  instructions: [
@@ -2609,31 +2926,31 @@ var detectLanguage = () => runAgent({
2609
2926
  });
2610
2927
 
2611
2928
  // src/actions/analyzeCodebase.ts
2612
- import z17 from "zod";
2929
+ import z20 from "zod";
2613
2930
  var READONLY_TOOLS = [
2614
2931
  "listFiles",
2615
2932
  "changeDirectory",
2616
2933
  "readFile",
2617
2934
  "searchFiles"
2618
2935
  ];
2619
- var ingestionAnalysisSchema = z17.object({
2620
- ingestionAnalysis: z17.array(
2621
- z17.object({
2622
- name: z17.string(),
2623
- paths: z17.array(z17.string()),
2936
+ var ingestionAnalysisSchema = z20.object({
2937
+ ingestionAnalysis: z20.array(
2938
+ z20.object({
2939
+ name: z20.string(),
2940
+ paths: z20.array(z20.string()),
2624
2941
  // indexable fields the agent found for this entity
2625
- attributes: z17.array(z17.string())
2942
+ attributes: z20.array(z20.string())
2626
2943
  })
2627
2944
  )
2628
2945
  });
2629
- var searchImplementationAnalysisSchema = z17.object({
2630
- searchImplementationAnalysis: z17.string()
2946
+ var searchImplementationAnalysisSchema = z20.object({
2947
+ searchImplementationAnalysis: z20.string()
2631
2948
  });
2632
- var verificationSchema = z17.object({
2633
- verification: z17.array(z17.string())
2949
+ var verificationSchema = z20.object({
2950
+ verification: z20.array(z20.string())
2634
2951
  });
2635
2952
  var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
2636
- var analyzeCodebaseSchema = z17.object({
2953
+ var analyzeCodebaseSchema = z20.object({
2637
2954
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
2638
2955
  searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
2639
2956
  verification: verificationSchema.shape.verification.optional(),
@@ -2695,7 +3012,7 @@ async function runAnalysis(mode, extraInstructions = []) {
2695
3012
  // package.json
2696
3013
  var package_default = {
2697
3014
  name: "@algolia/wizard",
2698
- version: "0.6.0",
3015
+ version: "0.7.0-rc.53.39",
2699
3016
  description: "Magically implement Algolia functionality in your codebase",
2700
3017
  type: "module",
2701
3018
  engines: {
@@ -2743,7 +3060,6 @@ var package_default = {
2743
3060
  dependencies: {
2744
3061
  "@ai-sdk/anthropic": "^3.0.81",
2745
3062
  "@ai-sdk/openai-compatible": "^2.0.47",
2746
- "@algolia/cli": "^5.11.0",
2747
3063
  "@hono/node-server": "^2.0.10",
2748
3064
  "@mishieck/ink-titled-box": "^0.4.2",
2749
3065
  "@segment/analytics-node": "^3.1.0",
@@ -2758,7 +3074,6 @@ var package_default = {
2758
3074
  nanoid: "^5.1.15",
2759
3075
  pino: "^10.3.1",
2760
3076
  react: "^19.2.7",
2761
- toml: "^4.1.1",
2762
3077
  varlock: "^1.5.1",
2763
3078
  zod: "^4.4.3",
2764
3079
  zustand: "^5.0.14"
@@ -2816,8 +3131,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
2816
3131
  }
2817
3132
 
2818
3133
  // src/actions/confirmLanguage.ts
2819
- import z19 from "zod";
2820
- var confirmLanguageSchema = z19.object({
3134
+ import z22 from "zod";
3135
+ var confirmLanguageSchema = z22.object({
2821
3136
  languages: detectLanguageSchema.shape.languages
2822
3137
  });
2823
3138
  async function confirmLanguage(ctx) {
@@ -2838,8 +3153,8 @@ async function confirmLanguage(ctx) {
2838
3153
  }
2839
3154
 
2840
3155
  // src/actions/confirmFramework.ts
2841
- import z20 from "zod";
2842
- var confirmFrameworkSchema = z20.object({
3156
+ import z23 from "zod";
3157
+ var confirmFrameworkSchema = z23.object({
2843
3158
  frameworks: detectLanguageSchema.shape.frameworks
2844
3159
  });
2845
3160
  var CURATED_FRAMEWORKS = [
@@ -2967,8 +3282,8 @@ async function promptUser(ctx, params) {
2967
3282
  }
2968
3283
 
2969
3284
  // src/actions/confirmEntities.ts
2970
- import z21 from "zod";
2971
- var confirmEntitiesSchema = z21.object({
3285
+ import z24 from "zod";
3286
+ var confirmEntitiesSchema = z24.object({
2972
3287
  // Final detection — the focused re-run may supersede project-scan's.
2973
3288
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
2974
3289
  confirmedEntities: confirmedEntitiesFieldSchema
@@ -3038,15 +3353,15 @@ async function confirmEntities(ctx) {
3038
3353
  }
3039
3354
 
3040
3355
  // src/actions/review.ts
3041
- import { z as z22 } from "zod";
3042
- var reviewSchema = z22.object({
3356
+ import { z as z25 } from "zod";
3357
+ var reviewSchema = z25.object({
3043
3358
  // Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
3044
3359
  // not one entry per workflow step — a step's raw output can be a long,
3045
3360
  // multi-paragraph blob (see implement.ts's summaries.join), and mirroring
3046
3361
  // that 1:1 is what made the old per-step summary an unreadable wall of text.
3047
- summaryPoints: z22.array(z22.string()),
3048
- reviewPrompt: z22.string(),
3049
- nextSteps: z22.array(z22.string())
3362
+ summaryPoints: z25.array(z25.string()),
3363
+ reviewPrompt: z25.string(),
3364
+ nextSteps: z25.array(z25.string())
3050
3365
  });
3051
3366
  function formatCompletedSteps(steps) {
3052
3367
  if (!steps.length) return "(no prior steps completed)";
@@ -3097,16 +3412,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
3097
3412
  };
3098
3413
 
3099
3414
  // src/actions/implement.ts
3100
- import z24 from "zod";
3415
+ import z26 from "zod";
3101
3416
 
3102
3417
  // src/lib/worktree.ts
3103
3418
  import { execFile, spawn as spawn3 } from "node:child_process";
3104
- import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3419
+ import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3105
3420
  import {
3106
3421
  basename as basename2,
3107
3422
  dirname as dirname7,
3108
3423
  isAbsolute as isAbsolute2,
3109
- join as join10,
3424
+ join as join9,
3110
3425
  relative as relative2,
3111
3426
  resolve as resolve3
3112
3427
  } from "node:path";
@@ -3140,7 +3455,7 @@ async function isWorkingTreeDirty(repoRoot) {
3140
3455
  return out.trim().length > 0;
3141
3456
  }
3142
3457
  async function pruneOldWorktrees(repoRoot) {
3143
- const dir = join10(stateDir(repoRoot), "worktrees");
3458
+ const dir = join9(stateDir(repoRoot), "worktrees");
3144
3459
  const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
3145
3460
  for (const slug of stale) {
3146
3461
  const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
@@ -3151,7 +3466,7 @@ async function pruneOldWorktrees(repoRoot) {
3151
3466
  "worktree",
3152
3467
  "remove",
3153
3468
  "--force",
3154
- join10(dir, slug)
3469
+ join9(dir, slug)
3155
3470
  ]);
3156
3471
  await git(["-C", repoRoot, "branch", "-D", branch]);
3157
3472
  } catch (err) {
@@ -3165,7 +3480,7 @@ async function pruneOldWorktrees(repoRoot) {
3165
3480
  async function createWorktree(repoRoot) {
3166
3481
  const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
3167
3482
  const dirSlug = branch.replace(/\//g, "-");
3168
- const path = join10(stateDir(repoRoot), "worktrees", dirSlug);
3483
+ const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
3169
3484
  await git(["-C", repoRoot, "worktree", "prune"]);
3170
3485
  await pruneOldWorktrees(repoRoot);
3171
3486
  await mkdir6(dirname7(path), { recursive: true });
@@ -3285,8 +3600,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
3285
3600
  } catch {
3286
3601
  return { ok: false, reason: `"${sourcePath}" does not exist` };
3287
3602
  }
3288
- const relPath = join10(ingestDir, basename2(source));
3289
- const dest = join10(worktreePath, relPath);
3603
+ const relPath = join9(ingestDir, basename2(source));
3604
+ const dest = join9(worktreePath, relPath);
3290
3605
  try {
3291
3606
  await mkdir6(dirname7(dest), { recursive: true });
3292
3607
  await copyFile(source, dest);
@@ -3302,10 +3617,10 @@ function hasEnvVar(content, name) {
3302
3617
  return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
3303
3618
  }
3304
3619
  async function writeSearchEnvValues(worktreePath, vars) {
3305
- const target = join10(worktreePath, ".env");
3620
+ const target = join9(worktreePath, ".env");
3306
3621
  let existing = "";
3307
3622
  try {
3308
- existing = await readFile8(target, "utf8");
3623
+ existing = await readFile7(target, "utf8");
3309
3624
  } catch (err) {
3310
3625
  if (err.code !== "ENOENT") throw err;
3311
3626
  }
@@ -3373,63 +3688,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
3373
3688
  }
3374
3689
  }
3375
3690
 
3376
- // src/lib/algoliaApiKey.ts
3377
- import { z as z23 } from "zod";
3378
- var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
3379
- var apiKeySchema = z23.object({
3380
- value: z23.string().min(1),
3381
- acl: z23.array(z23.string()).default([]),
3382
- indexes: z23.array(z23.string()).default([])
3383
- });
3384
- var apiKeyListSchema = z23.object({
3385
- items: z23.array(apiKeySchema).optional(),
3386
- keys: z23.array(apiKeySchema).optional()
3387
- }).transform((o) => o.items ?? o.keys ?? []);
3388
- var createdKeySchema = z23.object({
3389
- key: z23.string().min(1).optional(),
3390
- value: z23.string().min(1).optional()
3391
- });
3392
- function canReuse(key, index) {
3393
- return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
3394
- }
3395
- async function createSearchKey(index) {
3396
- const stdout = await runAlgoliaCli([
3397
- "apikeys",
3398
- "create",
3399
- "--indices",
3400
- index,
3401
- "--acl",
3402
- "search,browse",
3403
- "--description",
3404
- `wizard search-only key for ${index}`,
3405
- "-o",
3406
- "json"
3407
- ]);
3408
- const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
3409
- const created = key ?? value;
3410
- if (!created) throw new Error("apikeys create returned no key value");
3411
- return created;
3412
- }
3413
- async function resolveSearchOnlyKey(index) {
3414
- const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
3415
- const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
3416
- if (existing) {
3417
- logger.info({ index }, "reusing existing search-only API key");
3418
- return existing;
3419
- }
3420
- logger.info({ index }, "no reusable search-only key found; creating one");
3421
- return createSearchKey(index);
3422
- }
3423
-
3424
3691
  // src/lib/algoliaDocs.ts
3425
3692
  import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
3426
- import { dirname as dirname8, join as join11 } from "node:path";
3693
+ import { dirname as dirname8, join as join10 } from "node:path";
3427
3694
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3428
- var DOCS_SUBPATH = join11("docs", "algolia-sdk");
3695
+ var DOCS_SUBPATH = join10("docs", "algolia-sdk");
3429
3696
  function findDocsDir() {
3430
3697
  let dir = dirname8(fileURLToPath2(import.meta.url));
3431
3698
  for (; ; ) {
3432
- const candidate = join11(dir, DOCS_SUBPATH);
3699
+ const candidate = join10(dir, DOCS_SUBPATH);
3433
3700
  if (existsSync2(candidate)) return candidate;
3434
3701
  const parent = dirname8(dir);
3435
3702
  if (parent === dir) return void 0;
@@ -3452,7 +3719,7 @@ function loadAlgoliaDoc(language) {
3452
3719
  );
3453
3720
  return "";
3454
3721
  }
3455
- return readFileSync(join11(docsDir, files[0]), "utf8").trim();
3722
+ return readFileSync(join10(docsDir, files[0]), "utf8").trim();
3456
3723
  }
3457
3724
  function getNamedDoc(name, language) {
3458
3725
  const docsDir = findDocsDir();
@@ -3460,7 +3727,7 @@ function getNamedDoc(name, language) {
3460
3727
  logger.warn("docs/algolia-sdk not found");
3461
3728
  return "";
3462
3729
  }
3463
- const file = join11(docsDir, `${name}-${language}.md`);
3730
+ const file = join10(docsDir, `${name}-${language}.md`);
3464
3731
  if (!existsSync2(file)) {
3465
3732
  logger.warn({ name, language }, "named SDK reference not found");
3466
3733
  return "";
@@ -3487,50 +3754,50 @@ function shellQuote(value) {
3487
3754
  }
3488
3755
 
3489
3756
  // src/actions/implement.ts
3490
- var implementSchema = z24.object({
3491
- filesChanged: z24.array(z24.string()),
3492
- summary: z24.string(),
3757
+ var implementSchema = z26.object({
3758
+ filesChanged: z26.array(z26.string()),
3759
+ summary: z26.string(),
3493
3760
  // Absolute path to the throwaway worktree holding the generated changes, so
3494
3761
  // the user can open it (`cd <worktreePath>`) or inspect the diff
3495
3762
  // (`git -C <worktreePath> status/diff`).
3496
- worktreePath: z24.string().optional(),
3497
- ingestCommand: z24.string().optional(),
3763
+ worktreePath: z26.string().optional(),
3764
+ ingestCommand: z26.string().optional(),
3498
3765
  // True when the user accepted the run-now prompt and the wizard executed the
3499
3766
  // ingestion script; downstream steps use this to avoid telling the user to run
3500
3767
  // a script that already ran.
3501
- ingestScriptRan: z24.boolean().optional(),
3768
+ ingestScriptRan: z26.boolean().optional(),
3502
3769
  // Records ingested by the run-now execution, parsed from the script's
3503
3770
  // machine-readable count line; absent when the script didn't run or emitted
3504
3771
  // no parseable count.
3505
- ingestRecordCount: z24.number().optional(),
3772
+ ingestRecordCount: z26.number().optional(),
3506
3773
  // Wall-clock duration of the run-now ingestion execution, in ms.
3507
- ingestDurationMs: z24.number().optional(),
3508
- ingestionSource: z24.enum(["local", "fileUpload", "generated"]),
3774
+ ingestDurationMs: z26.number().optional(),
3775
+ ingestionSource: z26.enum(["local", "fileUpload", "generated"]),
3509
3776
  // Suggested names/values, built from framework detection. The search agent is
3510
3777
  // instructed to rename the prefix if it doesn't match the project's build
3511
3778
  // tool, so the names it actually wrote can differ — treat these as hints, not
3512
3779
  // ground truth (the agent's summary carries the final names).
3513
- searchEnvVars: z24.array(
3514
- z24.object({
3515
- name: z24.string(),
3516
- value: z24.string()
3780
+ searchEnvVars: z26.array(
3781
+ z26.object({
3782
+ name: z26.string(),
3783
+ value: z26.string()
3517
3784
  })
3518
3785
  ).optional()
3519
3786
  });
3520
- var implementationOutputSchema = z24.object({
3521
- summary: z24.string(),
3787
+ var implementationOutputSchema = z26.object({
3788
+ summary: z26.string(),
3522
3789
  // Ingestion only: how to run the generated script, as a structured pair the
3523
3790
  // wizard turns into an argv (`<runtime> <entrypoint>`) — never a free-form
3524
3791
  // command string. `runtime` is constrained to an allowlisted interpreter and
3525
3792
  // `entrypoint` is validated to a worktree-relative path before execution, so
3526
3793
  // the agent cannot inject extra commands or swap the interpreter.
3527
- runtime: z24.enum(INGEST_RUNTIMES).optional(),
3528
- entrypoint: z24.string().optional()
3794
+ runtime: z26.enum(INGEST_RUNTIMES).optional(),
3795
+ entrypoint: z26.string().optional()
3529
3796
  });
3530
- var verificationOutputSchema = z24.object({
3531
- summary: z24.string(),
3532
- sufficient: z24.boolean(),
3533
- additionalInstructions: z24.string().optional()
3797
+ var verificationOutputSchema = z26.object({
3798
+ summary: z26.string(),
3799
+ sufficient: z26.boolean(),
3800
+ additionalInstructions: z26.string().optional()
3534
3801
  });
3535
3802
  var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
3536
3803
  var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
@@ -3644,7 +3911,7 @@ function searchInstructions(input) {
3644
3911
  doc,
3645
3912
  `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.`,
3646
3913
  "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.",
3647
- // appId always resolves (loadActiveProfile throws otherwise); only the
3914
+ // appId always resolves (requireApplication throws otherwise); only the
3648
3915
  // search-only key is best-effort and can fall back to a placeholder.
3649
3916
  `Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
3650
3917
  // Names are fixed, not the agent's to rename: the wizard writes the
@@ -3793,6 +4060,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3793
4060
  }
3794
4061
  }
3795
4062
  const targetIndex = selected?.selection;
4063
+ useWizard.getState().setTargetIndex(targetIndex ?? null);
3796
4064
  await assertGitRepoWithHead(repoRoot);
3797
4065
  if (await isWorkingTreeDirty(repoRoot)) {
3798
4066
  await confirmDirtyWorkingTree(ctx, repoRoot);
@@ -3803,7 +4071,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3803
4071
  let appId;
3804
4072
  let searchKey;
3805
4073
  if (useCases.includes("search")) {
3806
- appId = (await loadActiveProfile()).appId;
4074
+ appId = (await requireApplication()).id;
3807
4075
  try {
3808
4076
  searchKey = await resolveSearchOnlyKey(targetIndex);
3809
4077
  } catch (err) {
@@ -3914,7 +4182,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3914
4182
  messages: []
3915
4183
  }) === true;
3916
4184
  if (runNow) {
3917
- const profile2 = await loadActiveProfile();
4185
+ const ingestApp = await requireApplication();
4186
+ const writeKey = await resolveWriteKey(targetIndex);
3918
4187
  ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
3919
4188
  const scriptLogId = ctx.logStart("runIngestScript", {
3920
4189
  runtime: ingestRuntime,
@@ -3926,8 +4195,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
3926
4195
  ingestRuntime,
3927
4196
  ingestEntrypoint,
3928
4197
  {
3929
- [APP_ID_VAR]: profile2.appId,
3930
- [API_KEY_VAR]: profile2.apiKey
4198
+ [APP_ID_VAR]: ingestApp.id,
4199
+ [API_KEY_VAR]: writeKey
3931
4200
  }
3932
4201
  );
3933
4202
  ctx.logEnd(scriptLogId, run.ok ? "success" : "error");
@@ -4138,8 +4407,8 @@ var defaultWorkflow = {
4138
4407
  defineStep({
4139
4408
  id: "select-index",
4140
4409
  title: "Set up index",
4141
- outputSchema: z25.object({
4142
- selection: z25.string()
4410
+ outputSchema: z27.object({
4411
+ selection: z27.string()
4143
4412
  }),
4144
4413
  run: (ctx) => selectIndexStep(ctx)
4145
4414
  }),
@@ -4218,27 +4487,34 @@ if (!workflow) {
4218
4487
  }
4219
4488
  var store = useWizard.getState();
4220
4489
  var instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4490
+ await store.waitForStart();
4221
4491
  var user = await getUser();
4222
4492
  if (!user) {
4223
- await instance.waitUntilRenderFlush();
4224
- instance.cleanup();
4493
+ store.beginAuth();
4225
4494
  try {
4226
4495
  await runAuthLogin();
4227
4496
  } catch (err) {
4228
- console.error(err instanceof Error ? err.message : String(err));
4497
+ store.setError(err instanceof Error ? err.message : String(err));
4498
+ await instance.waitUntilExit();
4229
4499
  process.exit(1);
4230
4500
  }
4231
- instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4501
+ store.endAuth();
4232
4502
  user = await getUser();
4233
4503
  if (!user) {
4234
4504
  store.setError(
4235
- "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
4505
+ "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
4236
4506
  );
4237
4507
  await instance.waitUntilExit();
4238
4508
  process.exit(1);
4239
4509
  }
4240
4510
  }
4241
4511
  store.setUser(user);
4242
- var profile = await loadActiveProfile();
4243
- await store.waitForStart();
4244
- runWorkflow(workflow, profile?.appId);
4512
+ var app;
4513
+ try {
4514
+ app = await ensureApplication();
4515
+ } catch (err) {
4516
+ store.setError(err instanceof Error ? err.message : String(err));
4517
+ await instance.waitUntilExit();
4518
+ process.exit(1);
4519
+ }
4520
+ runWorkflow(workflow, app.id);