@algolia/wizard 0.6.0-rc.51.26 → 0.6.0-rc.53.27

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 (2) hide show
  1. package/dist/main.js +767 -495
  2. 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,20 +12,54 @@ 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
+ function npxArgs(args) {
16
+ return ["--yes", "@algolia/cli@latest", ...args];
19
17
  }
20
- function runAlgoliaCli(args) {
18
+ var shell = process.platform === "win32";
19
+ function lineSplitter(emit) {
20
+ let buffer = "";
21
+ return {
22
+ push(chunk) {
23
+ buffer += chunk;
24
+ const lines = buffer.split("\n");
25
+ buffer = lines.pop() ?? "";
26
+ for (const line of lines) emit(line.replace(/\r$/, ""));
27
+ },
28
+ flush() {
29
+ if (buffer) emit(buffer.replace(/\r$/, ""));
30
+ buffer = "";
31
+ }
32
+ };
33
+ }
34
+ var wizardSink = (stream, line) => {
35
+ if (!line.trim()) return;
36
+ useWizard.getState().pushCliOutput(stream, line);
37
+ };
38
+ function runAlgoliaCli(args, { onOutput } = {}) {
39
+ const store2 = useWizard.getState();
40
+ const logId = store2.logStart("tool", `algolia ${args.join(" ")}`);
21
41
  return new Promise((resolve4, reject) => {
22
- const child = spawn(process.execPath, [algoliaCliEntry(), ...args]);
42
+ const child = spawn("npx", npxArgs(args), { shell });
23
43
  let stdout = "";
24
44
  let stderr = "";
25
- child.stdout.on("data", (chunk) => stdout += chunk);
26
- child.stderr.on("data", (chunk) => stderr += chunk);
45
+ const splitters = {
46
+ stdout: lineSplitter((line) => onOutput?.("stdout", line)),
47
+ stderr: lineSplitter((line) => onOutput?.("stderr", line))
48
+ };
49
+ child.stdout.on("data", (chunk) => {
50
+ const text = String(chunk);
51
+ stdout += text;
52
+ splitters.stdout.push(text);
53
+ });
54
+ child.stderr.on("data", (chunk) => {
55
+ const text = String(chunk);
56
+ stderr += text;
57
+ splitters.stderr.push(text);
58
+ });
27
59
  child.on("error", reject);
28
60
  child.on("close", (code) => {
61
+ splitters.stdout.flush();
62
+ splitters.stderr.flush();
29
63
  if (code === 0) {
30
64
  resolve4(stdout);
31
65
  } else {
@@ -37,7 +71,16 @@ function runAlgoliaCli(args) {
37
71
  );
38
72
  }
39
73
  });
40
- });
74
+ }).then(
75
+ (out) => {
76
+ useWizard.getState().logEnd(logId, "success");
77
+ return out;
78
+ },
79
+ (err) => {
80
+ useWizard.getState().logEnd(logId, "error");
81
+ throw err;
82
+ }
83
+ );
41
84
  }
42
85
  async function getUser() {
43
86
  let raw;
@@ -52,13 +95,21 @@ async function getUser() {
52
95
  return null;
53
96
  }
54
97
  }
98
+ function needsInteractiveTerminal(err) {
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ return /non-interactive mode/i.test(message);
101
+ }
55
102
  function runAuthLogin() {
103
+ return runAlgoliaCli(["auth", "login", "--default"], {
104
+ onOutput: wizardSink
105
+ }).then(() => void 0);
106
+ }
107
+ function runAuthLoginInTerminal() {
56
108
  return new Promise((resolve4, reject) => {
57
- const child = spawn(
58
- process.execPath,
59
- [algoliaCliEntry(), "auth", "login", "--default"],
60
- { stdio: "inherit" }
61
- );
109
+ const child = spawn("npx", npxArgs(["auth", "login", "--default"]), {
110
+ shell,
111
+ stdio: "inherit"
112
+ });
62
113
  child.on("error", reject);
63
114
  child.on("close", (code) => {
64
115
  if (code === 0) resolve4();
@@ -171,6 +222,7 @@ function describeInputValue(value) {
171
222
  return Array.isArray(value) ? value.join(", ") : value;
172
223
  }
173
224
  var NOTICE_INTERVAL_MS = 2e3;
225
+ var CLI_OUTPUT_LIMIT = 200;
174
226
  var useWizard = create((set, get) => ({
175
227
  phase: "idle",
176
228
  homeScreen: "home",
@@ -182,10 +234,23 @@ var useWizard = create((set, get) => ({
182
234
  notices: [],
183
235
  _noticeQueue: [],
184
236
  _noticeTimer: null,
237
+ cliOutput: [],
238
+ targetIndex: null,
185
239
  logs: [],
186
240
  error: null,
187
241
  inputReq: null,
188
242
  _resolve: null,
243
+ // Brackets a CLI subprocess that needs the screen. `endAuth` must land back
244
+ // on exactly 'idle': `confirmStart` is a no-op from any other phase and
245
+ // `waitForStart` resolves on any non-idle phase, so ending anywhere else
246
+ // either skips the welcome screen or ignores its spacebar forever.
247
+ beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
248
+ // Re-enters the auth phase *keeping* the CLI output on screen, for returning
249
+ // from a prompt raised mid-sign-in (`submitInput` leaves the phase at
250
+ // 'running'). Clearing here would blank the login output the user was reading
251
+ // the instant they answered.
252
+ resumeAuth: () => set({ phase: "authenticating" }),
253
+ endAuth: () => set((s) => s.phase === "authenticating" ? { phase: "idle" } : {}),
189
254
  // Advances past the welcome screen. Only meaningful from 'idle' — once the
190
255
  // workflow is running there's nothing left to confirm.
191
256
  // Reset `homeScreen` so preflight shows Welcome, not the Learn more sub-view.
@@ -220,7 +285,13 @@ var useWizard = create((set, get) => ({
220
285
  syncSteps: (steps, currentStepIndex) => set({ steps, currentStepIndex }),
221
286
  setActiveStep: (index) => {
222
287
  get()._clearNoticeQueue();
223
- set({ phase: "running", currentStepIndex: index, output: "", notices: [] });
288
+ set({
289
+ phase: "running",
290
+ currentStepIndex: index,
291
+ output: "",
292
+ notices: [],
293
+ cliOutput: []
294
+ });
224
295
  },
225
296
  setUser: (user2) => set({ user: user2 }),
226
297
  appendToken: (text) => set((s) => ({ output: s.output + text })),
@@ -261,6 +332,16 @@ var useWizard = create((set, get) => ({
261
332
  get()._clearNoticeQueue();
262
333
  set({ notices: [] });
263
334
  },
335
+ // Unthrottled, unlike `pushNotice`: these lines arrive at whatever rate the
336
+ // subprocess emits them, and holding them back would land output after the
337
+ // command it belongs to has already exited.
338
+ pushCliOutput: (stream, text) => set((s) => ({
339
+ cliOutput: [...s.cliOutput, { id: nanoid(), stream, text }].slice(
340
+ -CLI_OUTPUT_LIMIT
341
+ )
342
+ })),
343
+ clearCliOutput: () => set({ cliOutput: [] }),
344
+ setTargetIndex: (index) => set({ targetIndex: index }),
264
345
  logStart: (kind, name, input) => {
265
346
  const id = nanoid();
266
347
  set((s) => ({
@@ -305,6 +386,8 @@ var useWizard = create((set, get) => ({
305
386
  currentStepIndex: 0,
306
387
  output: "",
307
388
  notices: [],
389
+ cliOutput: [],
390
+ targetIndex: null,
308
391
  logs: [],
309
392
  error: null,
310
393
  inputReq: null,
@@ -313,16 +396,88 @@ var useWizard = create((set, get) => ({
313
396
  }
314
397
  }));
315
398
 
399
+ // src/ui/CliOutput.tsx
400
+ import { Box, Text, useWindowSize } from "ink";
401
+
402
+ // src/ui/theme.ts
403
+ var MARKER = {
404
+ pending: "\u25CB",
405
+ running: "\u25D0",
406
+ done: "\u2713",
407
+ error: "\u2716"
408
+ };
409
+ var BRAND = "#003DFF";
410
+ var SECONDARY = "#5468FF";
411
+ var DANGER = "#F86E7E";
412
+ var COLORS = {
413
+ brand: BRAND,
414
+ primary: "#E6EDF3",
415
+ secondary: SECONDARY,
416
+ strong: "#FFFFFF",
417
+ muted: "#8B949E",
418
+ dim: "#484F58",
419
+ highlight: { bg: "#12331C", fg: "#4ADE80" },
420
+ badge: "#E3B341",
421
+ danger: DANGER,
422
+ success: "#4ADE80",
423
+ bg: {
424
+ main: "#0B0E14",
425
+ sidebar: "#14171E"
426
+ },
427
+ border: "#30363D",
428
+ accent: "#76A0FF",
429
+ status: {
430
+ pending: "gray",
431
+ running: "#76A0FF",
432
+ done: "#4ADE80",
433
+ error: DANGER
434
+ }
435
+ };
436
+
437
+ // src/ui/CliOutput.tsx
438
+ import { jsxs } from "react/jsx-runtime";
439
+ var CLI_MARKER = "\u203A";
440
+ var RESERVED_ROWS = 16;
441
+ var MAX_LINES = 12;
442
+ function CliOutput() {
443
+ const cliOutput = useWizard((s) => s.cliOutput);
444
+ const { rows } = useWindowSize();
445
+ if (!cliOutput.length) return null;
446
+ const budget = Math.min(Math.max(rows - RESERVED_ROWS, 3), MAX_LINES);
447
+ const visible = cliOutput.slice(-budget);
448
+ const hidden = cliOutput.length - visible.length;
449
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
450
+ hidden > 0 && /* @__PURE__ */ jsxs(Text, { color: COLORS.dim, children: [
451
+ "\u2191 ",
452
+ hidden,
453
+ " earlier line(s)"
454
+ ] }),
455
+ visible.map((line) => /* @__PURE__ */ jsxs(
456
+ Text,
457
+ {
458
+ color: line.stream === "stderr" ? COLORS.muted : COLORS.dim,
459
+ wrap: "truncate",
460
+ children: [
461
+ CLI_MARKER,
462
+ " ",
463
+ line.text
464
+ ]
465
+ },
466
+ line.id
467
+ ))
468
+ ] });
469
+ }
470
+
316
471
  // src/ui/Notices.tsx
317
- import { Box as Box2, Text as Text2, useWindowSize as useWindowSize2 } from "ink";
472
+ import { Box as Box3, Text as Text3, useWindowSize as useWindowSize3 } from "ink";
318
473
  import { useEffect as useEffect2, useState as useState2 } from "react";
319
474
 
320
475
  // src/ui/Table.tsx
321
- import { Box, Text, measureElement, useWindowSize } from "ink";
476
+ import { Box as Box2, Text as Text2, measureElement, useWindowSize as useWindowSize2 } from "ink";
322
477
  import { useEffect, useRef, useState } from "react";
323
478
  import { jsx } from "react/jsx-runtime";
324
479
  function Table({ columns, rows }) {
325
- const { columns: termCols } = useWindowSize();
480
+ const { columns: termCols } = useWindowSize2();
326
481
  const ref = useRef(null);
327
482
  const [width, setWidth] = useState(0);
328
483
  useEffect(() => {
@@ -330,7 +485,7 @@ function Table({ columns, rows }) {
330
485
  }, [termCols, columns, rows]);
331
486
  if (rows.length === 0) return null;
332
487
  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}`)) });
488
+ return /* @__PURE__ */ jsx(Box2, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text2, { wrap: "truncate", children: line }, `tbl-${i}`)) });
334
489
  }
335
490
  function formatTable(columns, rows, width) {
336
491
  const natural = columns.map(
@@ -370,45 +525,10 @@ function resize(widths, budget) {
370
525
  }
371
526
  var truncate = (s, width) => s.length <= width ? s : width <= 1 ? s.slice(0, width) : `${s.slice(0, width - 1)}\u2026`;
372
527
 
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
528
  // src/ui/Notices.tsx
409
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
529
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
410
530
  var AGENT_MARKER = "\u2726";
411
- var RESERVED_ROWS = 14;
531
+ var RESERVED_ROWS2 = 14;
412
532
  var PANEL_TEXT_WIDTH = 45;
413
533
  function messageLineCount(text) {
414
534
  return Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH));
@@ -422,7 +542,7 @@ function noticeLineCount(notice) {
422
542
  return messageLines + tableLines;
423
543
  }
424
544
  function fitVisibleNotices(notices, windowRows) {
425
- const budget = Math.max(windowRows - RESERVED_ROWS, 3);
545
+ const budget = Math.max(windowRows - RESERVED_ROWS2, 3);
426
546
  let used = 0;
427
547
  let count = 0;
428
548
  for (let i = notices.length - 1; i >= 0; i--) {
@@ -455,7 +575,7 @@ function parseHex(hex) {
455
575
  }
456
576
  function Notices() {
457
577
  const notices = useWizard((s) => s.notices);
458
- const { rows: windowRows } = useWindowSize2();
578
+ const { rows: windowRows } = useWindowSize3();
459
579
  const visible = fitVisibleNotices(notices, windowRows);
460
580
  const [pulseStep, setPulseStep] = useState2(0);
461
581
  useEffect2(() => {
@@ -472,14 +592,14 @@ function Notices() {
472
592
  }, []);
473
593
  if (!visible.length) return null;
474
594
  const pulseColor = PULSE_COLORS[pulseStep];
475
- return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
595
+ return /* @__PURE__ */ jsx2(Box3, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
476
596
  const isLatest = i === visible.length - 1;
477
- return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", children: [
597
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
478
598
  notice.messages?.map((m, j) => {
479
599
  const line = typeof m === "string" ? { text: m } : m;
480
600
  const prefix = j === 0 ? `${AGENT_MARKER} ` : " ";
481
- return /* @__PURE__ */ jsxs(
482
- Text2,
601
+ return /* @__PURE__ */ jsxs2(
602
+ Text3,
483
603
  {
484
604
  color: isLatest && !line.color ? pulseColor : line.color ?? COLORS.dim,
485
605
  bold: line.bold,
@@ -497,37 +617,37 @@ function Notices() {
497
617
  }
498
618
 
499
619
  // src/ui/PromptInput.tsx
500
- import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
620
+ import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
501
621
  import TextInput from "ink-text-input";
502
622
  import { useState as useState4 } from "react";
503
623
 
504
624
  // 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";
625
+ import { Box as Box4, Text as Text4 } from "ink";
626
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
507
627
  function NextAction({
508
628
  action,
509
629
  keyHint,
510
630
  hierarchy = "primary"
511
631
  }) {
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 })
632
+ return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "row", gap: 1, children: [
633
+ hierarchy === "primary" && /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `> ${action}` }),
634
+ hierarchy === "secondary" && /* @__PURE__ */ jsxs3(Fragment, { children: [
635
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `>` }),
636
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, bold: true, children: action })
517
637
  ] }),
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: `]` })
638
+ /* @__PURE__ */ jsxs3(Box4, { children: [
639
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: "press " }),
640
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `[` }),
641
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, children: keyHint }),
642
+ /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `]` })
523
643
  ] })
524
644
  ] });
525
645
  }
526
646
 
527
647
  // src/ui/SelectPrompt.tsx
528
- import { Box as Box4, Text as Text4, measureElement as measureElement2, useInput, useWindowSize as useWindowSize3 } from "ink";
648
+ import { Box as Box5, Text as Text5, measureElement as measureElement2, useInput, useWindowSize as useWindowSize4 } from "ink";
529
649
  import { useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
530
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
650
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
531
651
  var CANCEL = "cancel";
532
652
  var ARROW_WIDTH = 4;
533
653
  var COLUMN_GAP = 2;
@@ -564,7 +684,7 @@ function SelectPrompt({
564
684
  if (multi) hints.push({ key: "[space]", label: "select" });
565
685
  hints.push({ key: "[enter]", label: "confirm" });
566
686
  const containerRef = useRef2(null);
567
- const { columns } = useWindowSize3();
687
+ const { columns } = useWindowSize4();
568
688
  const [width, setWidth] = useState3(columns);
569
689
  useLayoutEffect(() => {
570
690
  if (containerRef.current) {
@@ -609,53 +729,53 @@ function SelectPrompt({
609
729
  }
610
730
  }
611
731
  });
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}`)),
732
+ return /* @__PURE__ */ jsx4(Box5, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, width, children: [
733
+ error && /* @__PURE__ */ jsx4(Text5, { color: COLORS.danger, children: error }),
734
+ messages?.map((m, i) => /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
615
735
  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 })
736
+ /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
737
+ question && /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: question }),
738
+ helpText && /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: helpText })
619
739
  ] }),
620
- /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: rows.map((option, i) => {
740
+ /* @__PURE__ */ jsx4(Box5, { flexDirection: "column", children: rows.map((option, i) => {
621
741
  const highlighted = i === index;
622
742
  const isCancel = i === cancelIndex;
623
743
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
624
744
  const sec = isCancel ? void 0 : secondary?.[i];
625
745
  const labelColor = highlighted ? COLORS.highlight.fg : void 0;
626
- const label = /* @__PURE__ */ jsxs3(Text4, { color: labelColor, wrap: "truncate", children: [
746
+ const label = /* @__PURE__ */ jsxs4(Text5, { color: labelColor, wrap: "truncate", children: [
627
747
  highlighted ? "\u276F " : " ",
628
748
  bullet,
629
749
  option
630
750
  ] });
631
751
  const isText = sec?.kind === "text";
632
- return /* @__PURE__ */ jsxs3(
633
- Box4,
752
+ return /* @__PURE__ */ jsxs4(
753
+ Box5,
634
754
  {
635
755
  width: isText ? "100%" : barWidth,
636
756
  paddingX: 1,
637
757
  paddingY: 1,
638
758
  backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
639
759
  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,
760
+ /* @__PURE__ */ jsx4(Box5, { width: isText ? labelWidth : barLabelWidth, children: label }),
761
+ isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box5, { width: textWidth, children: /* @__PURE__ */ jsx4(
762
+ Text5,
643
763
  {
644
764
  wrap: "truncate",
645
765
  color: highlighted ? COLORS.primary : COLORS.muted,
646
766
  children: sec.value
647
767
  }
648
768
  ) }),
649
- sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box4, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text4, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
769
+ sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box5, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text5, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
650
770
  ]
651
771
  },
652
772
  `row-${i}`
653
773
  );
654
774
  }) }),
655
- /* @__PURE__ */ jsx4(Text4, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs3(Text4, { children: [
775
+ /* @__PURE__ */ jsx4(Text5, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
656
776
  i > 0 ? " " : "",
657
- /* @__PURE__ */ jsx4(Text4, { color: COLORS.primary, children: key }),
658
- /* @__PURE__ */ jsxs3(Text4, { color: COLORS.dim, children: [
777
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: key }),
778
+ /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
659
779
  " ",
660
780
  label
661
781
  ] })
@@ -664,7 +784,7 @@ function SelectPrompt({
664
784
  }
665
785
 
666
786
  // src/ui/PromptInput.tsx
667
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
787
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
668
788
  var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
669
789
  function SpaceToContinuePrompt({
670
790
  question,
@@ -675,10 +795,10 @@ function SpaceToContinuePrompt({
675
795
  if (input === " ") onDecide(true);
676
796
  else if (key.escape) onDecide(false);
677
797
  });
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: [
798
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
799
+ messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
800
+ question && /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, children: question }),
801
+ /* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
682
802
  /* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "space" }),
683
803
  /* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
684
804
  ] })
@@ -688,11 +808,11 @@ function PromptInput() {
688
808
  const { phase, inputReq, submitInput } = useWizard();
689
809
  const [draft, setDraft] = useState4("");
690
810
  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" }) });
811
+ return /* @__PURE__ */ jsx5(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
692
812
  }
693
813
  if (phase !== "awaitingInput" || !inputReq) return null;
694
814
  if (inputReq.promptType === "multipleChoice") {
695
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
815
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
696
816
  SelectPrompt,
697
817
  {
698
818
  question: inputReq.prompt,
@@ -709,7 +829,7 @@ function PromptInput() {
709
829
  ) });
710
830
  }
711
831
  if (inputReq.promptType === "multiSelect") {
712
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
832
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
713
833
  SelectPrompt,
714
834
  {
715
835
  multi: true,
@@ -724,7 +844,7 @@ function PromptInput() {
724
844
  ) });
725
845
  }
726
846
  if (inputReq.promptType === "notice") {
727
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
847
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
728
848
  SelectPrompt,
729
849
  {
730
850
  question: inputReq.prompt,
@@ -746,7 +866,7 @@ function PromptInput() {
746
866
  }
747
867
  if (inputReq.promptType === "acceptReject") {
748
868
  const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
749
- return /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(
869
+ return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
750
870
  SelectPrompt,
751
871
  {
752
872
  question: inputReq.prompt,
@@ -757,11 +877,11 @@ function PromptInput() {
757
877
  }
758
878
  ) });
759
879
  }
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: [
880
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
881
+ inputReq.error && /* @__PURE__ */ jsx5(Text6, { color: COLORS.danger, children: inputReq.error }),
882
+ inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
883
+ /* @__PURE__ */ jsxs5(Box6, { children: [
884
+ /* @__PURE__ */ jsxs5(Text6, { color: COLORS.primary, children: [
765
885
  inputReq.prompt,
766
886
  " "
767
887
  ] }),
@@ -783,7 +903,7 @@ function PromptInput() {
783
903
  // src/ui/Welcome.tsx
784
904
  import { dirname as dirname2, join as join3 } from "node:path";
785
905
  import { fileURLToPath } from "node:url";
786
- import { Box as Box6, Spacer, Text as Text6, useInput as useInput3, useWindowSize as useWindowSize4 } from "ink";
906
+ import { Box as Box7, Spacer, Text as Text7, useInput as useInput3, useWindowSize as useWindowSize5 } from "ink";
787
907
 
788
908
  // src/ui/copy/welcome.ts
789
909
  var sidebarItems = [
@@ -811,27 +931,27 @@ var sidebarItems = [
811
931
 
812
932
  // src/ui/Welcome.tsx
813
933
  import Image, { InkPictureProvider } from "ink-picture";
814
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
934
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
815
935
  var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
816
936
  function SidebarItem({
817
937
  title,
818
938
  description
819
939
  }) {
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 })
940
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
941
+ /* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
942
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.success, children: "\u2192" }),
943
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.strong, bold: true, children: title })
824
944
  ] }),
825
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", gap: 2, children: [
945
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 2, children: [
826
946
  /* @__PURE__ */ jsx6(Spacer, {}),
827
- /* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: description })
947
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: description })
828
948
  ] })
829
949
  ] });
830
950
  }
831
951
  function Welcome() {
832
952
  const confirmStart = useWizard((s) => s.confirmStart);
833
953
  const openLearnMore = useWizard((s) => s.openLearnMore);
834
- const { rows } = useWindowSize4();
954
+ const { rows } = useWindowSize5();
835
955
  useInput3((input) => {
836
956
  if (input === " ") confirmStart();
837
957
  else if (input === "i") openLearnMore();
@@ -850,15 +970,15 @@ function Welcome() {
850
970
  if (rows < 30) {
851
971
  layout = scales["small"];
852
972
  }
853
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
973
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
854
974
  /* @__PURE__ */ jsx6(
855
- Box6,
975
+ Box7,
856
976
  {
857
977
  paddingY: layout.main.padding.y,
858
978
  paddingX: layout.main.padding.x,
859
979
  flexDirection: "column",
860
980
  justifyContent: "center",
861
- children: /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 2, children: [
981
+ children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 2, children: [
862
982
  /* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
863
983
  Image,
864
984
  {
@@ -870,16 +990,16 @@ function Welcome() {
870
990
  protocol: "halfBlock"
871
991
  }
872
992
  ) }),
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: [
993
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
994
+ /* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
875
995
  /* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "space" }),
876
996
  /* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
877
997
  ] })
878
998
  ] })
879
999
  }
880
1000
  ),
881
- /* @__PURE__ */ jsxs5(
882
- Box6,
1001
+ /* @__PURE__ */ jsxs6(
1002
+ Box7,
883
1003
  {
884
1004
  backgroundColor: COLORS.bg.sidebar,
885
1005
  width: 40,
@@ -889,7 +1009,7 @@ function Welcome() {
889
1009
  flexDirection: "column",
890
1010
  justifyContent: "center",
891
1011
  children: [
892
- /* @__PURE__ */ jsx6(Text6, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
1012
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
893
1013
  sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
894
1014
  ]
895
1015
  }
@@ -899,7 +1019,7 @@ function Welcome() {
899
1019
 
900
1020
  // src/ui/LearnMore.tsx
901
1021
  import { Fragment as Fragment2 } from "react";
902
- import { Box as Box7, Text as Text7, useInput as useInput4, useWindowSize as useWindowSize5 } from "ink";
1022
+ import { Box as Box8, Text as Text8, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
903
1023
 
904
1024
  // src/ui/copy/learn-more.ts
905
1025
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -936,7 +1056,7 @@ var policyLinks = [
936
1056
  ];
937
1057
 
938
1058
  // src/ui/LearnMore.tsx
939
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1059
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
940
1060
  var TAG_COLORS = {
941
1061
  READ: COLORS.success,
942
1062
  WRITE: COLORS.badge,
@@ -952,25 +1072,25 @@ function NeverLine({
952
1072
  }) {
953
1073
  const used = segments.reduce((n, s) => n + s.text.length, 0);
954
1074
  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" }),
1075
+ return /* @__PURE__ */ jsxs7(Text8, { children: [
1076
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" }),
957
1077
  " ".repeat(NEVER_BOX_PAD_X),
958
- segments.map((s, i) => /* @__PURE__ */ jsx7(Text7, { color: s.color, bold: s.bold, children: s.text }, i)),
1078
+ segments.map((s, i) => /* @__PURE__ */ jsx7(Text8, { color: s.color, bold: s.bold, children: s.text }, i)),
959
1079
  " ".repeat(rightPad),
960
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: "\u2502" })
1080
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" })
961
1081
  ] });
962
1082
  }
963
1083
  function LearnMore() {
964
1084
  const confirmStart = useWizard((s) => s.confirmStart);
965
1085
  const backToHome = useWizard((s) => s.backToHome);
966
- const { columns } = useWindowSize5();
1086
+ const { columns } = useWindowSize6();
967
1087
  const dividerWidth = Math.max(0, columns - PADDING_X * 2);
968
1088
  useInput4((input, key) => {
969
1089
  if (key.escape) backToHome();
970
1090
  else if (input === " ") confirmStart();
971
1091
  });
972
- return /* @__PURE__ */ jsxs6(
973
- Box7,
1092
+ return /* @__PURE__ */ jsxs7(
1093
+ Box8,
974
1094
  {
975
1095
  flexDirection: "column",
976
1096
  paddingX: PADDING_X,
@@ -978,20 +1098,20 @@ function LearnMore() {
978
1098
  width: "100%",
979
1099
  gap: 1,
980
1100
  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}` })
1101
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
1102
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: accessIntro }),
1103
+ /* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", marginTop: 1, children: [
1104
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
1105
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, marginTop: 1, children: [
1106
+ /* @__PURE__ */ jsx7(Box8, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text8, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
1107
+ /* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text8, { children: [
1108
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: item.title }),
1109
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
990
1110
  ] }) })
991
1111
  ] })
992
1112
  ] }, 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` }),
1113
+ /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", children: [
1114
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
995
1115
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
996
1116
  /* @__PURE__ */ jsx7(
997
1117
  NeverLine,
@@ -1000,7 +1120,7 @@ function LearnMore() {
1000
1120
  segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
1001
1121
  }
1002
1122
  ),
1003
- neverItems.map((item) => /* @__PURE__ */ jsxs6(Fragment2, { children: [
1123
+ neverItems.map((item) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
1004
1124
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1005
1125
  /* @__PURE__ */ jsx7(
1006
1126
  NeverLine,
@@ -1015,23 +1135,23 @@ function LearnMore() {
1015
1135
  )
1016
1136
  ] }, item)),
1017
1137
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1018
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1138
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1019
1139
  ] }),
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 })
1140
+ /* @__PURE__ */ jsx7(Box8, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1141
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1142
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.accent, children: link.url })
1023
1143
  ] }, 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" })
1144
+ /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1145
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1146
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
1147
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "esc" }),
1148
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "] back" })
1029
1149
  ] }),
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: "space" }),
1033
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.muted, children: "]" }),
1034
- /* @__PURE__ */ jsx7(Text7, { color: COLORS.success, bold: true, children: "start wizard" })
1150
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1151
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
1152
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "space" }),
1153
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "]" }),
1154
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.success, bold: true, children: "start wizard" })
1035
1155
  ] })
1036
1156
  ] })
1037
1157
  ]
@@ -1040,10 +1160,10 @@ function LearnMore() {
1040
1160
  }
1041
1161
 
1042
1162
  // src/ui/Sidebar.tsx
1043
- import { Box as Box10, Text as Text10 } from "ink";
1163
+ import { Box as Box11, Text as Text11 } from "ink";
1044
1164
 
1045
1165
  // src/ui/Steps.tsx
1046
- import { Box as Box8, Text as Text8 } from "ink";
1166
+ import { Box as Box9, Text as Text9 } from "ink";
1047
1167
  import Spinner from "ink-spinner";
1048
1168
 
1049
1169
  // src/core/persistence.ts
@@ -1072,11 +1192,11 @@ async function clearWorkflowState(workflowId) {
1072
1192
  }
1073
1193
 
1074
1194
  // src/ui/Steps.tsx
1075
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1195
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1076
1196
  function Steps() {
1077
1197
  const { steps } = useWizard();
1078
1198
  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: [
1199
+ 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
1200
  s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
1081
1201
  " ",
1082
1202
  s.title
@@ -1086,7 +1206,7 @@ function CurrentStep() {
1086
1206
  const { steps } = useWizard();
1087
1207
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
1088
1208
  if (!currentStep) return null;
1089
- return /* @__PURE__ */ jsxs7(Text8, { color: COLORS.status.running, children: [
1209
+ return /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status.running, children: [
1090
1210
  /* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
1091
1211
  " ",
1092
1212
  ` ${currentStep.title}`
@@ -1094,19 +1214,19 @@ function CurrentStep() {
1094
1214
  }
1095
1215
 
1096
1216
  // 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";
1217
+ import { Box as Box10, Text as Text10 } from "ink";
1218
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1099
1219
  function Progress() {
1100
1220
  const { steps, currentStepIndex } = useWizard();
1101
1221
  const visibleSteps = steps.filter(isStepVisible);
1102
1222
  if (visibleSteps.length === 0) return null;
1103
1223
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
1104
1224
  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 })
1225
+ return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
1226
+ /* @__PURE__ */ jsx9(Text10, { color: COLORS.muted, children: "STEP" }),
1227
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: activeStepNumber }),
1228
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: "/" }),
1229
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: visibleSteps.length })
1110
1230
  ] });
1111
1231
  }
1112
1232
 
@@ -1117,10 +1237,10 @@ var sidebarCommands = [
1117
1237
  ];
1118
1238
 
1119
1239
  // src/ui/Sidebar.tsx
1120
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1240
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1121
1241
  function Sidebar() {
1122
- return /* @__PURE__ */ jsxs9(
1123
- Box10,
1242
+ return /* @__PURE__ */ jsxs10(
1243
+ Box11,
1124
1244
  {
1125
1245
  backgroundColor: "#14171E",
1126
1246
  width: 30,
@@ -1129,16 +1249,16 @@ function Sidebar() {
1129
1249
  flexDirection: "column",
1130
1250
  justifyContent: "space-between",
1131
1251
  children: [
1132
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
1133
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "PROGRESS" }),
1252
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
1253
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: "PROGRESS" }),
1134
1254
  /* @__PURE__ */ jsx10(Steps, {})
1135
1255
  ] }),
1136
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
1256
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
1137
1257
  /* @__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 })
1258
+ /* @__PURE__ */ jsx10(Box11, { flexDirection: "column", children: sidebarCommands.map((c) => {
1259
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
1260
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1261
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: c.description })
1142
1262
  ] });
1143
1263
  }) })
1144
1264
  ] })
@@ -1148,12 +1268,12 @@ function Sidebar() {
1148
1268
  }
1149
1269
 
1150
1270
  // 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";
1271
+ import { Box as Box12, Text as Text12 } from "ink";
1272
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1153
1273
  function Ribbon() {
1154
1274
  const firstCommand = sidebarCommands[0];
1155
- return /* @__PURE__ */ jsxs10(
1156
- Box11,
1275
+ return /* @__PURE__ */ jsxs11(
1276
+ Box12,
1157
1277
  {
1158
1278
  backgroundColor: "#14171E",
1159
1279
  flexDirection: "row",
@@ -1163,9 +1283,9 @@ function Ribbon() {
1163
1283
  children: [
1164
1284
  /* @__PURE__ */ jsx11(Progress, {}),
1165
1285
  /* @__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 })
1286
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
1287
+ /* @__PURE__ */ jsx11(Text12, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1288
+ /* @__PURE__ */ jsx11(Text12, { color: COLORS.muted, children: firstCommand.description })
1169
1289
  ] })
1170
1290
  ]
1171
1291
  }
@@ -1177,8 +1297,8 @@ import { useState as useState6 } from "react";
1177
1297
 
1178
1298
  // src/ui/Logs.tsx
1179
1299
  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";
1300
+ import { Box as Box13, Text as Text13, measureElement as measureElement3, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
1301
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1182
1302
  var KIND_COLOR = {
1183
1303
  tool: COLORS.primary,
1184
1304
  prompt: COLORS.badge
@@ -1208,7 +1328,7 @@ function formatTimestamp(ms) {
1208
1328
  }
1209
1329
  function Logs() {
1210
1330
  const logs = useWizard((s) => s.logs);
1211
- const { rows, columns } = useWindowSize6();
1331
+ const { rows, columns } = useWindowSize7();
1212
1332
  const viewportRef = useRef3(null);
1213
1333
  const [viewportHeight, setViewportHeight] = useState5(0);
1214
1334
  const [viewportWidth, setViewportWidth] = useState5(0);
@@ -1245,10 +1365,10 @@ function Logs() {
1245
1365
  const visible = logs.slice(scrollOffset, scrollOffset + capacity);
1246
1366
  const hiddenAbove = scrollOffset;
1247
1367
  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: [
1368
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1369
+ logs.length === 0 && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "No logs yet." }),
1370
+ /* @__PURE__ */ jsxs12(Box13, { ref: viewportRef, flexDirection: "column", flexGrow: 1, children: [
1371
+ hiddenAbove > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
1252
1372
  "\u2191 ",
1253
1373
  hiddenAbove,
1254
1374
  " more"
@@ -1263,20 +1383,20 @@ function Logs() {
1263
1383
  const name = truncate2(entry.name, budget);
1264
1384
  budget -= name.length;
1265
1385
  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 })
1386
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: ROW_GAP, children: [
1387
+ /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: timestamp }),
1388
+ /* @__PURE__ */ jsx12(Text13, { color: logNameColor(entry), wrap: "truncate", children: name }),
1389
+ preview && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, wrap: "truncate", children: preview }),
1390
+ durationText && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: durationText })
1271
1391
  ] }, entry.id);
1272
1392
  }),
1273
- hiddenBelow > 0 && /* @__PURE__ */ jsxs11(Text12, { color: COLORS.dim, children: [
1393
+ hiddenBelow > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
1274
1394
  "\u2193 ",
1275
1395
  hiddenBelow,
1276
1396
  " more"
1277
1397
  ] })
1278
1398
  ] }),
1279
- /* @__PURE__ */ jsx12(Text12, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1399
+ /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1280
1400
  ] });
1281
1401
  }
1282
1402
 
@@ -1300,11 +1420,11 @@ function track(event, payload) {
1300
1420
  }
1301
1421
 
1302
1422
  // src/ui/App.tsx
1303
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1423
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1304
1424
  function App() {
1305
1425
  const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
1306
1426
  const { exit } = useApp();
1307
- const { columns, rows } = useWindowSize7();
1427
+ const { columns, rows } = useWindowSize8();
1308
1428
  const [showLogs, setShowLogs] = useState6(false);
1309
1429
  const finished = phase === "done" || phase === "error";
1310
1430
  const currentStep = steps[currentStepIndex];
@@ -1317,7 +1437,8 @@ function App() {
1317
1437
  { isActive: finished }
1318
1438
  );
1319
1439
  useInput6((_input, key) => {
1320
- if (phase === "idle" || phase === "preflight") return;
1440
+ if (phase === "idle" || phase === "preflight" || phase === "authenticating")
1441
+ return;
1321
1442
  if (key.tab) {
1322
1443
  setShowLogs(!showLogs);
1323
1444
  track("AI Wizard Interaction", {
@@ -1327,7 +1448,7 @@ function App() {
1327
1448
  });
1328
1449
  }
1329
1450
  });
1330
- const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "spaceToContinue";
1451
+ const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "spaceToContinue";
1331
1452
  useInput6((_input, key) => {
1332
1453
  if (escOwnedElsewhere) return;
1333
1454
  if (key.escape) {
@@ -1340,19 +1461,19 @@ function App() {
1340
1461
  exit();
1341
1462
  }
1342
1463
  });
1343
- const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1464
+ const mainWindowVisible = phase === "authenticating" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1344
1465
  const flexDirection = columns > 90 ? "row" : "column";
1345
1466
  const showSidebar = flexDirection === "row";
1346
- return /* @__PURE__ */ jsxs12(
1347
- Box13,
1467
+ return /* @__PURE__ */ jsxs13(
1468
+ Box14,
1348
1469
  {
1349
1470
  backgroundColor: COLORS.bg.main,
1350
1471
  flexDirection: "row",
1351
1472
  width: columns,
1352
1473
  minHeight: rows,
1353
1474
  children: [
1354
- mainWindowVisible && /* @__PURE__ */ jsxs12(
1355
- Box13,
1475
+ mainWindowVisible && /* @__PURE__ */ jsxs13(
1476
+ Box14,
1356
1477
  {
1357
1478
  flexDirection,
1358
1479
  width: "100%",
@@ -1360,8 +1481,8 @@ function App() {
1360
1481
  children: [
1361
1482
  showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
1362
1483
  /* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
1363
- /* @__PURE__ */ jsxs12(
1364
- Box13,
1484
+ /* @__PURE__ */ jsxs13(
1485
+ Box14,
1365
1486
  {
1366
1487
  flexDirection: "column",
1367
1488
  paddingX: 4,
@@ -1369,10 +1490,15 @@ function App() {
1369
1490
  width: showSidebar ? 70 : "100%",
1370
1491
  flexGrow: showSidebar ? 1 : 0,
1371
1492
  children: [
1493
+ phase === "authenticating" && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginBottom: 1, children: [
1494
+ /* @__PURE__ */ jsx13(Text14, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
1495
+ /* @__PURE__ */ jsx13(Text14, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
1496
+ ] }),
1497
+ /* @__PURE__ */ jsx13(CliOutput, {}),
1372
1498
  /* @__PURE__ */ jsx13(Notices, {}),
1373
1499
  /* @__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: [
1500
+ phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1501
+ phase === "error" && error && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.error, children: [
1376
1502
  "\u2716 ",
1377
1503
  error
1378
1504
  ] }) })
@@ -1784,61 +1910,134 @@ async function runWorkflow(workflow2, appId) {
1784
1910
  }
1785
1911
  }
1786
1912
 
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;
1913
+ // src/lib/algoliaApp.ts
1914
+ import { z as z3 } from "zod";
1915
+ var currentSchema = z3.object({
1916
+ id: z3.string().min(1),
1917
+ name: z3.string().default(""),
1918
+ plan: z3.string().optional()
1919
+ });
1920
+ var listSchema = z3.array(
1921
+ z3.object({
1922
+ id: z3.string().min(1),
1923
+ name: z3.string().default(""),
1924
+ plan_label: z3.string().optional()
1925
+ }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
1926
+ );
1927
+ async function currentApplication() {
1928
+ let raw;
1800
1929
  try {
1801
- parsed = parseToml(tomlText);
1930
+ raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1802
1931
  } catch {
1803
- return [];
1932
+ return null;
1933
+ }
1934
+ const parsed = currentSchema.safeParse(parseJson(raw));
1935
+ return parsed.success ? parsed.data : null;
1936
+ }
1937
+ async function requireApplication() {
1938
+ const app2 = await currentApplication();
1939
+ if (!app2) {
1940
+ throw new Error(
1941
+ "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1942
+ );
1943
+ }
1944
+ return app2;
1945
+ }
1946
+ async function listApplications() {
1947
+ const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1948
+ const parsed = listSchema.safeParse(parseJson(raw));
1949
+ if (!parsed.success) {
1950
+ throw new Error("Could not read the list of Algolia applications.");
1951
+ }
1952
+ return parsed.data;
1953
+ }
1954
+ function selectableApplications(apps) {
1955
+ const nameCounts = /* @__PURE__ */ new Map();
1956
+ for (const app2 of apps) {
1957
+ const name = app2.name.trim();
1958
+ if (name) nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1);
1804
1959
  }
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;
1960
+ return apps.filter((app2) => nameCounts.get(app2.name.trim()) === 1);
1961
+ }
1962
+ async function selectApplication(name) {
1963
+ await runAlgoliaCli(["application", "select", "--app-name", name]);
1964
+ }
1965
+ function parseJson(text) {
1818
1966
  try {
1819
- profiles = profilesFromConfig(await readFile3(configPath(), "utf8"));
1967
+ return JSON.parse(text);
1820
1968
  } catch {
1821
- profiles = [];
1969
+ return void 0;
1822
1970
  }
1823
- const profile2 = profiles[0];
1824
- if (!profile2) {
1971
+ }
1972
+
1973
+ // src/lib/algoliaAppPicker.ts
1974
+ function secondaryFor(app2) {
1975
+ return app2.plan ? { kind: "badge", value: app2.plan } : void 0;
1976
+ }
1977
+ async function promptForApplication() {
1978
+ const store2 = useWizard.getState();
1979
+ const phaseBefore = store2.phase;
1980
+ const all = await listApplications();
1981
+ const selectable = selectableApplications(all);
1982
+ const skipped = all.length - selectable.length;
1983
+ if (selectable.length === 0) {
1984
+ const why = all.length === 0 ? "this account has no applications" : "none of this account\u2019s applications have a unique name";
1825
1985
  throw new Error(
1826
- "No Algolia profile is configured. Run `npx @algolia/cli auth login` to authenticate."
1986
+ `No selectable Algolia application was found: the CLI selects an application by name, and ${why}. Name one in the Algolia dashboard, or run \`npx @algolia/cli@latest application select\` directly.`
1987
+ );
1988
+ }
1989
+ const messages = ["Which Algolia application should the wizard work in?"];
1990
+ if (skipped > 0) {
1991
+ messages.push(
1992
+ `${skipped} application${skipped === 1 ? "" : "s"} not listed: the CLI selects by name, so unnamed and duplicate-named ones can\u2019t be chosen here.`
1993
+ );
1994
+ }
1995
+ for (; ; ) {
1996
+ const choice = await store2.requestUserInput({
1997
+ prompt: "Select an application",
1998
+ promptType: "multipleChoice",
1999
+ options: selectable.map((app2) => `${app2.name} \u2014 ${app2.id}`),
2000
+ secondary: selectable.map(secondaryFor),
2001
+ messages
2002
+ });
2003
+ const index = selectable.findIndex(
2004
+ (app2) => `${app2.name} \u2014 ${app2.id}` === choice
1827
2005
  );
2006
+ const chosen = selectable[index];
2007
+ if (!chosen) {
2008
+ throw new Error("Application picker received an unexpected selection");
2009
+ }
2010
+ try {
2011
+ await selectApplication(chosen.name);
2012
+ } catch (err) {
2013
+ logger.warn(
2014
+ { app: chosen.id, err: err.message },
2015
+ "application select failed; re-prompting"
2016
+ );
2017
+ messages.push(
2018
+ `Could not select \u201C${chosen.name}\u201D. It may have been renamed or removed \u2014 pick another.`
2019
+ );
2020
+ continue;
2021
+ }
2022
+ if (phaseBefore === "authenticating") store2.resumeAuth();
2023
+ logger.info({ app: chosen.id }, "selected Algolia application");
2024
+ return chosen;
1828
2025
  }
1829
- return profile2;
2026
+ }
2027
+ async function ensureApplication() {
2028
+ return await currentApplication() ?? await promptForApplication();
1830
2029
  }
1831
2030
 
1832
2031
  // src/workflows/default.ts
1833
- import { z as z25 } from "zod";
2032
+ import { z as z26 } from "zod";
1834
2033
 
1835
2034
  // 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)
2035
+ import { z as z4 } from "zod";
2036
+ var indicesListSchema = z4.object({
2037
+ items: z4.array(
2038
+ z4.object({
2039
+ name: z4.string(),
2040
+ entries: z4.number().default(0)
1842
2041
  })
1843
2042
  )
1844
2043
  });
@@ -1909,12 +2108,12 @@ import "zod";
1909
2108
 
1910
2109
  // src/lib/tools/listFiles.ts
1911
2110
  import { tool } from "ai";
1912
- import z4 from "zod";
2111
+ import z5 from "zod";
1913
2112
  import { readdir } from "node:fs/promises";
1914
2113
 
1915
2114
  // src/lib/tools/path.ts
1916
2115
  import { lstat } from "node:fs/promises";
1917
- import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join7, sep } from "node:path";
2116
+ import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join6, sep } from "node:path";
1918
2117
  function resolveInRoot(ctx, path) {
1919
2118
  const target = resolve2(ctx.cwd, path);
1920
2119
  const rel = relative(ctx.root, target);
@@ -1930,7 +2129,7 @@ async function hasSymlinkParent(ctx, target) {
1930
2129
  let current = ctx.root;
1931
2130
  const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
1932
2131
  for (const part of parts) {
1933
- current = join7(current, part);
2132
+ current = join6(current, part);
1934
2133
  try {
1935
2134
  if ((await lstat(current)).isSymbolicLink()) return true;
1936
2135
  } catch (err) {
@@ -1945,7 +2144,7 @@ async function hasSymlinkParent(ctx, target) {
1945
2144
  function listFilesTool(ctx) {
1946
2145
  return tool({
1947
2146
  description: "List files in the current working directory",
1948
- inputSchema: z4.object(),
2147
+ inputSchema: z5.object(),
1949
2148
  execute: async () => {
1950
2149
  logger.info("called listFiles tool");
1951
2150
  if (++ctx.counts.list > ctx.limits.list) {
@@ -1961,13 +2160,13 @@ function listFilesTool(ctx) {
1961
2160
 
1962
2161
  // src/lib/tools/changeDirectory.ts
1963
2162
  import { tool as tool2 } from "ai";
1964
- import z5 from "zod";
2163
+ import z6 from "zod";
1965
2164
  import { stat } from "node:fs/promises";
1966
2165
  function changeDirectoryTool(ctx) {
1967
2166
  return tool2({
1968
2167
  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")
2168
+ inputSchema: z6.object({
2169
+ path: z6.string().describe("Directory to change into")
1971
2170
  }),
1972
2171
  execute: async ({ path }) => {
1973
2172
  logger.info({ path }, "called changeDirectory tool");
@@ -1989,13 +2188,13 @@ function changeDirectoryTool(ctx) {
1989
2188
 
1990
2189
  // src/lib/tools/reportStatus.ts
1991
2190
  import { tool as tool3 } from "ai";
1992
- import z6 from "zod";
2191
+ import z7 from "zod";
1993
2192
  function reportStatusTool(output) {
1994
2193
  return tool3({
1995
2194
  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(),
2195
+ inputSchema: z7.object({
2196
+ status: z7.enum(["success", "fail"]),
2197
+ reason: z7.string().optional(),
1999
2198
  output
2000
2199
  }),
2001
2200
  execute: async ({ status, reason, output: output2 }) => {
@@ -2007,8 +2206,8 @@ function reportStatusTool(output) {
2007
2206
 
2008
2207
  // src/lib/tools/readFile.ts
2009
2208
  import { tool as tool4 } from "ai";
2010
- import z7 from "zod";
2011
- import { readFile as readFile4 } from "node:fs/promises";
2209
+ import z8 from "zod";
2210
+ import { readFile as readFile3 } from "node:fs/promises";
2012
2211
 
2013
2212
  // src/lib/tools/env.ts
2014
2213
  import { basename } from "node:path";
@@ -2035,8 +2234,8 @@ function redactEnvValues(content) {
2035
2234
  function readFileTool(ctx) {
2036
2235
  return tool4({
2037
2236
  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")
2237
+ inputSchema: z8.object({
2238
+ filePath: z8.string().describe("Path to the file to read")
2040
2239
  }),
2041
2240
  execute: async ({ filePath }) => {
2042
2241
  if (++ctx.counts.read > ctx.limits.read) {
@@ -2046,7 +2245,7 @@ function readFileTool(ctx) {
2046
2245
  const resolved = resolveInRoot(ctx, filePath);
2047
2246
  if (!resolved.ok) return resolved.error;
2048
2247
  try {
2049
- const content = await readFile4(resolved.target, "utf8");
2248
+ const content = await readFile3(resolved.target, "utf8");
2050
2249
  return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
2051
2250
  } catch (err) {
2052
2251
  return `Error reading ${filePath}: ${err.message}`;
@@ -2057,15 +2256,15 @@ function readFileTool(ctx) {
2057
2256
 
2058
2257
  // src/lib/tools/writeFile.ts
2059
2258
  import { tool as tool5 } from "ai";
2060
- import z8 from "zod";
2259
+ import z9 from "zod";
2061
2260
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2062
2261
  import { dirname as dirname4 } from "node:path";
2063
2262
  function writeFileTool(ctx) {
2064
2263
  return tool5({
2065
2264
  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")
2265
+ inputSchema: z9.object({
2266
+ filePath: z9.string().describe("Path to the file to write"),
2267
+ content: z9.string().describe("Content to write to the file")
2069
2268
  }),
2070
2269
  execute: async ({ filePath, content }) => {
2071
2270
  logger.info({ filePath }, "called writeFile tool");
@@ -2090,9 +2289,95 @@ function writeFileTool(ctx) {
2090
2289
 
2091
2290
  // src/lib/tools/writeAlgoliaCredentials.ts
2092
2291
  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";
2292
+ import z11 from "zod";
2293
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
2095
2294
  import { dirname as dirname5 } from "node:path";
2295
+
2296
+ // src/lib/algoliaApiKey.ts
2297
+ import { z as z10 } from "zod";
2298
+ var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
2299
+ var WRITE_ACLS = [
2300
+ "addObject",
2301
+ "deleteObject",
2302
+ "settings",
2303
+ "editSettings",
2304
+ "listIndexes"
2305
+ ];
2306
+ var WRITE_ACL_SET = new Set(WRITE_ACLS);
2307
+ var apiKeySchema = z10.object({
2308
+ value: z10.string().min(1),
2309
+ acl: z10.array(z10.string()).default([]),
2310
+ indexes: z10.array(z10.string()).default([])
2311
+ });
2312
+ var apiKeyListSchema = z10.object({
2313
+ items: z10.array(apiKeySchema).optional(),
2314
+ keys: z10.array(apiKeySchema).optional()
2315
+ }).transform((o) => o.items ?? o.keys ?? []);
2316
+ var createdKeySchema = z10.object({
2317
+ key: z10.string().min(1).optional(),
2318
+ value: z10.string().min(1).optional()
2319
+ });
2320
+ function canReuse(key, index) {
2321
+ return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
2322
+ }
2323
+ async function createSearchKey(index) {
2324
+ const stdout = await runAlgoliaCli([
2325
+ "apikeys",
2326
+ "create",
2327
+ "--indices",
2328
+ index,
2329
+ "--acl",
2330
+ "search,browse",
2331
+ "--description",
2332
+ `wizard search-only key for ${index}`,
2333
+ "-o",
2334
+ "json"
2335
+ ]);
2336
+ const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
2337
+ const created = key ?? value;
2338
+ if (!created) throw new Error("apikeys create returned no key value");
2339
+ return created;
2340
+ }
2341
+ function canReuseForWrites(key, index) {
2342
+ return WRITE_ACLS.every((acl) => key.acl.includes(acl)) && key.acl.every((acl) => WRITE_ACL_SET.has(acl)) && key.indexes.includes(index);
2343
+ }
2344
+ async function resolveWriteKey(index) {
2345
+ const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2346
+ const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key2) => canReuseForWrites(key2, index))?.value;
2347
+ if (existing) {
2348
+ logger.info({ index }, "reusing existing write API key");
2349
+ return existing;
2350
+ }
2351
+ logger.info({ index }, "no reusable write key found; creating one");
2352
+ const created = await runAlgoliaCli([
2353
+ "apikeys",
2354
+ "create",
2355
+ "--indices",
2356
+ index,
2357
+ "--acl",
2358
+ WRITE_ACLS.join(","),
2359
+ "--description",
2360
+ `wizard write key for ${index}`,
2361
+ "-o",
2362
+ "json"
2363
+ ]);
2364
+ const { key, value } = createdKeySchema.parse(JSON.parse(created));
2365
+ const writeKey = key ?? value;
2366
+ if (!writeKey) throw new Error("apikeys create returned no key value");
2367
+ return writeKey;
2368
+ }
2369
+ async function resolveSearchOnlyKey(index) {
2370
+ const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2371
+ const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
2372
+ if (existing) {
2373
+ logger.info({ index }, "reusing existing search-only API key");
2374
+ return existing;
2375
+ }
2376
+ logger.info({ index }, "no reusable search-only key found; creating one");
2377
+ return createSearchKey(index);
2378
+ }
2379
+
2380
+ // src/lib/tools/writeAlgoliaCredentials.ts
2096
2381
  var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
2097
2382
  var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
2098
2383
  function appendEnv(content, entries) {
@@ -2106,9 +2391,9 @@ function hasEnv(content, name) {
2106
2391
  }
2107
2392
  function writeCredentialsTool(ctx) {
2108
2393
  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(
2394
+ 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.`,
2395
+ inputSchema: z11.object({
2396
+ filePath: z11.string().describe(
2112
2397
  'Path to the env file to write credentials into (e.g. ".env")'
2113
2398
  )
2114
2399
  }),
@@ -2116,11 +2401,17 @@ function writeCredentialsTool(ctx) {
2116
2401
  logger.info({ filePath }, "called writeCredentials tool");
2117
2402
  const resolved = resolveInRoot(ctx, filePath);
2118
2403
  if (resolved.ok === false) return resolved.error;
2119
- let profile2;
2404
+ const targetIndex = useWizard.getState().targetIndex;
2405
+ if (!targetIndex) {
2406
+ return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
2407
+ }
2408
+ let appId;
2409
+ let writeKey;
2120
2410
  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.";
2411
+ appId = (await requireApplication()).id;
2412
+ writeKey = await resolveWriteKey(targetIndex);
2413
+ } catch (err) {
2414
+ return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
2124
2415
  }
2125
2416
  try {
2126
2417
  if (await hasSymlinkParent(ctx, resolved.target)) {
@@ -2128,7 +2419,7 @@ function writeCredentialsTool(ctx) {
2128
2419
  }
2129
2420
  let existing = "";
2130
2421
  try {
2131
- existing = await readFile5(resolved.target, "utf8");
2422
+ existing = await readFile4(resolved.target, "utf8");
2132
2423
  } catch (err) {
2133
2424
  if (err.code !== "ENOENT") throw err;
2134
2425
  }
@@ -2139,8 +2430,8 @@ function writeCredentialsTool(ctx) {
2139
2430
  return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
2140
2431
  }
2141
2432
  const envWithCredentials = appendEnv(existing, [
2142
- [APP_ID_VAR, profile2.appId],
2143
- [API_KEY_VAR, profile2.apiKey]
2433
+ [APP_ID_VAR, appId],
2434
+ [API_KEY_VAR, writeKey]
2144
2435
  ]);
2145
2436
  await mkdir4(dirname5(resolved.target), { recursive: true });
2146
2437
  await writeFile4(resolved.target, envWithCredentials, "utf8");
@@ -2154,19 +2445,19 @@ function writeCredentialsTool(ctx) {
2154
2445
 
2155
2446
  // src/lib/tools/searchFiles.ts
2156
2447
  import { tool as tool7 } from "ai";
2157
- import z10 from "zod";
2158
- import { readdir as readdir3, readFile as readFile7 } from "node:fs/promises";
2159
- import { join as join10 } from "node:path";
2448
+ import z12 from "zod";
2449
+ import { readdir as readdir3, readFile as readFile6 } from "node:fs/promises";
2450
+ import { join as join9 } from "node:path";
2160
2451
 
2161
2452
  // src/lib/languages.ts
2162
2453
  import { readdir as readdir2 } from "node:fs/promises";
2163
2454
  import { existsSync as existsSync2 } from "node:fs";
2164
- import { join as join9 } from "node:path";
2455
+ import { join as join8 } from "node:path";
2165
2456
 
2166
2457
  // src/lib/tools/utils/packageManager.ts
2167
- import { readFile as readFile6 } from "node:fs/promises";
2458
+ import { readFile as readFile5 } from "node:fs/promises";
2168
2459
  import { existsSync } from "node:fs";
2169
- import { join as join8 } from "node:path";
2460
+ import { join as join7 } from "node:path";
2170
2461
  var LOCKFILES = [
2171
2462
  ["pnpm-lock.yaml", "pnpm"],
2172
2463
  ["yarn.lock", "yarn"],
@@ -2175,13 +2466,13 @@ var LOCKFILES = [
2175
2466
  ["package-lock.json", "npm"]
2176
2467
  ];
2177
2468
  async function readPackageJson(cwd = process.cwd()) {
2178
- return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
2469
+ return JSON.parse(await readFile5(join7(cwd, "package.json"), "utf8"));
2179
2470
  }
2180
2471
  function packageManagerFrom(pkg) {
2181
2472
  return pkg.packageManager?.split("@")[0] ?? "npm";
2182
2473
  }
2183
2474
  function packageManagerFromLockfile(cwd) {
2184
- return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
2475
+ return LOCKFILES.find(([file]) => existsSync(join7(cwd, file)))?.[1];
2185
2476
  }
2186
2477
  async function detectPackageManager(cwd) {
2187
2478
  try {
@@ -2703,17 +2994,17 @@ var DEFAULT_LANGUAGE_ID = "javascript";
2703
2994
  var JAVASCRIPT = "javascript";
2704
2995
  var CURATED_LANGUAGES = Object.values(
2705
2996
  LANGUAGE_PROFILES
2706
- ).map((profile2) => profile2.displayName);
2707
- function isBackendLanguage(profile2) {
2708
- return profile2.id !== JAVASCRIPT;
2997
+ ).map((profile) => profile.displayName);
2998
+ function isBackendLanguage(profile) {
2999
+ return profile.id !== JAVASCRIPT;
2709
3000
  }
2710
3001
  function normalizeLanguageName(name) {
2711
3002
  return name.toLowerCase().trim().replace(/[\s_-]+/g, "");
2712
3003
  }
2713
3004
  var ALIAS_TO_ID = /* @__PURE__ */ new Map();
2714
- for (const profile2 of Object.values(LANGUAGE_PROFILES)) {
2715
- for (const alias of [profile2.id, profile2.displayName, ...profile2.aliases]) {
2716
- ALIAS_TO_ID.set(normalizeLanguageName(alias), profile2.id);
3005
+ for (const profile of Object.values(LANGUAGE_PROFILES)) {
3006
+ for (const alias of [profile.id, profile.displayName, ...profile.aliases]) {
3007
+ ALIAS_TO_ID.set(normalizeLanguageName(alias), profile.id);
2717
3008
  }
2718
3009
  }
2719
3010
  function resolveLanguageProfile(name) {
@@ -2734,12 +3025,12 @@ var ALL_SKIP_DIRS = /* @__PURE__ */ new Set([
2734
3025
  ...Object.values(LANGUAGE_PROFILES).flatMap((p) => p.skipDirs)
2735
3026
  ]);
2736
3027
  var ALLOWED_BINARIES = new Set(
2737
- Object.values(LANGUAGE_PROFILES).flatMap((profile2) => [
2738
- ...profile2.packageManagers.flatMap((pm) => [
3028
+ Object.values(LANGUAGE_PROFILES).flatMap((profile) => [
3029
+ ...profile.packageManagers.flatMap((pm) => [
2739
3030
  ...pm.installSteps.map((s) => s.argv[0]),
2740
3031
  ...pm.ingest.kind === "auto" ? [pm.ingest.argv[0]] : []
2741
3032
  ]),
2742
- ...profile2.verification.map((v) => v.argv[0])
3033
+ ...profile.verification.map((v) => v.argv[0])
2743
3034
  ])
2744
3035
  );
2745
3036
  var JS_PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun"]);
@@ -2755,13 +3046,13 @@ function resolveDeclaredManifest(root, packageManager) {
2755
3046
  return packageManager;
2756
3047
  }
2757
3048
  const present = [dependency.file, ...dependency.alternatives].find(
2758
- (file) => existsSync2(join9(root, file))
3049
+ (file) => existsSync2(join8(root, file))
2759
3050
  );
2760
3051
  if (!present || present === dependency.file) return packageManager;
2761
3052
  return { ...packageManager, dependency: { ...dependency, file: present } };
2762
3053
  }
2763
3054
  async function manifestPresent(root, manifest, listing) {
2764
- if (!manifest.startsWith("*.")) return existsSync2(join9(root, manifest));
3055
+ if (!manifest.startsWith("*.")) return existsSync2(join8(root, manifest));
2765
3056
  if (!listing.entries) {
2766
3057
  const entries = await readdir2(root).catch(() => []);
2767
3058
  listing.entries = Array.isArray(entries) ? entries : [];
@@ -2769,8 +3060,8 @@ async function manifestPresent(root, manifest, listing) {
2769
3060
  const suffix = manifest.slice(1);
2770
3061
  return listing.entries.some((e) => e.endsWith(suffix));
2771
3062
  }
2772
- async function profileManifestPresent(root, profile2, listing) {
2773
- for (const manifest of profile2.manifests) {
3063
+ async function profileManifestPresent(root, profile, listing) {
3064
+ for (const manifest of profile.manifests) {
2774
3065
  if (await manifestPresent(root, manifest, listing)) return true;
2775
3066
  }
2776
3067
  return false;
@@ -2778,13 +3069,13 @@ async function profileManifestPresent(root, profile2, listing) {
2778
3069
  async function detectProfilesFromManifests(root) {
2779
3070
  const listing = {};
2780
3071
  const found = [];
2781
- for (const profile2 of Object.values(LANGUAGE_PROFILES)) {
2782
- if (await profileManifestPresent(root, profile2, listing)) found.push(profile2);
3072
+ for (const profile of Object.values(LANGUAGE_PROFILES)) {
3073
+ if (await profileManifestPresent(root, profile, listing)) found.push(profile);
2783
3074
  }
2784
3075
  return found;
2785
3076
  }
2786
- async function hasProfileManifest(root, profile2) {
2787
- return profileManifestPresent(root, profile2, {});
3077
+ async function hasProfileManifest(root, profile) {
3078
+ return profileManifestPresent(root, profile, {});
2788
3079
  }
2789
3080
  async function pickIngestionCandidates(root, confirmedNames) {
2790
3081
  const confirmed3 = confirmedNames.map((name) => resolveLanguageProfile(name)).filter((p) => p !== void 0);
@@ -2797,27 +3088,27 @@ async function pickIngestionCandidates(root, confirmedNames) {
2797
3088
  ];
2798
3089
  return { candidates, confirmed: confirmed3, onDisk };
2799
3090
  }
2800
- async function resolveToolchain(root, profile2) {
2801
- const matched = profile2.packageManagers.find(
3091
+ async function resolveToolchain(root, profile) {
3092
+ const matched = profile.packageManagers.find(
2802
3093
  (pm) => [...pm.lockfiles ?? [], ...pm.detectFiles ?? []].some(
2803
- (f) => existsSync2(join9(root, f))
3094
+ (f) => existsSync2(join8(root, f))
2804
3095
  )
2805
3096
  );
2806
3097
  const packageManager = resolveDeclaredManifest(
2807
3098
  root,
2808
- matched ?? profile2.packageManagers[0]
3099
+ matched ?? profile.packageManagers[0]
2809
3100
  );
2810
3101
  let { installSteps, ingest } = packageManager;
2811
3102
  installSteps = installSteps.map(
2812
- (step) => isWorktreeRelativeCommand(step.argv[0]) ? { ...step, argv: withCommand(step.argv, join9(root, step.argv[0])) } : step
3103
+ (step) => isWorktreeRelativeCommand(step.argv[0]) ? { ...step, argv: withCommand(step.argv, join8(root, step.argv[0])) } : step
2813
3104
  );
2814
3105
  if (ingest.kind === "auto" && isWorktreeRelativeCommand(ingest.argv[0])) {
2815
3106
  ingest = {
2816
3107
  ...ingest,
2817
- argv: withCommand(ingest.argv, join9(root, ingest.argv[0]))
3108
+ argv: withCommand(ingest.argv, join8(root, ingest.argv[0]))
2818
3109
  };
2819
3110
  }
2820
- if (profile2.id === "javascript") {
3111
+ if (profile.id === "javascript") {
2821
3112
  const pm = await detectPackageManager(root);
2822
3113
  if (JS_PACKAGE_MANAGERS.has(pm)) {
2823
3114
  installSteps = installSteps.map((step) => ({
@@ -2829,7 +3120,7 @@ async function resolveToolchain(root, profile2) {
2829
3120
  }
2830
3121
  }
2831
3122
  }
2832
- return { profile: profile2, packageManager, installSteps, ingest };
3123
+ return { profile, packageManager, installSteps, ingest };
2833
3124
  }
2834
3125
  function resolveIngestArgv(ingest, entrypoint) {
2835
3126
  if (ingest.kind !== "auto") {
@@ -2843,14 +3134,14 @@ function describeIngestCommand(ingest, entrypoint) {
2843
3134
  if (ingest.kind !== "auto") return ingest.runCommand;
2844
3135
  return ingest.argv.map((part) => part === ENTRYPOINT_TOKEN ? shellQuote(entrypoint) : part).join(" ");
2845
3136
  }
2846
- function ingestScriptDir(profile2) {
2847
- const parts = profile2.ingestEntrypointExample.split("/");
3137
+ function ingestScriptDir(profile) {
3138
+ const parts = profile.ingestEntrypointExample.split("/");
2848
3139
  return parts.slice(0, -1).join("/") || ".";
2849
3140
  }
2850
3141
  function dependencyInstruction(toolchain) {
2851
- const { profile: profile2, packageManager } = toolchain;
2852
- const { packageName, versionPin } = profile2.sdk;
2853
- const also = profile2.sdk.alsoRequires ? ` ${profile2.sdk.alsoRequires}` : "";
3142
+ const { profile, packageManager } = toolchain;
3143
+ const { packageName, versionPin } = profile.sdk;
3144
+ const also = profile.sdk.alsoRequires ? ` ${profile.sdk.alsoRequires}` : "";
2854
3145
  switch (packageManager.dependency.mode) {
2855
3146
  case "wizard-installs":
2856
3147
  return `The wizard installs ${packageName} ${versionPin} in the worktree after you finish \u2014 import it directly and do not edit dependency manifests for it.${also}`;
@@ -2867,7 +3158,7 @@ async function walkFiles(dir) {
2867
3158
  const out = [];
2868
3159
  for (const e of await readdir3(dir, { withFileTypes: true })) {
2869
3160
  if (e.name.startsWith(".") || ALL_SKIP_DIRS.has(e.name)) continue;
2870
- const full = join10(dir, e.name);
3161
+ const full = join9(dir, e.name);
2871
3162
  if (e.isDirectory()) out.push(...await walkFiles(full));
2872
3163
  else if (e.isFile()) out.push(full);
2873
3164
  }
@@ -2876,9 +3167,9 @@ async function walkFiles(dir) {
2876
3167
  function searchFilesTool(ctx) {
2877
3168
  return tool7({
2878
3169
  description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
2879
- inputSchema: z10.object({
2880
- query: z10.string().describe("JavaScript RegExp pattern to search for"),
2881
- path: z10.string().optional().describe("Directory to search in (default: cwd)")
3170
+ inputSchema: z12.object({
3171
+ query: z12.string().describe("JavaScript RegExp pattern to search for"),
3172
+ path: z12.string().optional().describe("Directory to search in (default: cwd)")
2882
3173
  }),
2883
3174
  execute: async ({ query, path = "." }) => {
2884
3175
  logger.info({ query, path }, "called searchFiles tool");
@@ -2900,7 +3191,7 @@ function searchFilesTool(ctx) {
2900
3191
  for (const file of await walkFiles(resolved.target)) {
2901
3192
  let content;
2902
3193
  try {
2903
- content = await readFile7(file, "utf8");
3194
+ content = await readFile6(file, "utf8");
2904
3195
  } catch {
2905
3196
  continue;
2906
3197
  }
@@ -2922,11 +3213,11 @@ function searchFilesTool(ctx) {
2922
3213
 
2923
3214
  // src/lib/tools/verifyImplementation.ts
2924
3215
  import { tool as tool8 } from "ai";
2925
- import z11 from "zod";
3216
+ import z13 from "zod";
2926
3217
 
2927
3218
  // src/lib/tools/repoVerification.ts
2928
3219
  import { existsSync as existsSync3 } from "node:fs";
2929
- import { join as join11 } from "node:path";
3220
+ import { join as join10 } from "node:path";
2930
3221
 
2931
3222
  // src/lib/tools/utils/runCommand.ts
2932
3223
  import { spawn as spawn2 } from "node:child_process";
@@ -3024,13 +3315,13 @@ async function runRepoVerificationCheck(languages = [DEFAULT_LANGUAGE_ID]) {
3024
3315
  else limitations.push(result.limitation);
3025
3316
  continue;
3026
3317
  }
3027
- const profile2 = LANGUAGE_PROFILES[id];
3028
- const runnable = profile2.verification.filter(
3029
- (spec) => !spec.requiresFile || existsSync3(join11(process.cwd(), spec.requiresFile))
3318
+ const profile = LANGUAGE_PROFILES[id];
3319
+ const runnable = profile.verification.filter(
3320
+ (spec) => !spec.requiresFile || existsSync3(join10(process.cwd(), spec.requiresFile))
3030
3321
  );
3031
3322
  if (runnable.length === 0) {
3032
3323
  limitations.push(
3033
- `No mechanical verification available for ${profile2.displayName} in this repo.`
3324
+ `No mechanical verification available for ${profile.displayName} in this repo.`
3034
3325
  );
3035
3326
  continue;
3036
3327
  }
@@ -3060,7 +3351,7 @@ async function runRepoVerificationCheck(languages = [DEFAULT_LANGUAGE_ID]) {
3060
3351
  function verifyImplementationTool(ctx) {
3061
3352
  return tool8({
3062
3353
  description: "Run the repo's mechanical verification checks for generated implementation changes. Uses the conventions of the repo's languages (package.json lint/typecheck/check scripts for JavaScript, the equivalent compile/analyze command elsewhere) and returns structured pass/fail evidence for the verifier to interpret.",
3063
- inputSchema: z11.object(),
3354
+ inputSchema: z13.object(),
3064
3355
  execute: async () => {
3065
3356
  logger.info({ languages: ctx.languages }, "called verifyImplementation tool");
3066
3357
  return runRepoVerificationCheck(ctx.languages);
@@ -3074,7 +3365,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
3074
3365
  import { nanoid as nanoid2 } from "nanoid";
3075
3366
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
3076
3367
  import { dirname as dirname6 } from "node:path";
3077
- import z12 from "zod";
3368
+ import z14 from "zod";
3078
3369
  var DATA_DIR = ".algolia-wizard/data";
3079
3370
  var RECORD_MODEL = "claude-haiku-4-5";
3080
3371
  var MAX_RECORDS = 100;
@@ -3086,17 +3377,17 @@ var anthropic = createAnthropic({
3086
3377
  function generateRecordTool(ctx) {
3087
3378
  return tool9({
3088
3379
  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.",
3089
- inputSchema: z12.object({
3090
- entityName: z12.string().describe("Name of the entity to generate records for."),
3091
- attributes: z12.array(z12.string()).describe("Attribute names each record must contain."),
3092
- count: z12.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
3093
- hint: z12.string().optional().describe("Optional context to steer realistic values.")
3380
+ inputSchema: z14.object({
3381
+ entityName: z14.string().describe("Name of the entity to generate records for."),
3382
+ attributes: z14.array(z14.string()).describe("Attribute names each record must contain."),
3383
+ count: z14.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
3384
+ hint: z14.string().optional().describe("Optional context to steer realistic values.")
3094
3385
  }),
3095
3386
  execute: async ({ entityName, attributes, count, hint }) => {
3096
3387
  logger.info({ entityName, count }, "called generateRecord tool");
3097
3388
  try {
3098
- const value = z12.union([z12.string(), z12.number(), z12.boolean(), z12.null()]);
3099
- const recordSchema = z12.object(
3389
+ const value = z14.union([z14.string(), z14.number(), z14.boolean(), z14.null()]);
3390
+ const recordSchema = z14.object(
3100
3391
  Object.fromEntries(attributes.map((attr) => [attr, value]))
3101
3392
  );
3102
3393
  const generateBatch = async (batchCount) => {
@@ -3106,8 +3397,8 @@ function generateRecordTool(ctx) {
3106
3397
  const { output } = await generateText({
3107
3398
  model: anthropic(RECORD_MODEL),
3108
3399
  output: Output.object({
3109
- schema: z12.object({
3110
- records: z12.array(recordSchema).length(batchCount)
3400
+ schema: z14.object({
3401
+ records: z14.array(recordSchema).length(batchCount)
3111
3402
  })
3112
3403
  }),
3113
3404
  prompt: [
@@ -3165,12 +3456,12 @@ function generateRecordTool(ctx) {
3165
3456
 
3166
3457
  // src/lib/tools/notifyUser.ts
3167
3458
  import { tool as tool10 } from "ai";
3168
- import z13 from "zod";
3459
+ import z15 from "zod";
3169
3460
  function notifyUserTool() {
3170
3461
  return tool10({
3171
3462
  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.`,
3172
- inputSchema: z13.object({
3173
- message: z13.string().describe(
3463
+ inputSchema: z15.object({
3464
+ message: z15.string().describe(
3174
3465
  "Short, plain-language description of what you are doing now."
3175
3466
  )
3176
3467
  }),
@@ -3355,10 +3646,10 @@ async function runAgent(req) {
3355
3646
  }
3356
3647
 
3357
3648
  // src/actions/detectLanguage.ts
3358
- import z16 from "zod";
3359
- var detectLanguageSchema = z16.object({
3360
- languages: z16.array(z16.object({ name: z16.string(), version: z16.string() })),
3361
- frameworks: z16.array(z16.object({ name: z16.string(), version: z16.string() }))
3649
+ import z18 from "zod";
3650
+ var detectLanguageSchema = z18.object({
3651
+ languages: z18.array(z18.object({ name: z18.string(), version: z18.string() })),
3652
+ frameworks: z18.array(z18.object({ name: z18.string(), version: z18.string() }))
3362
3653
  });
3363
3654
  var detectLanguage = () => runAgent({
3364
3655
  instructions: [
@@ -3379,31 +3670,31 @@ var detectLanguage = () => runAgent({
3379
3670
  });
3380
3671
 
3381
3672
  // src/actions/analyzeCodebase.ts
3382
- import z17 from "zod";
3673
+ import z19 from "zod";
3383
3674
  var READONLY_TOOLS = [
3384
3675
  "listFiles",
3385
3676
  "changeDirectory",
3386
3677
  "readFile",
3387
3678
  "searchFiles"
3388
3679
  ];
3389
- var ingestionAnalysisSchema = z17.object({
3390
- ingestionAnalysis: z17.array(
3391
- z17.object({
3392
- name: z17.string(),
3393
- paths: z17.array(z17.string()),
3680
+ var ingestionAnalysisSchema = z19.object({
3681
+ ingestionAnalysis: z19.array(
3682
+ z19.object({
3683
+ name: z19.string(),
3684
+ paths: z19.array(z19.string()),
3394
3685
  // indexable fields the agent found for this entity
3395
- attributes: z17.array(z17.string())
3686
+ attributes: z19.array(z19.string())
3396
3687
  })
3397
3688
  )
3398
3689
  });
3399
- var searchImplementationAnalysisSchema = z17.object({
3400
- searchImplementationAnalysis: z17.string()
3690
+ var searchImplementationAnalysisSchema = z19.object({
3691
+ searchImplementationAnalysis: z19.string()
3401
3692
  });
3402
- var verificationSchema = z17.object({
3403
- verification: z17.array(z17.string())
3693
+ var verificationSchema = z19.object({
3694
+ verification: z19.array(z19.string())
3404
3695
  });
3405
3696
  var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
3406
- var analyzeCodebaseSchema = z17.object({
3697
+ var analyzeCodebaseSchema = z19.object({
3407
3698
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
3408
3699
  searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
3409
3700
  verification: verificationSchema.shape.verification.optional(),
@@ -3467,7 +3758,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3467
3758
  // package.json
3468
3759
  var package_default = {
3469
3760
  name: "@algolia/wizard",
3470
- version: "0.6.0-rc.51.26",
3761
+ version: "0.6.0-rc.53.27",
3471
3762
  description: "Magically implement Algolia functionality in your codebase",
3472
3763
  type: "module",
3473
3764
  engines: {
@@ -3515,7 +3806,6 @@ var package_default = {
3515
3806
  dependencies: {
3516
3807
  "@ai-sdk/anthropic": "^3.0.81",
3517
3808
  "@ai-sdk/openai-compatible": "^2.0.47",
3518
- "@algolia/cli": "^5.11.0",
3519
3809
  "@hono/node-server": "^2.0.10",
3520
3810
  "@mishieck/ink-titled-box": "^0.4.2",
3521
3811
  "@segment/analytics-node": "^3.1.0",
@@ -3530,7 +3820,6 @@ var package_default = {
3530
3820
  nanoid: "^5.1.15",
3531
3821
  pino: "^10.3.1",
3532
3822
  react: "^19.2.7",
3533
- toml: "^4.1.1",
3534
3823
  varlock: "^1.5.1",
3535
3824
  zod: "^4.4.3",
3536
3825
  zustand: "^5.0.14"
@@ -3572,8 +3861,8 @@ function parseEntries(raw) {
3572
3861
  var summarize = (entries) => entries.length ? entries.map((e) => e.name).join(", ") : "none";
3573
3862
 
3574
3863
  // src/actions/confirmLanguage.ts
3575
- import z19 from "zod";
3576
- var confirmLanguageSchema = z19.object({
3864
+ import z21 from "zod";
3865
+ var confirmLanguageSchema = z21.object({
3577
3866
  languages: detectLanguageSchema.shape.languages
3578
3867
  });
3579
3868
  var OTHER_OPTION = "Other";
@@ -3643,7 +3932,7 @@ async function confirmLanguage(ctx) {
3643
3932
  }
3644
3933
 
3645
3934
  // src/actions/confirmFramework.ts
3646
- import z20 from "zod";
3935
+ import z22 from "zod";
3647
3936
 
3648
3937
  // src/lib/frameworks.ts
3649
3938
  var BACKEND_ONLY_FRAMEWORK = "Backend only / API";
@@ -3744,7 +4033,7 @@ function describeSearchTarget(strategy, frameworkName) {
3744
4033
  }
3745
4034
 
3746
4035
  // src/actions/confirmFramework.ts
3747
- var confirmFrameworkSchema = z20.object({
4036
+ var confirmFrameworkSchema = z22.object({
3748
4037
  frameworks: detectLanguageSchema.shape.frameworks
3749
4038
  });
3750
4039
  var OTHER_OPTION2 = "Other";
@@ -3842,8 +4131,8 @@ async function promptUser(ctx, params) {
3842
4131
  }
3843
4132
 
3844
4133
  // src/actions/confirmEntities.ts
3845
- import z21 from "zod";
3846
- var confirmEntitiesSchema = z21.object({
4134
+ import z23 from "zod";
4135
+ var confirmEntitiesSchema = z23.object({
3847
4136
  // Final detection — the focused re-run may supersede project-scan's.
3848
4137
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
3849
4138
  confirmedEntities: confirmedEntitiesFieldSchema
@@ -3913,15 +4202,15 @@ async function confirmEntities(ctx) {
3913
4202
  }
3914
4203
 
3915
4204
  // src/actions/review.ts
3916
- import { z as z22 } from "zod";
3917
- var reviewSchema = z22.object({
4205
+ import { z as z24 } from "zod";
4206
+ var reviewSchema = z24.object({
3918
4207
  // Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
3919
4208
  // not one entry per workflow step — a step's raw output can be a long,
3920
4209
  // multi-paragraph blob (see implement.ts's summaries.join), and mirroring
3921
4210
  // that 1:1 is what made the old per-step summary an unreadable wall of text.
3922
- summaryPoints: z22.array(z22.string()),
3923
- reviewPrompt: z22.string(),
3924
- nextSteps: z22.array(z22.string())
4211
+ summaryPoints: z24.array(z24.string()),
4212
+ reviewPrompt: z24.string(),
4213
+ nextSteps: z24.array(z24.string())
3925
4214
  });
3926
4215
  function formatCompletedSteps(steps) {
3927
4216
  if (!steps.length) return "(no prior steps completed)";
@@ -3972,17 +4261,17 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
3972
4261
  };
3973
4262
 
3974
4263
  // src/actions/implement.ts
3975
- import z24 from "zod";
4264
+ import z25 from "zod";
3976
4265
 
3977
4266
  // src/lib/worktree.ts
3978
4267
  import { execFile } from "node:child_process";
3979
4268
  import { existsSync as existsSync4 } from "node:fs";
3980
- import { copyFile, mkdir as mkdir6, readdir as readdir4, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
4269
+ import { copyFile, mkdir as mkdir6, readdir as readdir4, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3981
4270
  import {
3982
4271
  basename as basename2,
3983
4272
  dirname as dirname7,
3984
4273
  isAbsolute as isAbsolute2,
3985
- join as join12,
4274
+ join as join11,
3986
4275
  relative as relative2,
3987
4276
  resolve as resolve3
3988
4277
  } from "node:path";
@@ -4016,7 +4305,7 @@ async function isWorkingTreeDirty(repoRoot) {
4016
4305
  return out.trim().length > 0;
4017
4306
  }
4018
4307
  async function pruneOldWorktrees(repoRoot) {
4019
- const dir = join12(stateDir(repoRoot), "worktrees");
4308
+ const dir = join11(stateDir(repoRoot), "worktrees");
4020
4309
  const stale = (await readdir4(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
4021
4310
  for (const slug of stale) {
4022
4311
  const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
@@ -4027,7 +4316,7 @@ async function pruneOldWorktrees(repoRoot) {
4027
4316
  "worktree",
4028
4317
  "remove",
4029
4318
  "--force",
4030
- join12(dir, slug)
4319
+ join11(dir, slug)
4031
4320
  ]);
4032
4321
  await git(["-C", repoRoot, "branch", "-D", branch]);
4033
4322
  } catch (err) {
@@ -4041,7 +4330,7 @@ async function pruneOldWorktrees(repoRoot) {
4041
4330
  async function createWorktree(repoRoot) {
4042
4331
  const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
4043
4332
  const dirSlug = branch.replace(/\//g, "-");
4044
- const path = join12(stateDir(repoRoot), "worktrees", dirSlug);
4333
+ const path = join11(stateDir(repoRoot), "worktrees", dirSlug);
4045
4334
  await git(["-C", repoRoot, "worktree", "prune"]);
4046
4335
  await pruneOldWorktrees(repoRoot);
4047
4336
  await mkdir6(dirname7(path), { recursive: true });
@@ -4056,24 +4345,24 @@ async function spawnStep(worktreePath, argv) {
4056
4345
  return { ok: code === 0, output: output.trim() };
4057
4346
  }
4058
4347
  async function installWorktreeDeps(worktreePath, toolchain) {
4059
- const { profile: profile2, installSteps, packageManager } = toolchain;
4348
+ const { profile, installSteps, packageManager } = toolchain;
4060
4349
  const declared = packageManager.dependency.mode === "agent-declares" ? packageManager.dependency.file : void 0;
4061
- const haveSomethingToInstall = await hasProfileManifest(worktreePath, profile2) || declared !== void 0 && existsSync4(join12(worktreePath, declared));
4350
+ const haveSomethingToInstall = await hasProfileManifest(worktreePath, profile) || declared !== void 0 && existsSync4(join11(worktreePath, declared));
4062
4351
  if (!haveSomethingToInstall) {
4063
4352
  return {
4064
4353
  ok: true,
4065
- output: `no ${profile2.displayName} manifest; skipped install`
4354
+ output: `no ${profile.displayName} manifest; skipped install`
4066
4355
  };
4067
4356
  }
4068
4357
  if (installSteps.length === 0) {
4069
4358
  return {
4070
4359
  ok: true,
4071
- output: `${profile2.displayName} (${toolchain.packageManager.id}) has no wizard-run install step`
4360
+ output: `${profile.displayName} (${toolchain.packageManager.id}) has no wizard-run install step`
4072
4361
  };
4073
4362
  }
4074
4363
  const outputs = [];
4075
4364
  for (const step of installSteps) {
4076
- if (step.requiresFile && !existsSync4(join12(worktreePath, step.requiresFile)))
4365
+ if (step.requiresFile && !existsSync4(join11(worktreePath, step.requiresFile)))
4077
4366
  continue;
4078
4367
  const result = await spawnStep(worktreePath, step.argv);
4079
4368
  if (result.output) outputs.push(result.output);
@@ -4113,13 +4402,13 @@ function validateIngestEntrypoint(worktreePath, entrypoint, allowedExtensions) {
4113
4402
  return { ok: true, target };
4114
4403
  }
4115
4404
  async function runIngestScript(worktreePath, toolchain, entrypoint, env = {}) {
4116
- const { ingest, profile: profile2, packageManager } = toolchain;
4405
+ const { ingest, profile, packageManager } = toolchain;
4117
4406
  if (ingest.kind !== "auto") {
4118
4407
  return {
4119
4408
  ran: false,
4120
4409
  ok: false,
4121
4410
  output: "",
4122
- reason: `${profile2.displayName} (${packageManager.id}) projects must be run manually: ${ingest.runCommand}`
4411
+ reason: `${profile.displayName} (${packageManager.id}) projects must be run manually: ${ingest.runCommand}`
4123
4412
  };
4124
4413
  }
4125
4414
  const validated = validateIngestEntrypoint(
@@ -4168,8 +4457,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
4168
4457
  } catch {
4169
4458
  return { ok: false, reason: `"${sourcePath}" does not exist` };
4170
4459
  }
4171
- const relPath = join12(ingestDir, basename2(source));
4172
- const dest = join12(worktreePath, relPath);
4460
+ const relPath = join11(ingestDir, basename2(source));
4461
+ const dest = join11(worktreePath, relPath);
4173
4462
  try {
4174
4463
  await mkdir6(dirname7(dest), { recursive: true });
4175
4464
  await copyFile(source, dest);
@@ -4185,10 +4474,10 @@ function hasEnvVar(content, name) {
4185
4474
  return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
4186
4475
  }
4187
4476
  async function writeSearchEnvValues(worktreePath, vars) {
4188
- const target = join12(worktreePath, ".env");
4477
+ const target = join11(worktreePath, ".env");
4189
4478
  let existing = "";
4190
4479
  try {
4191
- existing = await readFile8(target, "utf8");
4480
+ existing = await readFile7(target, "utf8");
4192
4481
  } catch (err) {
4193
4482
  if (err.code !== "ENOENT") throw err;
4194
4483
  }
@@ -4256,63 +4545,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
4256
4545
  }
4257
4546
  }
4258
4547
 
4259
- // src/lib/algoliaApiKey.ts
4260
- import { z as z23 } from "zod";
4261
- var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
4262
- var apiKeySchema = z23.object({
4263
- value: z23.string().min(1),
4264
- acl: z23.array(z23.string()).default([]),
4265
- indexes: z23.array(z23.string()).default([])
4266
- });
4267
- var apiKeyListSchema = z23.object({
4268
- items: z23.array(apiKeySchema).optional(),
4269
- keys: z23.array(apiKeySchema).optional()
4270
- }).transform((o) => o.items ?? o.keys ?? []);
4271
- var createdKeySchema = z23.object({
4272
- key: z23.string().min(1).optional(),
4273
- value: z23.string().min(1).optional()
4274
- });
4275
- function canReuse(key, index) {
4276
- return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
4277
- }
4278
- async function createSearchKey(index) {
4279
- const stdout = await runAlgoliaCli([
4280
- "apikeys",
4281
- "create",
4282
- "--indices",
4283
- index,
4284
- "--acl",
4285
- "search,browse",
4286
- "--description",
4287
- `wizard search-only key for ${index}`,
4288
- "-o",
4289
- "json"
4290
- ]);
4291
- const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
4292
- const created = key ?? value;
4293
- if (!created) throw new Error("apikeys create returned no key value");
4294
- return created;
4295
- }
4296
- async function resolveSearchOnlyKey(index) {
4297
- const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
4298
- const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
4299
- if (existing) {
4300
- logger.info({ index }, "reusing existing search-only API key");
4301
- return existing;
4302
- }
4303
- logger.info({ index }, "no reusable search-only key found; creating one");
4304
- return createSearchKey(index);
4305
- }
4306
-
4307
4548
  // src/lib/algoliaDocs.ts
4308
4549
  import { readFileSync, existsSync as existsSync5 } from "node:fs";
4309
- import { dirname as dirname8, join as join13 } from "node:path";
4550
+ import { dirname as dirname8, join as join12 } from "node:path";
4310
4551
  import { fileURLToPath as fileURLToPath2 } from "node:url";
4311
- var DOCS_SUBPATH = join13("docs", "algolia-sdk");
4552
+ var DOCS_SUBPATH = join12("docs", "algolia-sdk");
4312
4553
  function findDocsDir() {
4313
4554
  let dir = dirname8(fileURLToPath2(import.meta.url));
4314
4555
  for (; ; ) {
4315
- const candidate = join13(dir, DOCS_SUBPATH);
4556
+ const candidate = join12(dir, DOCS_SUBPATH);
4316
4557
  if (existsSync5(candidate)) return candidate;
4317
4558
  const parent = dirname8(dir);
4318
4559
  if (parent === dir) return void 0;
@@ -4325,7 +4566,7 @@ function getNamedDoc(name, key) {
4325
4566
  logger.warn("docs/algolia-sdk not found");
4326
4567
  return "";
4327
4568
  }
4328
- const file = join13(docsDir, `${name}-${key}.md`);
4569
+ const file = join12(docsDir, `${name}-${key}.md`);
4329
4570
  if (!existsSync5(file)) {
4330
4571
  logger.warn({ name, key }, "named SDK reference not found");
4331
4572
  return "";
@@ -4334,49 +4575,49 @@ function getNamedDoc(name, key) {
4334
4575
  }
4335
4576
 
4336
4577
  // src/actions/implement.ts
4337
- var implementSchema = z24.object({
4338
- filesChanged: z24.array(z24.string()),
4339
- summary: z24.string(),
4578
+ var implementSchema = z25.object({
4579
+ filesChanged: z25.array(z25.string()),
4580
+ summary: z25.string(),
4340
4581
  // Absolute path to the throwaway worktree holding the generated changes, so
4341
4582
  // the user can open it (`cd <worktreePath>`) or inspect the diff
4342
4583
  // (`git -C <worktreePath> status/diff`).
4343
- worktreePath: z24.string().optional(),
4344
- ingestCommand: z24.string().optional(),
4584
+ worktreePath: z25.string().optional(),
4585
+ ingestCommand: z25.string().optional(),
4345
4586
  // True when the user accepted the run-now prompt and the wizard executed the
4346
4587
  // ingestion script; downstream steps use this to avoid telling the user to run
4347
4588
  // a script that already ran.
4348
- ingestScriptRan: z24.boolean().optional(),
4589
+ ingestScriptRan: z25.boolean().optional(),
4349
4590
  // Records ingested by the run-now execution, parsed from the script's
4350
4591
  // machine-readable count line; absent when the script didn't run or emitted
4351
4592
  // no parseable count.
4352
- ingestRecordCount: z24.number().optional(),
4593
+ ingestRecordCount: z25.number().optional(),
4353
4594
  // Wall-clock duration of the run-now ingestion execution, in ms.
4354
- ingestDurationMs: z24.number().optional(),
4355
- ingestionSource: z24.enum(["local", "fileUpload", "generated"]),
4595
+ ingestDurationMs: z25.number().optional(),
4596
+ ingestionSource: z25.enum(["local", "fileUpload", "generated"]),
4356
4597
  // Suggested names/values, built from framework detection. The search agent is
4357
4598
  // instructed to rename the prefix if it doesn't match the project's build
4358
4599
  // tool, so the names it actually wrote can differ — treat these as hints, not
4359
4600
  // ground truth (the agent's summary carries the final names).
4360
- searchEnvVars: z24.array(
4361
- z24.object({
4362
- name: z24.string(),
4363
- value: z24.string()
4601
+ searchEnvVars: z25.array(
4602
+ z25.object({
4603
+ name: z25.string(),
4604
+ value: z25.string()
4364
4605
  })
4365
4606
  ).optional()
4366
4607
  });
4367
- var implementationOutputSchema = z24.object({
4368
- summary: z24.string(),
4608
+ var implementationOutputSchema = z25.object({
4609
+ summary: z25.string(),
4369
4610
  // Ingestion only: the script the wizard should run, as a bare path — never a
4370
4611
  // command string, and never the interpreter. The command comes from the
4371
4612
  // resolved language toolchain (a registry constant); this path is validated to
4372
4613
  // a worktree-relative file with a runnable extension and substituted into it.
4373
4614
  // So the agent contributes no part of the command that gets executed.
4374
- entrypoint: z24.string().optional()
4615
+ entrypoint: z25.string().optional()
4375
4616
  });
4376
- var verificationOutputSchema = z24.object({
4377
- summary: z24.string(),
4378
- sufficient: z24.boolean(),
4379
- additionalInstructions: z24.string().optional()
4617
+ var verificationOutputSchema = z25.object({
4618
+ summary: z25.string(),
4619
+ sufficient: z25.boolean(),
4620
+ additionalInstructions: z25.string().optional()
4380
4621
  });
4381
4622
  var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
4382
4623
  var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
@@ -4462,18 +4703,18 @@ function sourceSpecificInstructions(input) {
4462
4703
  return byLine[input.ingestionSource];
4463
4704
  }
4464
4705
  function ingestionInstructions(input) {
4465
- const { ingestionProfile: profile2, toolchain } = input;
4706
+ const { ingestionProfile: profile, toolchain } = input;
4466
4707
  const { ingest } = toolchain;
4467
4708
  const extensions = ingest.entrypointExtensions.join(", ");
4468
- const runInstruction = ingest.kind === "auto" ? `Return "entrypoint": the script path relative to the worktree root (e.g. "${profile2.ingestEntrypointExample}"), ending in one of ${extensions}. The wizard runs it with \`${describeIngestCommand(ingest, profile2.ingestEntrypointExample)}\`, so it must be a plain path with no flags or arguments and must run as-is under that command.` : `Return "entrypoint": the script path relative to the worktree root (e.g. "${profile2.ingestEntrypointExample}"), ending in one of ${extensions}. The wizard does NOT run ${profile2.displayName} ${toolchain.packageManager.id} projects itself \u2014 it tells the developer to run \`${describeIngestCommand(ingest, profile2.ingestEntrypointExample)}\`, so also add whatever build configuration that command needs.`;
4709
+ const runInstruction = ingest.kind === "auto" ? `Return "entrypoint": the script path relative to the worktree root (e.g. "${profile.ingestEntrypointExample}"), ending in one of ${extensions}. The wizard runs it with \`${describeIngestCommand(ingest, profile.ingestEntrypointExample)}\`, so it must be a plain path with no flags or arguments and must run as-is under that command.` : `Return "entrypoint": the script path relative to the worktree root (e.g. "${profile.ingestEntrypointExample}"), ending in one of ${extensions}. The wizard does NOT run ${profile.displayName} ${toolchain.packageManager.id} projects itself \u2014 it tells the developer to run \`${describeIngestCommand(ingest, profile.ingestEntrypointExample)}\`, so also add whatever build configuration that command needs.`;
4469
4710
  return [
4470
4711
  ...input.confirmed && input.confirmed.length ? [
4471
- `Write the ingestion script in ${profile2.displayName}, at "${ingestScriptDir(profile2)}/" in the repo.`,
4712
+ `Write the ingestion script in ${profile.displayName}, at "${ingestScriptDir(profile)}/" in the repo.`,
4472
4713
  `Ingest only these confirmed entities (name, source paths, attributes): ${JSON.stringify(input.confirmed)}.`,
4473
- `Ingesting writes to Algolia, so the script needs a write API key and App ID \u2014 read them from the ${API_KEY_VAR} and ${APP_ID_VAR} environment variables rather than hardcoding them. ${profile2.envReadInstruction} The wizard sets these when it runs the script.`,
4474
- `Use the official Algolia ${profile2.displayName} client (${profile2.sdk.packageName}). Do not use the raw HTTP API, and do not use a client for another language.`,
4714
+ `Ingesting writes to Algolia, so the script needs a write API key and App ID \u2014 read them from the ${API_KEY_VAR} and ${APP_ID_VAR} environment variables rather than hardcoding them. ${profile.envReadInstruction} The wizard sets these when it runs the script.`,
4715
+ `Use the official Algolia ${profile.displayName} client (${profile.sdk.packageName}). Do not use the raw HTTP API, and do not use a client for another language.`,
4475
4716
  "After a successful ingest, the script must print exactly one line to stdout in the form `ALGOLIA_WIZARD_RECORD_COUNT=<n>`, where <n> is the total number of records pushed to Algolia. Print it last, on its own line, with no surrounding text.",
4476
- getNamedDoc("save-records", profile2.sdk.docKey),
4717
+ getNamedDoc("save-records", profile.sdk.docKey),
4477
4718
  dependencyInstruction(toolchain),
4478
4719
  "The summary should be extremely concise.",
4479
4720
  runInstruction,
@@ -4497,7 +4738,7 @@ function searchInstructions(input) {
4497
4738
  `It needs at least a working SearchBox and Hits against the "${input.targetIndex}" index.`,
4498
4739
  isTemplate ? "Load InstantSearch from a CDN with script tags as shown in the reference. Do not add JavaScript package dependencies, a bundler, or a build step." : 'Add any Algolia/InstantSearch packages you import to package.json "dependencies" with a valid version range; the wizard installs them in the worktree after you finish.',
4499
4740
  isTemplate ? "Read the App ID and search-only API key from server-side configuration/environment and render them into the page (e.g. as data- attributes the script reads); never hardcode them, and never put a write/admin key in HTML." : "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.",
4500
- // appId always resolves (loadActiveProfile throws otherwise); only the
4741
+ // appId always resolves (requireApplication throws otherwise); only the
4501
4742
  // search-only key is best-effort and can fall back to a placeholder.
4502
4743
  `Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
4503
4744
  // Names are fixed, not the agent's to rename: the wizard writes the
@@ -4650,6 +4891,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4650
4891
  }
4651
4892
  }
4652
4893
  const targetIndex = selected?.selection;
4894
+ useWizard.getState().setTargetIndex(targetIndex ?? null);
4653
4895
  await assertGitRepoWithHead(repoRoot);
4654
4896
  if (await isWorkingTreeDirty(repoRoot)) {
4655
4897
  await confirmDirtyWorkingTree(ctx, repoRoot);
@@ -4660,7 +4902,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4660
4902
  let appId;
4661
4903
  let searchKey;
4662
4904
  if (useCases.includes("search")) {
4663
- appId = (await loadActiveProfile()).appId;
4905
+ appId = (await requireApplication()).id;
4664
4906
  try {
4665
4907
  searchKey = await resolveSearchOnlyKey(targetIndex);
4666
4908
  } catch (err) {
@@ -4810,7 +5052,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4810
5052
  messages: []
4811
5053
  }) === true;
4812
5054
  if (runNow) {
4813
- const profile2 = await loadActiveProfile();
5055
+ const ingestApp = await requireApplication();
5056
+ const writeKey = await resolveWriteKey(targetIndex);
4814
5057
  ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
4815
5058
  const scriptLogId = ctx.logStart("runIngestScript", {
4816
5059
  language: ingestionProfile.id,
@@ -4822,8 +5065,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4822
5065
  toolchain,
4823
5066
  ingestEntrypoint,
4824
5067
  {
4825
- [APP_ID_VAR]: profile2.appId,
4826
- [API_KEY_VAR]: profile2.apiKey
5068
+ [APP_ID_VAR]: ingestApp.id,
5069
+ [API_KEY_VAR]: writeKey
4827
5070
  }
4828
5071
  );
4829
5072
  ctx.logEnd(scriptLogId, run.ok ? "success" : "error");
@@ -5052,8 +5295,8 @@ var defaultWorkflow = {
5052
5295
  defineStep({
5053
5296
  id: "select-index",
5054
5297
  title: "Set up index",
5055
- outputSchema: z25.object({
5056
- selection: z25.string()
5298
+ outputSchema: z26.object({
5299
+ selection: z26.string()
5057
5300
  }),
5058
5301
  run: (ctx) => selectIndexStep(ctx)
5059
5302
  }),
@@ -5134,25 +5377,54 @@ var store = useWizard.getState();
5134
5377
  var instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
5135
5378
  var user = await getUser();
5136
5379
  if (!user) {
5137
- await instance.waitUntilRenderFlush();
5138
- instance.cleanup();
5380
+ store.beginAuth();
5139
5381
  try {
5140
5382
  await runAuthLogin();
5141
5383
  } catch (err) {
5142
- console.error(err instanceof Error ? err.message : String(err));
5143
- process.exit(1);
5384
+ if (!needsInteractiveTerminal(err)) {
5385
+ store.setError(err instanceof Error ? err.message : String(err));
5386
+ await instance.waitUntilExit();
5387
+ process.exit(1);
5388
+ }
5389
+ try {
5390
+ await promptForApplication();
5391
+ } catch (pickErr) {
5392
+ logger.warn(
5393
+ { err: pickErr.message },
5394
+ "in-wizard application selection failed; handing over the terminal"
5395
+ );
5396
+ await instance.waitUntilRenderFlush();
5397
+ instance.cleanup();
5398
+ try {
5399
+ await runAuthLoginInTerminal();
5400
+ } catch (fallbackErr) {
5401
+ console.error(
5402
+ fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)
5403
+ );
5404
+ process.exit(1);
5405
+ }
5406
+ instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
5407
+ store.clearCliOutput();
5408
+ }
5144
5409
  }
5145
- instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
5410
+ store.endAuth();
5146
5411
  user = await getUser();
5147
5412
  if (!user) {
5148
5413
  store.setError(
5149
- "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
5414
+ "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
5150
5415
  );
5151
5416
  await instance.waitUntilExit();
5152
5417
  process.exit(1);
5153
5418
  }
5154
5419
  }
5155
5420
  store.setUser(user);
5156
- var profile = await loadActiveProfile();
5157
5421
  await store.waitForStart();
5158
- runWorkflow(workflow, profile?.appId);
5422
+ var app;
5423
+ try {
5424
+ app = await ensureApplication();
5425
+ } catch (err) {
5426
+ store.setError(err instanceof Error ? err.message : String(err));
5427
+ await instance.waitUntilExit();
5428
+ process.exit(1);
5429
+ }
5430
+ runWorkflow(workflow, app.id);