@algolia/wizard 0.9.0-rc.85.78 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/main.js +539 -759
  3. package/package.json +3 -1
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 Box15, Text as Text15, useApp, useInput as useInput6, useWindowSize as useWindowSize8 } from "ink";
7
+ import { Box as Box14, Text as Text14, useApp, useInput as useInput6, useWindowSize as useWindowSize7 } from "ink";
8
8
 
9
9
  // src/core/store.ts
10
10
  import { create } from "zustand";
@@ -12,86 +12,32 @@ import { nanoid } from "nanoid";
12
12
 
13
13
  // src/lib/algoliaCli.ts
14
14
  import { spawn } from "node:child_process";
15
- import { z } from "zod";
16
- function npxArgs(args) {
17
- return ["--yes", "@algolia/cli@latest", ...args];
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");
18
19
  }
19
- var shell = process.platform === "win32";
20
- function lineSplitter(emit) {
21
- let buffer = "";
22
- return {
23
- push(chunk) {
24
- buffer += chunk;
25
- const lines = buffer.split("\n");
26
- buffer = lines.pop() ?? "";
27
- for (const line of lines) emit(line.replace(/\r$/, ""));
28
- },
29
- flush() {
30
- if (buffer) emit(buffer.replace(/\r$/, ""));
31
- buffer = "";
32
- }
33
- };
34
- }
35
- var wizardSink = (stream, line) => {
36
- if (!line.trim()) return;
37
- useWizard.getState().pushCliOutput(stream, line);
38
- };
39
- var stderrSink = (stream, line) => {
40
- if (stream === "stdout") return;
41
- wizardSink(stream, line);
42
- };
43
- function runAlgoliaCli(args, { onOutput } = {}) {
44
- const store = useWizard.getState();
45
- const logId = store.logStart("tool", `algolia ${args.join(" ")}`);
20
+ function runAlgoliaCli(args) {
46
21
  return new Promise((resolve4, reject) => {
47
- const child = spawn("npx", npxArgs(args), { shell });
22
+ const child = spawn(process.execPath, [algoliaCliEntry(), ...args]);
48
23
  let stdout = "";
49
24
  let stderr = "";
50
- const splitters = {
51
- stdout: lineSplitter((line) => onOutput?.("stdout", line)),
52
- stderr: lineSplitter((line) => onOutput?.("stderr", line))
53
- };
54
- child.stdout.on("data", (chunk) => {
55
- const text = String(chunk);
56
- stdout += text;
57
- splitters.stdout.push(text);
58
- });
59
- child.stderr.on("data", (chunk) => {
60
- const text = String(chunk);
61
- stderr += text;
62
- splitters.stderr.push(text);
63
- });
25
+ child.stdout.on("data", (chunk) => stdout += chunk);
26
+ child.stderr.on("data", (chunk) => stderr += chunk);
64
27
  child.on("error", reject);
65
28
  child.on("close", (code) => {
66
- splitters.stdout.flush();
67
- splitters.stderr.flush();
68
29
  if (code === 0) {
69
30
  resolve4(stdout);
70
31
  } else {
71
- const failed = stderr.trim();
72
- let detail = "";
73
- if (failed) {
74
- detail = `: ${failed}`;
75
- } else if (stdout.trim()) {
76
- detail = " (no stderr; stdout withheld \u2014 it may contain credentials)";
77
- }
32
+ const detail = stderr.trim() || stdout.trim();
78
33
  reject(
79
34
  new Error(
80
- `Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail}`
35
+ `Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail ? `: ${detail}` : ""}`
81
36
  )
82
37
  );
83
38
  }
84
39
  });
85
- }).then(
86
- (out) => {
87
- useWizard.getState().logEnd(logId, "success");
88
- return out;
89
- },
90
- (err) => {
91
- useWizard.getState().logEnd(logId, "error");
92
- throw err;
93
- }
94
- );
40
+ });
95
41
  }
96
42
  async function getUser() {
97
43
  let raw;
@@ -106,23 +52,19 @@ async function getUser() {
106
52
  return null;
107
53
  }
108
54
  }
109
- var loginResultSchema = z.object({
110
- success: z.boolean(),
111
- email: z.string().optional()
112
- });
113
- async function runAuthLogin() {
114
- const raw = await runAlgoliaCli(["auth", "login", "--non-interactive"], {
115
- onOutput: stderrSink
55
+ function runAuthLogin() {
56
+ return new Promise((resolve4, reject) => {
57
+ const child = spawn(
58
+ process.execPath,
59
+ [algoliaCliEntry(), "auth", "login", "--default"],
60
+ { stdio: "inherit" }
61
+ );
62
+ child.on("error", reject);
63
+ child.on("close", (code) => {
64
+ if (code === 0) resolve4();
65
+ else reject(new Error(`Algolia authentication failed (exit ${code}).`));
66
+ });
116
67
  });
117
- let parsed;
118
- try {
119
- parsed = loginResultSchema.safeParse(JSON.parse(raw));
120
- } catch {
121
- parsed = void 0;
122
- }
123
- if (parsed?.success && !parsed.data.success) {
124
- throw new Error("Algolia sign-in did not report success.");
125
- }
126
68
  }
127
69
 
128
70
  // src/lib/auth.ts
@@ -229,7 +171,6 @@ function describeInputValue(value) {
229
171
  return Array.isArray(value) ? value.join(", ") : value;
230
172
  }
231
173
  var NOTICE_INTERVAL_MS = 2e3;
232
- var CLI_OUTPUT_LIMIT = 200;
233
174
  var useWizard = create((set, get) => ({
234
175
  phase: "idle",
235
176
  homeScreen: "home",
@@ -241,21 +182,22 @@ var useWizard = create((set, get) => ({
241
182
  notices: [],
242
183
  _noticeQueue: [],
243
184
  _noticeTimer: null,
244
- cliOutput: [],
245
- targetIndex: null,
246
185
  logs: [],
247
186
  error: null,
248
187
  inputReq: null,
249
188
  _resolve: null,
250
- // `endAuth` lands on 'preflight', not 'idle': sign-in happens after the
251
- // welcome screen, so going back would gate the run a second time.
252
- beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
253
- endAuth: () => set((s) => s.phase === "authenticating" ? { phase: "preflight" } : {}),
189
+ // Advances past the welcome screen. Only meaningful from 'idle' once the
190
+ // workflow is running there's nothing left to confirm.
191
+ // Reset `homeScreen` so preflight shows Welcome, not the Learn more sub-view.
254
192
  confirmStart: () => set(
255
193
  (s) => s.phase === "idle" ? { phase: "preflight", homeScreen: "home" } : {}
256
194
  ),
195
+ // Welcome sub-view navigation; leaves `phase` untouched so the workflow stays paused.
257
196
  openLearnMore: () => set({ homeScreen: "learnMore" }),
258
197
  backToHome: () => set({ homeScreen: "home" }),
198
+ // Resolves once the phase leaves 'idle', whether that happens before or
199
+ // after this is called (the welcome screen's enter handler is what
200
+ // drives the transition via `confirmStart`).
259
201
  waitForStart: () => new Promise((resolve4) => {
260
202
  if (get().phase !== "idle") {
261
203
  resolve4();
@@ -278,19 +220,15 @@ var useWizard = create((set, get) => ({
278
220
  syncSteps: (steps, currentStepIndex) => set({ steps, currentStepIndex }),
279
221
  setActiveStep: (index) => {
280
222
  get()._clearNoticeQueue();
281
- set({
282
- phase: "running",
283
- currentStepIndex: index,
284
- output: "",
285
- notices: [],
286
- cliOutput: []
287
- });
223
+ set({ phase: "running", currentStepIndex: index, output: "", notices: [] });
288
224
  },
289
225
  setUser: (user) => set({ user }),
290
226
  appendToken: (text) => set((s) => ({ output: s.output + text })),
291
227
  clearOutput: () => set({ output: "" }),
292
- // The timer stays armed through an empty drain, so the spacing covers the
293
- // time since the last render even across bursts.
228
+ // Renders the first notice of a burst immediately, then holds later
229
+ // arrivals in `_noticeQueue` and drains one per `NOTICE_INTERVAL_MS` —
230
+ // the timer stays armed through an empty drain so the cooldown always
231
+ // covers the time since the last render, even across bursts.
294
232
  pushNotice: (notice) => {
295
233
  const { notices, _noticeQueue, _noticeTimer } = get();
296
234
  if (_noticeTimer === null) {
@@ -323,13 +261,6 @@ var useWizard = create((set, get) => ({
323
261
  get()._clearNoticeQueue();
324
262
  set({ notices: [] });
325
263
  },
326
- pushCliOutput: (stream, text) => set((s) => ({
327
- cliOutput: [...s.cliOutput, { id: nanoid(), stream, text }].slice(
328
- -CLI_OUTPUT_LIMIT
329
- )
330
- })),
331
- clearCliOutput: () => set({ cliOutput: [] }),
332
- setTargetIndex: (index) => set({ targetIndex: index }),
333
264
  logStart: (kind, name, input) => {
334
265
  const id = nanoid();
335
266
  set((s) => ({
@@ -352,6 +283,9 @@ var useWizard = create((set, get) => ({
352
283
  _resolve: resolve4
353
284
  });
354
285
  }),
286
+ // Logs what the user picked — not the prompt text that was shown, which
287
+ // may repeat or duplicate on-screen content and isn't the useful signal
288
+ // here.
355
289
  submitInput: async (value) => {
356
290
  await markInteraction();
357
291
  get()._resolve?.(value);
@@ -371,8 +305,6 @@ var useWizard = create((set, get) => ({
371
305
  currentStepIndex: 0,
372
306
  output: "",
373
307
  notices: [],
374
- cliOutput: [],
375
- targetIndex: null,
376
308
  logs: [],
377
309
  error: null,
378
310
  inputReq: null,
@@ -381,100 +313,16 @@ var useWizard = create((set, get) => ({
381
313
  }
382
314
  }));
383
315
 
384
- // src/ui/CliOutput.tsx
385
- import { Box, Text, useWindowSize } from "ink";
386
-
387
- // src/ui/theme.ts
388
- var MARKER = {
389
- pending: "\u25CB",
390
- running: "\u25D0",
391
- done: "\u2713",
392
- error: "\u2716"
393
- };
394
- var BRAND = "#003DFF";
395
- var SECONDARY = "#5468FF";
396
- var DANGER = "#F86E7E";
397
- var COLORS = {
398
- brand: BRAND,
399
- primary: "#E6EDF3",
400
- secondary: SECONDARY,
401
- strong: "#FFFFFF",
402
- muted: "#8B949E",
403
- dim: "#484F58",
404
- highlight: { bg: "#12331C", fg: "#4ADE80" },
405
- badge: "#E3B341",
406
- danger: DANGER,
407
- success: "#4ADE80",
408
- bg: {
409
- main: "#0B0E14",
410
- sidebar: "#14171E"
411
- },
412
- border: "#30363D",
413
- accent: "#76A0FF",
414
- status: {
415
- pending: "gray",
416
- running: "#76A0FF",
417
- done: "#4ADE80",
418
- error: DANGER
419
- }
420
- };
421
-
422
- // src/ui/CliOutput.tsx
423
- import { jsxs } from "react/jsx-runtime";
424
- var CLI_MARKER = "\u203A";
425
- var RESERVED_ROWS = 16;
426
- var MAX_ROWS = 12;
427
- var PANEL_TEXT_WIDTH = 45;
428
- var URL_PATTERN = /https?:\/\//;
429
- function rowCost(text) {
430
- return URL_PATTERN.test(text) ? Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH)) : 1;
431
- }
432
- function CliOutput() {
433
- const cliOutput = useWizard((s) => s.cliOutput);
434
- const { rows } = useWindowSize();
435
- if (!cliOutput.length) return null;
436
- const rowBudget = Math.min(Math.max(rows - RESERVED_ROWS, 3), MAX_ROWS);
437
- const visible = [];
438
- let usedRows = 0;
439
- for (let i = cliOutput.length - 1; i >= 0; i--) {
440
- const cost = rowCost(cliOutput[i].text);
441
- if (usedRows + cost > rowBudget && visible.length > 0) break;
442
- visible.unshift(cliOutput[i]);
443
- usedRows += cost;
444
- }
445
- const hidden = cliOutput.length - visible.length;
446
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
447
- hidden > 0 && /* @__PURE__ */ jsxs(Text, { color: COLORS.dim, children: [
448
- "\u2191 ",
449
- hidden,
450
- " earlier line(s)"
451
- ] }),
452
- visible.map((line) => /* @__PURE__ */ jsxs(
453
- Text,
454
- {
455
- color: line.stream === "stderr" ? COLORS.muted : COLORS.dim,
456
- wrap: URL_PATTERN.test(line.text) ? "wrap" : "truncate",
457
- children: [
458
- CLI_MARKER,
459
- " ",
460
- line.text
461
- ]
462
- },
463
- line.id
464
- ))
465
- ] });
466
- }
467
-
468
316
  // src/ui/Notices.tsx
469
- import { Box as Box3, Text as Text3, useWindowSize as useWindowSize3 } from "ink";
317
+ import { Box as Box2, Text as Text2, useWindowSize as useWindowSize2 } from "ink";
470
318
  import { useEffect as useEffect2, useState as useState2 } from "react";
471
319
 
472
320
  // src/ui/Table.tsx
473
- import { Box as Box2, Text as Text2, measureElement, useWindowSize as useWindowSize2 } from "ink";
321
+ import { Box, Text, measureElement, useWindowSize } from "ink";
474
322
  import { useEffect, useRef, useState } from "react";
475
323
  import { jsx } from "react/jsx-runtime";
476
324
  function Table({ columns, rows }) {
477
- const { columns: termCols } = useWindowSize2();
325
+ const { columns: termCols } = useWindowSize();
478
326
  const ref = useRef(null);
479
327
  const [width, setWidth] = useState(0);
480
328
  useEffect(() => {
@@ -482,7 +330,7 @@ function Table({ columns, rows }) {
482
330
  }, [termCols, columns, rows]);
483
331
  if (rows.length === 0) return null;
484
332
  const lines = formatTable(columns, rows, width || void 0);
485
- return /* @__PURE__ */ jsx(Box2, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text2, { wrap: "truncate", children: line }, `tbl-${i}`)) });
333
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text, { wrap: "truncate", children: line }, `tbl-${i}`)) });
486
334
  }
487
335
  function formatTable(columns, rows, width) {
488
336
  const natural = columns.map(
@@ -522,13 +370,48 @@ function resize(widths, budget) {
522
370
  }
523
371
  var truncate = (s, width) => s.length <= width ? s : width <= 1 ? s.slice(0, width) : `${s.slice(0, width - 1)}\u2026`;
524
372
 
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
+
525
408
  // src/ui/Notices.tsx
526
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
409
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
527
410
  var AGENT_MARKER = "\u2726";
528
- var RESERVED_ROWS2 = 14;
529
- var PANEL_TEXT_WIDTH2 = 45;
411
+ var RESERVED_ROWS = 14;
412
+ var PANEL_TEXT_WIDTH = 45;
530
413
  function messageLineCount(text) {
531
- return Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH2));
414
+ return Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH));
532
415
  }
533
416
  function noticeLineCount(notice) {
534
417
  const messageLines = (notice.messages ?? []).reduce((sum, m) => {
@@ -539,7 +422,7 @@ function noticeLineCount(notice) {
539
422
  return messageLines + tableLines;
540
423
  }
541
424
  function fitVisibleNotices(notices, windowRows) {
542
- const budget = Math.max(windowRows - RESERVED_ROWS2, 3);
425
+ const budget = Math.max(windowRows - RESERVED_ROWS, 3);
543
426
  let used = 0;
544
427
  let count = 0;
545
428
  for (let i = notices.length - 1; i >= 0; i--) {
@@ -572,7 +455,7 @@ function parseHex(hex) {
572
455
  }
573
456
  function Notices() {
574
457
  const notices = useWizard((s) => s.notices);
575
- const { rows: windowRows } = useWindowSize3();
458
+ const { rows: windowRows } = useWindowSize2();
576
459
  const visible = fitVisibleNotices(notices, windowRows);
577
460
  const [pulseStep, setPulseStep] = useState2(0);
578
461
  useEffect2(() => {
@@ -589,14 +472,14 @@ function Notices() {
589
472
  }, []);
590
473
  if (!visible.length) return null;
591
474
  const pulseColor = PULSE_COLORS[pulseStep];
592
- return /* @__PURE__ */ jsx2(Box3, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
475
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
593
476
  const isLatest = i === visible.length - 1;
594
- return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
477
+ return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", children: [
595
478
  notice.messages?.map((m, j) => {
596
479
  const line = typeof m === "string" ? { text: m } : m;
597
480
  const prefix = j === 0 ? `${AGENT_MARKER} ` : " ";
598
- return /* @__PURE__ */ jsxs2(
599
- Text3,
481
+ return /* @__PURE__ */ jsxs(
482
+ Text2,
600
483
  {
601
484
  color: isLatest && !line.color ? pulseColor : line.color ?? COLORS.dim,
602
485
  bold: line.bold,
@@ -614,41 +497,41 @@ function Notices() {
614
497
  }
615
498
 
616
499
  // src/ui/PromptInput.tsx
617
- import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
500
+ import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
618
501
  import TextInput from "ink-text-input";
619
502
  import { useState as useState5 } from "react";
620
503
 
621
504
  // src/ui/NextAction.tsx
622
- import { Box as Box4, Text as Text4 } from "ink";
623
- import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
505
+ import { Box as Box3, Text as Text3 } from "ink";
506
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
624
507
  function NextAction({
625
508
  action,
626
509
  keyHint,
627
510
  hierarchy = "primary"
628
511
  }) {
629
- return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "row", gap: 1, children: [
630
- hierarchy === "primary" && /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `> ${action}` }),
631
- hierarchy === "secondary" && /* @__PURE__ */ jsxs3(Fragment, { children: [
632
- /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `>` }),
633
- /* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, bold: true, children: action })
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 })
634
517
  ] }),
635
- /* @__PURE__ */ jsxs3(Box4, { children: [
636
- /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: "press " }),
637
- /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `[` }),
638
- /* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, children: keyHint }),
639
- /* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `]` })
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: `]` })
640
523
  ] })
641
524
  ] });
642
525
  }
643
526
 
644
527
  // src/ui/SelectPrompt.tsx
645
- import { Box as Box6, Text as Text6, useInput, useWindowSize as useWindowSize5 } from "ink";
528
+ import { Box as Box5, Text as Text5, useInput, useWindowSize as useWindowSize4 } from "ink";
646
529
  import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
647
530
 
648
531
  // src/ui/ScrollView.tsx
649
- import { Box as Box5, Text as Text5, measureElement as measureElement2, useWindowSize as useWindowSize4 } from "ink";
532
+ import { Box as Box4, Text as Text4, measureElement as measureElement2, useWindowSize as useWindowSize3 } from "ink";
650
533
  import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
651
- import { jsxs as jsxs4 } from "react/jsx-runtime";
534
+ import { jsxs as jsxs3 } from "react/jsx-runtime";
652
535
  var INDICATOR_ROWS = 2;
653
536
  function fittedWidth(node, columns) {
654
537
  let left = 0;
@@ -663,7 +546,7 @@ function useScrollWindow({
663
546
  followBottom = false
664
547
  }) {
665
548
  const viewportRef = useRef2(null);
666
- const { columns } = useWindowSize4();
549
+ const { columns } = useWindowSize3();
667
550
  const [size, setSize] = useState3(
668
551
  null
669
552
  );
@@ -718,14 +601,14 @@ function useScrollWindow({
718
601
  };
719
602
  }
720
603
  function ScrollView({ scroll, children }) {
721
- return /* @__PURE__ */ jsxs4(Box5, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
722
- scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
604
+ return /* @__PURE__ */ jsxs3(Box4, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
605
+ scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs3(Text4, { color: COLORS.dim, children: [
723
606
  "\u2191 ",
724
607
  scroll.hiddenAbove,
725
608
  " more"
726
609
  ] }),
727
610
  children,
728
- scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
611
+ scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs3(Text4, { color: COLORS.dim, children: [
729
612
  "\u2193 ",
730
613
  scroll.hiddenBelow,
731
614
  " more"
@@ -734,7 +617,7 @@ function ScrollView({ scroll, children }) {
734
617
  }
735
618
 
736
619
  // src/ui/SelectPrompt.tsx
737
- import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
620
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
738
621
  var CANCEL = "cancel";
739
622
  var ARROW_WIDTH = 4;
740
623
  var COLUMN_GAP = 2;
@@ -765,7 +648,7 @@ function SelectPrompt({
765
648
  if (multi) hints.push({ key: "[space]", label: "select" });
766
649
  hints.push({ key: "[enter]", label: "confirm" });
767
650
  const containerRef = useRef3(null);
768
- const { columns } = useWindowSize5();
651
+ const { columns } = useWindowSize4();
769
652
  const [width, setWidth] = useState4(columns);
770
653
  useLayoutEffect2(() => {
771
654
  if (!containerRef.current) return;
@@ -819,14 +702,14 @@ function SelectPrompt({
819
702
  }
820
703
  }
821
704
  });
822
- return /* @__PURE__ */ jsx4(Box6, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, width, children: [
823
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
824
- error && /* @__PURE__ */ jsx4(Text6, { color: COLORS.danger, children: error }),
825
- messages?.map((m, i) => /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
705
+ return /* @__PURE__ */ jsx4(Box5, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, width, children: [
706
+ /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
707
+ error && /* @__PURE__ */ jsx4(Text5, { color: COLORS.danger, children: error }),
708
+ messages?.map((m, i) => /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
826
709
  table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
827
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
828
- question && /* @__PURE__ */ jsx4(Text6, { color: COLORS.muted, children: question }),
829
- helpText && /* @__PURE__ */ jsx4(Text6, { color: COLORS.dim, children: helpText })
710
+ /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
711
+ question && /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: question }),
712
+ helpText && /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: helpText })
830
713
  ] })
831
714
  ] }),
832
715
  /* @__PURE__ */ jsx4(ScrollView, { scroll, children: visible.map((option, visibleIndex) => {
@@ -836,39 +719,39 @@ function SelectPrompt({
836
719
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
837
720
  const sec = isCancel ? void 0 : secondary?.[i];
838
721
  const labelColor = highlighted ? COLORS.highlight.fg : void 0;
839
- const label = /* @__PURE__ */ jsxs5(Text6, { color: labelColor, wrap: "truncate", children: [
722
+ const label = /* @__PURE__ */ jsxs4(Text5, { color: labelColor, wrap: "truncate", children: [
840
723
  highlighted ? "\u276F " : " ",
841
724
  bullet,
842
725
  option
843
726
  ] });
844
727
  const isText = sec?.kind === "text";
845
- return /* @__PURE__ */ jsxs5(
846
- Box6,
728
+ return /* @__PURE__ */ jsxs4(
729
+ Box5,
847
730
  {
848
731
  width: isText ? "100%" : barWidth,
849
732
  paddingX: 1,
850
733
  paddingY: 1,
851
734
  backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
852
735
  children: [
853
- /* @__PURE__ */ jsx4(Box6, { width: isText ? labelWidth : barLabelWidth, children: label }),
854
- isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box6, { width: textWidth, children: /* @__PURE__ */ jsx4(
855
- Text6,
736
+ /* @__PURE__ */ jsx4(Box5, { width: isText ? labelWidth : barLabelWidth, children: label }),
737
+ isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box5, { width: textWidth, children: /* @__PURE__ */ jsx4(
738
+ Text5,
856
739
  {
857
740
  wrap: "truncate",
858
741
  color: highlighted ? COLORS.primary : COLORS.muted,
859
742
  children: sec.value
860
743
  }
861
744
  ) }),
862
- sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box6, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text6, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
745
+ sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box5, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text5, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
863
746
  ]
864
747
  },
865
748
  `row-${i}`
866
749
  );
867
750
  }) }),
868
- /* @__PURE__ */ jsx4(Box6, { flexShrink: 0, children: /* @__PURE__ */ jsx4(Text6, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
751
+ /* @__PURE__ */ jsx4(Box5, { flexShrink: 0, children: /* @__PURE__ */ jsx4(Text5, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
869
752
  i > 0 ? " " : "",
870
- /* @__PURE__ */ jsx4(Text6, { color: COLORS.primary, children: key }),
871
- /* @__PURE__ */ jsxs5(Text6, { color: COLORS.dim, children: [
753
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: key }),
754
+ /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
872
755
  " ",
873
756
  label
874
757
  ] })
@@ -877,7 +760,7 @@ function SelectPrompt({
877
760
  }
878
761
 
879
762
  // src/ui/PromptInput.tsx
880
- import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
763
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
881
764
  var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
882
765
  function EnterToContinuePrompt({
883
766
  question,
@@ -888,10 +771,10 @@ function EnterToContinuePrompt({
888
771
  if (key.return) onDecide(true);
889
772
  else if (key.escape) onDecide(false);
890
773
  });
891
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, children: [
892
- messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
893
- question && /* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, children: question }),
894
- /* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
774
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
775
+ messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
776
+ question && /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, children: question }),
777
+ /* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
895
778
  /* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
896
779
  /* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
897
780
  ] })
@@ -901,11 +784,11 @@ function PromptInput() {
901
784
  const { phase, inputReq, submitInput } = useWizard();
902
785
  const [draft, setDraft] = useState5("");
903
786
  if (phase === "done" || phase === "error") {
904
- return /* @__PURE__ */ jsx5(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text7, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
787
+ return /* @__PURE__ */ jsx5(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
905
788
  }
906
789
  if (phase !== "awaitingInput" || !inputReq) return null;
907
790
  if (inputReq.promptType === "multipleChoice") {
908
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
791
+ return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
909
792
  SelectPrompt,
910
793
  {
911
794
  question: inputReq.prompt,
@@ -922,7 +805,7 @@ function PromptInput() {
922
805
  ) });
923
806
  }
924
807
  if (inputReq.promptType === "multiSelect") {
925
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
808
+ return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
926
809
  SelectPrompt,
927
810
  {
928
811
  multi: true,
@@ -937,7 +820,7 @@ function PromptInput() {
937
820
  ) });
938
821
  }
939
822
  if (inputReq.promptType === "notice") {
940
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
823
+ return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
941
824
  SelectPrompt,
942
825
  {
943
826
  question: inputReq.prompt,
@@ -959,7 +842,7 @@ function PromptInput() {
959
842
  }
960
843
  if (inputReq.promptType === "acceptReject") {
961
844
  const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
962
- return /* @__PURE__ */ jsx5(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
845
+ return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
963
846
  SelectPrompt,
964
847
  {
965
848
  question: inputReq.prompt,
@@ -970,11 +853,11 @@ function PromptInput() {
970
853
  }
971
854
  ) });
972
855
  }
973
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
974
- inputReq.error && /* @__PURE__ */ jsx5(Text7, { color: COLORS.danger, children: inputReq.error }),
975
- inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: m }, `msg-${i}`)),
976
- /* @__PURE__ */ jsxs6(Box7, { children: [
977
- /* @__PURE__ */ jsxs6(Text7, { color: COLORS.primary, children: [
856
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
857
+ inputReq.error && /* @__PURE__ */ jsx5(Text6, { color: COLORS.danger, children: inputReq.error }),
858
+ inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
859
+ /* @__PURE__ */ jsxs5(Box6, { children: [
860
+ /* @__PURE__ */ jsxs5(Text6, { color: COLORS.primary, children: [
978
861
  inputReq.prompt,
979
862
  " "
980
863
  ] }),
@@ -996,7 +879,7 @@ function PromptInput() {
996
879
  // src/ui/Welcome.tsx
997
880
  import { dirname as dirname2, join as join3 } from "node:path";
998
881
  import { fileURLToPath } from "node:url";
999
- import { Box as Box8, Spacer, Text as Text8, useInput as useInput3, useWindowSize as useWindowSize6 } from "ink";
882
+ import { Box as Box7, Spacer, Text as Text7, useInput as useInput3, useWindowSize as useWindowSize5 } from "ink";
1000
883
 
1001
884
  // src/ui/copy/welcome.ts
1002
885
  var sidebarItems = [
@@ -1024,27 +907,27 @@ var sidebarItems = [
1024
907
 
1025
908
  // src/ui/Welcome.tsx
1026
909
  import Image, { InkPictureProvider } from "ink-picture";
1027
- import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
910
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1028
911
  var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
1029
912
  function SidebarItem({
1030
913
  title,
1031
914
  description
1032
915
  }) {
1033
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
1034
- /* @__PURE__ */ jsxs7(Box8, { gap: 1, children: [
1035
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.success, children: "\u2192" }),
1036
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.strong, bold: true, children: title })
916
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
917
+ /* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
918
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.success, children: "\u2192" }),
919
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.strong, bold: true, children: title })
1037
920
  ] }),
1038
- /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 2, children: [
921
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 2, children: [
1039
922
  /* @__PURE__ */ jsx6(Spacer, {}),
1040
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: description })
923
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: description })
1041
924
  ] })
1042
925
  ] });
1043
926
  }
1044
927
  function Welcome() {
1045
928
  const confirmStart = useWizard((s) => s.confirmStart);
1046
929
  const openLearnMore = useWizard((s) => s.openLearnMore);
1047
- const { rows } = useWindowSize6();
930
+ const { rows } = useWindowSize5();
1048
931
  useInput3((input, key) => {
1049
932
  if (key.return) confirmStart();
1050
933
  else if (input === "i") openLearnMore();
@@ -1063,15 +946,15 @@ function Welcome() {
1063
946
  if (rows < 30) {
1064
947
  layout = scales["small"];
1065
948
  }
1066
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
949
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
1067
950
  /* @__PURE__ */ jsx6(
1068
- Box8,
951
+ Box7,
1069
952
  {
1070
953
  paddingY: layout.main.padding.y,
1071
954
  paddingX: layout.main.padding.x,
1072
955
  flexDirection: "column",
1073
956
  justifyContent: "center",
1074
- children: /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 2, children: [
957
+ children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 2, children: [
1075
958
  /* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
1076
959
  Image,
1077
960
  {
@@ -1083,16 +966,16 @@ function Welcome() {
1083
966
  protocol: "halfBlock"
1084
967
  }
1085
968
  ) }),
1086
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
1087
- /* @__PURE__ */ jsxs7(Box8, { gap: 1, flexDirection: "column", children: [
969
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
970
+ /* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
1088
971
  /* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
1089
972
  /* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
1090
973
  ] })
1091
974
  ] })
1092
975
  }
1093
976
  ),
1094
- /* @__PURE__ */ jsxs7(
1095
- Box8,
977
+ /* @__PURE__ */ jsxs6(
978
+ Box7,
1096
979
  {
1097
980
  backgroundColor: COLORS.bg.sidebar,
1098
981
  width: 40,
@@ -1102,7 +985,7 @@ function Welcome() {
1102
985
  flexDirection: "column",
1103
986
  justifyContent: "center",
1104
987
  children: [
1105
- /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
988
+ /* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
1106
989
  sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
1107
990
  ]
1108
991
  }
@@ -1112,7 +995,7 @@ function Welcome() {
1112
995
 
1113
996
  // src/ui/LearnMore.tsx
1114
997
  import { Fragment as Fragment2 } from "react";
1115
- import { Box as Box9, Text as Text9, useInput as useInput4, useWindowSize as useWindowSize7 } from "ink";
998
+ import { Box as Box8, Text as Text8, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
1116
999
 
1117
1000
  // src/ui/copy/learn-more.ts
1118
1001
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -1149,7 +1032,7 @@ var policyLinks = [
1149
1032
  ];
1150
1033
 
1151
1034
  // src/ui/LearnMore.tsx
1152
- import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
1035
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1153
1036
  var TAG_COLORS = {
1154
1037
  READ: COLORS.success,
1155
1038
  WRITE: COLORS.badge,
@@ -1165,25 +1048,25 @@ function NeverLine({
1165
1048
  }) {
1166
1049
  const used = segments.reduce((n, s) => n + s.text.length, 0);
1167
1050
  const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
1168
- return /* @__PURE__ */ jsxs8(Text9, { children: [
1169
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" }),
1051
+ return /* @__PURE__ */ jsxs7(Text8, { children: [
1052
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" }),
1170
1053
  " ".repeat(NEVER_BOX_PAD_X),
1171
- segments.map((s, i) => /* @__PURE__ */ jsx7(Text9, { color: s.color, bold: s.bold, children: s.text }, i)),
1054
+ segments.map((s, i) => /* @__PURE__ */ jsx7(Text8, { color: s.color, bold: s.bold, children: s.text }, i)),
1172
1055
  " ".repeat(rightPad),
1173
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: "\u2502" })
1056
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" })
1174
1057
  ] });
1175
1058
  }
1176
1059
  function LearnMore() {
1177
1060
  const confirmStart = useWizard((s) => s.confirmStart);
1178
1061
  const backToHome = useWizard((s) => s.backToHome);
1179
- const { columns } = useWindowSize7();
1062
+ const { columns } = useWindowSize6();
1180
1063
  const dividerWidth = Math.max(0, columns - PADDING_X * 2);
1181
1064
  useInput4((_input, key) => {
1182
1065
  if (key.escape) backToHome();
1183
1066
  else if (key.return) confirmStart();
1184
1067
  });
1185
- return /* @__PURE__ */ jsxs8(
1186
- Box9,
1068
+ return /* @__PURE__ */ jsxs7(
1069
+ Box8,
1187
1070
  {
1188
1071
  flexDirection: "column",
1189
1072
  paddingX: PADDING_X,
@@ -1191,20 +1074,20 @@ function LearnMore() {
1191
1074
  width: "100%",
1192
1075
  gap: 1,
1193
1076
  children: [
1194
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
1195
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: accessIntro }),
1196
- /* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, children: [
1197
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
1198
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, marginTop: 1, children: [
1199
- /* @__PURE__ */ jsx7(Box9, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text9, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
1200
- /* @__PURE__ */ jsx7(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { children: [
1201
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: item.title }),
1202
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
1077
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
1078
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: accessIntro }),
1079
+ /* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", marginTop: 1, children: [
1080
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
1081
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, marginTop: 1, children: [
1082
+ /* @__PURE__ */ jsx7(Box8, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text8, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
1083
+ /* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text8, { children: [
1084
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: item.title }),
1085
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
1203
1086
  ] }) })
1204
1087
  ] })
1205
1088
  ] }, item.tag)) }),
1206
- /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
1207
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
1089
+ /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", children: [
1090
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
1208
1091
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1209
1092
  /* @__PURE__ */ jsx7(
1210
1093
  NeverLine,
@@ -1213,7 +1096,7 @@ function LearnMore() {
1213
1096
  segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
1214
1097
  }
1215
1098
  ),
1216
- neverItems.map((item) => /* @__PURE__ */ jsxs8(Fragment2, { children: [
1099
+ neverItems.map((item) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
1217
1100
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1218
1101
  /* @__PURE__ */ jsx7(
1219
1102
  NeverLine,
@@ -1228,23 +1111,23 @@ function LearnMore() {
1228
1111
  )
1229
1112
  ] }, item)),
1230
1113
  /* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
1231
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1114
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
1232
1115
  ] }),
1233
- /* @__PURE__ */ jsx7(Box9, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1234
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1235
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.accent, children: link.url })
1116
+ /* @__PURE__ */ jsx7(Box8, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1117
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
1118
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.accent, children: link.url })
1236
1119
  ] }, link.label)) }),
1237
- /* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1238
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1239
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
1240
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "esc" }),
1241
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "] back" })
1120
+ /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "row", gap: 3, children: [
1121
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1122
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
1123
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "esc" }),
1124
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "] back" })
1242
1125
  ] }),
1243
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
1244
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "[" }),
1245
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: "enter" }),
1246
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: "]" }),
1247
- /* @__PURE__ */ jsx7(Text9, { color: COLORS.success, bold: true, children: "start wizard" })
1126
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
1127
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
1128
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "enter" }),
1129
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "]" }),
1130
+ /* @__PURE__ */ jsx7(Text8, { color: COLORS.success, bold: true, children: "start wizard" })
1248
1131
  ] })
1249
1132
  ] })
1250
1133
  ]
@@ -1253,10 +1136,10 @@ function LearnMore() {
1253
1136
  }
1254
1137
 
1255
1138
  // src/ui/Sidebar.tsx
1256
- import { Box as Box12, Text as Text12 } from "ink";
1139
+ import { Box as Box11, Text as Text11 } from "ink";
1257
1140
 
1258
1141
  // src/ui/Steps.tsx
1259
- import { Box as Box10, Text as Text10 } from "ink";
1142
+ import { Box as Box9, Text as Text9 } from "ink";
1260
1143
  import Spinner from "ink-spinner";
1261
1144
 
1262
1145
  // src/core/persistence.ts
@@ -1285,11 +1168,11 @@ async function clearWorkflowState(workflowId) {
1285
1168
  }
1286
1169
 
1287
1170
  // src/ui/Steps.tsx
1288
- import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1171
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1289
1172
  function Steps() {
1290
1173
  const { steps } = useWizard();
1291
1174
  const visibleSteps = steps.filter(isStepVisible);
1292
- return /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx8(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status[s.status], children: [
1175
+ 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: [
1293
1176
  s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
1294
1177
  " ",
1295
1178
  s.title
@@ -1299,7 +1182,7 @@ function CurrentStep() {
1299
1182
  const { steps } = useWizard();
1300
1183
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
1301
1184
  if (!currentStep) return null;
1302
- return /* @__PURE__ */ jsxs9(Text10, { color: COLORS.status.running, children: [
1185
+ return /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status.running, children: [
1303
1186
  /* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
1304
1187
  " ",
1305
1188
  ` ${currentStep.title}`
@@ -1307,19 +1190,19 @@ function CurrentStep() {
1307
1190
  }
1308
1191
 
1309
1192
  // src/ui/Progress.tsx
1310
- import { Box as Box11, Text as Text11 } from "ink";
1311
- import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
1193
+ import { Box as Box10, Text as Text10 } from "ink";
1194
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1312
1195
  function Progress() {
1313
1196
  const { steps, currentStepIndex } = useWizard();
1314
1197
  const visibleSteps = steps.filter(isStepVisible);
1315
1198
  if (visibleSteps.length === 0) return null;
1316
1199
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
1317
1200
  const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
1318
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
1319
- /* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: "STEP" }),
1320
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: activeStepNumber }),
1321
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: "/" }),
1322
- /* @__PURE__ */ jsx9(Text11, { bold: true, children: visibleSteps.length })
1201
+ return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
1202
+ /* @__PURE__ */ jsx9(Text10, { color: COLORS.muted, children: "STEP" }),
1203
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: activeStepNumber }),
1204
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: "/" }),
1205
+ /* @__PURE__ */ jsx9(Text10, { bold: true, children: visibleSteps.length })
1323
1206
  ] });
1324
1207
  }
1325
1208
 
@@ -1330,10 +1213,10 @@ var sidebarCommands = [
1330
1213
  ];
1331
1214
 
1332
1215
  // src/ui/Sidebar.tsx
1333
- import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
1216
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1334
1217
  function Sidebar() {
1335
- return /* @__PURE__ */ jsxs11(
1336
- Box12,
1218
+ return /* @__PURE__ */ jsxs10(
1219
+ Box11,
1337
1220
  {
1338
1221
  backgroundColor: "#14171E",
1339
1222
  width: 30,
@@ -1342,16 +1225,16 @@ function Sidebar() {
1342
1225
  flexDirection: "column",
1343
1226
  justifyContent: "space-between",
1344
1227
  children: [
1345
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
1346
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "PROGRESS" }),
1228
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
1229
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: "PROGRESS" }),
1347
1230
  /* @__PURE__ */ jsx10(Steps, {})
1348
1231
  ] }),
1349
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", gap: 1, children: [
1232
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
1350
1233
  /* @__PURE__ */ jsx10(Progress, {}),
1351
- /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: sidebarCommands.map((c) => {
1352
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
1353
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1354
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: c.description })
1234
+ /* @__PURE__ */ jsx10(Box11, { flexDirection: "column", children: sidebarCommands.map((c) => {
1235
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
1236
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.primary, children: `[${c.keyHint}]` }),
1237
+ /* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: c.description })
1355
1238
  ] });
1356
1239
  }) })
1357
1240
  ] })
@@ -1361,12 +1244,12 @@ function Sidebar() {
1361
1244
  }
1362
1245
 
1363
1246
  // src/ui/Ribbon.tsx
1364
- import { Box as Box13, Text as Text13 } from "ink";
1365
- import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
1247
+ import { Box as Box12, Text as Text12 } from "ink";
1248
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1366
1249
  function Ribbon() {
1367
1250
  const firstCommand = sidebarCommands[0];
1368
- return /* @__PURE__ */ jsxs12(
1369
- Box13,
1251
+ return /* @__PURE__ */ jsxs11(
1252
+ Box12,
1370
1253
  {
1371
1254
  backgroundColor: "#14171E",
1372
1255
  flexDirection: "row",
@@ -1376,9 +1259,9 @@ function Ribbon() {
1376
1259
  children: [
1377
1260
  /* @__PURE__ */ jsx11(Progress, {}),
1378
1261
  /* @__PURE__ */ jsx11(CurrentStep, {}),
1379
- /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: 1, children: [
1380
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1381
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: firstCommand.description })
1262
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
1263
+ /* @__PURE__ */ jsx11(Text12, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
1264
+ /* @__PURE__ */ jsx11(Text12, { color: COLORS.muted, children: firstCommand.description })
1382
1265
  ] })
1383
1266
  ]
1384
1267
  }
@@ -1389,8 +1272,8 @@ function Ribbon() {
1389
1272
  import { useState as useState6 } from "react";
1390
1273
 
1391
1274
  // src/ui/Logs.tsx
1392
- import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
1393
- import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
1275
+ import { Box as Box13, Text as Text13, useInput as useInput5 } from "ink";
1276
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1394
1277
  var KIND_COLOR = {
1395
1278
  tool: COLORS.primary,
1396
1279
  prompt: COLORS.badge
@@ -1426,8 +1309,8 @@ function Logs() {
1426
1309
  else if (key.downArrow) scroll.scrollBy(1);
1427
1310
  });
1428
1311
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
1429
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1430
- logs.length === 0 && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "No logs yet." }),
1312
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
1313
+ logs.length === 0 && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "No logs yet." }),
1431
1314
  /* @__PURE__ */ jsx12(ScrollView, { scroll, children: visible.map((entry) => {
1432
1315
  const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
1433
1316
  const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
@@ -1438,14 +1321,14 @@ function Logs() {
1438
1321
  const name = truncate2(entry.name, budget);
1439
1322
  budget -= name.length;
1440
1323
  const preview = rawPreview ? truncate2(rawPreview, budget) : "";
1441
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: ROW_GAP, children: [
1442
- /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: timestamp }),
1443
- /* @__PURE__ */ jsx12(Text14, { color: logNameColor(entry), wrap: "truncate", children: name }),
1444
- preview && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, wrap: "truncate", children: preview }),
1445
- durationText && /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: durationText })
1324
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: ROW_GAP, children: [
1325
+ /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: timestamp }),
1326
+ /* @__PURE__ */ jsx12(Text13, { color: logNameColor(entry), wrap: "truncate", children: name }),
1327
+ preview && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, wrap: "truncate", children: preview }),
1328
+ durationText && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: durationText })
1446
1329
  ] }, entry.id);
1447
1330
  }) }),
1448
- /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1331
+ /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
1449
1332
  ] });
1450
1333
  }
1451
1334
 
@@ -1637,11 +1520,11 @@ function track(event, payload) {
1637
1520
  }
1638
1521
 
1639
1522
  // src/ui/App.tsx
1640
- import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
1523
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1641
1524
  function App() {
1642
1525
  const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
1643
1526
  const { exit } = useApp();
1644
- const { columns, rows } = useWindowSize8();
1527
+ const { columns, rows } = useWindowSize7();
1645
1528
  const [showLogs, setShowLogs] = useState6(false);
1646
1529
  const finished = phase === "done" || phase === "error";
1647
1530
  const currentStep = steps[currentStepIndex];
@@ -1654,7 +1537,7 @@ function App() {
1654
1537
  { isActive: finished }
1655
1538
  );
1656
1539
  useInput6((_input, key) => {
1657
- if (phase === "idle" || phase === "authenticating") return;
1540
+ if (phase === "idle" || phase === "preflight") return;
1658
1541
  if (key.tab) {
1659
1542
  setShowLogs(!showLogs);
1660
1543
  track("AI Wizard Interaction", {
@@ -1664,45 +1547,49 @@ function App() {
1664
1547
  });
1665
1548
  }
1666
1549
  });
1667
- const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1550
+ const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
1668
1551
  useInput6((_input, key) => {
1669
1552
  if (escOwnedElsewhere) return;
1670
1553
  if (key.escape) {
1671
1554
  track("AI Wizard Interaction", {
1672
1555
  context: "global",
1673
1556
  key: "esc",
1557
+ // No step is active until `startWorkflow` — report the phase instead.
1674
1558
  currentStep: currentStep?.id ?? phase
1675
1559
  });
1676
1560
  exit();
1677
1561
  }
1678
1562
  });
1679
- const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1563
+ const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
1680
1564
  const flexDirection = columns > 90 ? "row" : "column";
1681
1565
  const showSidebar = flexDirection === "row";
1682
- const scrollsPastViewport = phase === "idle" && homeScreen === "learnMore";
1683
- return (
1684
- /* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
1685
- the whole screen, and the scrolling throws off its cursor arithmetic —
1686
- flicker and leftover rows. */
1687
- /* @__PURE__ */ jsxs14(
1688
- Box15,
1689
- {
1690
- backgroundColor: COLORS.bg.main,
1691
- flexDirection: "row",
1692
- width: columns,
1693
- height: scrollsPastViewport ? void 0 : rows,
1694
- overflow: scrollsPastViewport ? "visible" : "hidden",
1695
- children: [
1696
- mainWindowVisible && /* @__PURE__ */ jsxs14(
1697
- Box15,
1698
- {
1699
- flexDirection,
1700
- width: "100%",
1701
- maxHeight: rows,
1702
- justifyContent: "space-between",
1703
- children: [
1704
- showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : /* @__PURE__ */ jsxs14(
1705
- Box15,
1566
+ return /* @__PURE__ */ jsxs13(
1567
+ Box14,
1568
+ {
1569
+ backgroundColor: COLORS.bg.main,
1570
+ flexDirection: "row",
1571
+ width: columns,
1572
+ minHeight: rows,
1573
+ children: [
1574
+ mainWindowVisible && // Ink sizes the root by width only, so without a cap the scrolling
1575
+ // lists in here grow to their content instead of windowing (see
1576
+ // `useScrollWindow`). The home screens below stay uncapped: they are
1577
+ // long static copy that would be clipped rather than windowed.
1578
+ /* @__PURE__ */ jsxs13(
1579
+ Box14,
1580
+ {
1581
+ flexDirection,
1582
+ width: "100%",
1583
+ maxHeight: rows,
1584
+ justifyContent: "space-between",
1585
+ children: [
1586
+ showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
1587
+ /* Fill the space the sidebar/ribbon leaves — width beside the
1588
+ sidebar, height above the ribbon. The height matters even
1589
+ stacked: it is what the prompt's scrolling list measures itself
1590
+ against (see SelectPrompt). */
1591
+ /* @__PURE__ */ jsxs13(
1592
+ Box14,
1706
1593
  {
1707
1594
  flexDirection: "column",
1708
1595
  paddingX: 4,
@@ -1710,29 +1597,24 @@ function App() {
1710
1597
  width: showSidebar ? 70 : "100%",
1711
1598
  flexGrow: 1,
1712
1599
  children: [
1713
- phase === "authenticating" && /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginBottom: 1, children: [
1714
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
1715
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
1716
- ] }),
1717
- /* @__PURE__ */ jsx13(CliOutput, {}),
1718
1600
  /* @__PURE__ */ jsx13(Notices, {}),
1719
1601
  /* @__PURE__ */ jsx13(PromptInput, {}),
1720
- phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1721
- phase === "error" && error && /* @__PURE__ */ jsx13(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { color: COLORS.status.error, children: [
1602
+ phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
1603
+ phase === "error" && error && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.error, children: [
1722
1604
  "\u2716 ",
1723
1605
  error
1724
1606
  ] }) })
1725
1607
  ]
1726
1608
  }
1727
- ),
1728
- showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1729
- ]
1730
- }
1731
- ),
1732
- phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1733
- ]
1734
- }
1735
- )
1609
+ )
1610
+ ),
1611
+ showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
1612
+ ]
1613
+ }
1614
+ ),
1615
+ (phase === "idle" || phase === "preflight") && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
1616
+ ]
1617
+ }
1736
1618
  );
1737
1619
  }
1738
1620
 
@@ -1968,138 +1850,61 @@ async function runWorkflow(workflow, appId) {
1968
1850
  }
1969
1851
  }
1970
1852
 
1971
- // src/lib/algoliaApp.ts
1972
- import { z as z4 } from "zod";
1973
- var applicationSchema = z4.object({
1974
- id: z4.string().min(1),
1975
- name: z4.string().default(""),
1976
- plan: z4.string().optional()
1977
- });
1978
- var listSchema = z4.array(
1979
- z4.object({
1980
- id: z4.string().min(1),
1981
- name: z4.string().default(""),
1982
- plan_label: z4.string().optional()
1983
- }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
1984
- );
1985
- async function currentApplication() {
1986
- let raw;
1853
+ // src/lib/algoliaProfile.ts
1854
+ import { readFile as readFile3 } from "node:fs/promises";
1855
+ import { createRequire as createRequire2 } from "node:module";
1856
+ import { homedir as homedir2 } from "node:os";
1857
+ import { join as join6 } from "node:path";
1858
+ import { parse as parseToml } from "toml";
1859
+ var require3 = createRequire2(import.meta.url);
1860
+ function configPath() {
1861
+ const base = process.env.XDG_CONFIG_HOME || join6(homedir2(), ".config");
1862
+ return join6(base, "algolia", "config.toml");
1863
+ }
1864
+ function profilesFromConfig(tomlText) {
1865
+ let parsed;
1987
1866
  try {
1988
- raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1867
+ parsed = parseToml(tomlText);
1989
1868
  } catch {
1990
- return null;
1869
+ return [];
1991
1870
  }
1992
- const parsed = applicationSchema.safeParse(parseJson(raw));
1993
- return parsed.success ? parsed.data : null;
1994
- }
1995
- async function requireApplication() {
1996
- const app = await currentApplication();
1997
- if (!app) {
1998
- throw new Error(
1999
- "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
2000
- );
2001
- }
2002
- return app;
2003
- }
2004
- async function listApplications() {
2005
- const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
2006
- const parsed = listSchema.safeParse(parseJson(raw));
2007
- if (!parsed.success) {
2008
- throw new Error("Could not read the list of Algolia applications.");
2009
- }
2010
- return parsed.data;
2011
- }
2012
- async function selectApplication(id) {
2013
- const raw = await runAlgoliaCli(
2014
- ["application", "select", "--non-interactive", "--app-id", id],
2015
- { onOutput: stderrSink }
2016
- );
2017
- const parsed = applicationSchema.safeParse(parseJson(raw));
2018
- if (!parsed.success) {
2019
- throw new Error(
2020
- `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
2021
- );
2022
- }
2023
- return parsed.data;
2024
- }
2025
- function parseJson(text) {
1871
+ const profiles = Object.entries(parsed).filter(
1872
+ ([, t]) => typeof t.application_id === "string" && typeof t.api_key === "string"
1873
+ ).map(([name, t]) => ({
1874
+ name,
1875
+ appId: t.application_id,
1876
+ apiKey: t.api_key,
1877
+ isDefault: t.default === true
1878
+ }));
1879
+ profiles.sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
1880
+ return profiles.map(({ name, appId, apiKey }) => ({ name, appId, apiKey }));
1881
+ }
1882
+ async function loadActiveProfile() {
1883
+ let profiles;
2026
1884
  try {
2027
- return JSON.parse(text);
1885
+ profiles = profilesFromConfig(await readFile3(configPath(), "utf8"));
2028
1886
  } catch {
2029
- return void 0;
1887
+ profiles = [];
2030
1888
  }
2031
- }
2032
-
2033
- // src/lib/algoliaAppPicker.ts
2034
- function secondaryFor(app) {
2035
- return app.plan ? { kind: "badge", value: app.plan } : void 0;
2036
- }
2037
- function labelFor(app) {
2038
- return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
2039
- }
2040
- function selectAndReport(app) {
2041
- useWizard.getState().pushCliOutput(
2042
- "stdout",
2043
- `Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
2044
- );
2045
- return selectApplication(app.id);
2046
- }
2047
- async function promptForApplication() {
2048
- const store = useWizard.getState();
2049
- const apps = await listApplications();
2050
- if (apps.length === 0) {
1889
+ const profile = profiles[0];
1890
+ if (!profile) {
2051
1891
  throw new Error(
2052
- "This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
2053
- );
2054
- }
2055
- if (apps.length === 1) {
2056
- const only = apps[0];
2057
- logger.info(
2058
- { app: only.id },
2059
- "single application on the account; selecting it"
1892
+ "No Algolia profile is configured. Run `npx @algolia/cli auth login` to authenticate."
2060
1893
  );
2061
- return selectAndReport(only);
2062
1894
  }
2063
- const messages = ["Which Algolia application should the wizard work in?"];
2064
- for (; ; ) {
2065
- const choice = await store.requestUserInput({
2066
- prompt: "Select an application",
2067
- promptType: "multipleChoice",
2068
- options: apps.map(labelFor),
2069
- secondary: apps.map(secondaryFor),
2070
- messages
2071
- });
2072
- const chosen = apps.find((app) => labelFor(app) === choice);
2073
- if (!chosen) {
2074
- throw new Error("Application picker received an unexpected selection");
2075
- }
2076
- try {
2077
- return await selectAndReport(chosen);
2078
- } catch (err) {
2079
- logger.warn(
2080
- { app: chosen.id, err: err.message },
2081
- "application select failed; re-prompting"
2082
- );
2083
- messages.push(
2084
- `Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
2085
- );
2086
- }
2087
- }
2088
- }
2089
- async function ensureApplication() {
2090
- return await currentApplication() ?? await promptForApplication();
1895
+ return profile;
2091
1896
  }
2092
1897
 
2093
1898
  // src/workflows/default.ts
2094
- import { z as z27 } from "zod";
1899
+ import { z as z25 } from "zod";
2095
1900
 
2096
1901
  // src/actions/listIndices.ts
2097
- import { z as z5 } from "zod";
2098
- var indicesListSchema = z5.object({
2099
- items: z5.array(
2100
- z5.object({
2101
- name: z5.string(),
2102
- entries: z5.number().default(0)
1902
+ import { z as z3 } from "zod";
1903
+ var indicesListSchema = z3.object({
1904
+ items: z3.array(
1905
+ z3.object({
1906
+ name: z3.string(),
1907
+ entries: z3.number().default(0)
2103
1908
  })
2104
1909
  )
2105
1910
  });
@@ -2170,12 +1975,12 @@ import "zod";
2170
1975
 
2171
1976
  // src/lib/tools/listFiles.ts
2172
1977
  import { tool } from "ai";
2173
- import z6 from "zod";
1978
+ import z4 from "zod";
2174
1979
  import { readdir } from "node:fs/promises";
2175
1980
 
2176
1981
  // src/lib/tools/path.ts
2177
1982
  import { lstat } from "node:fs/promises";
2178
- import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join6, sep } from "node:path";
1983
+ import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join7, sep } from "node:path";
2179
1984
  function resolveInRoot(ctx, path) {
2180
1985
  const target = resolve2(ctx.cwd, path);
2181
1986
  const rel = relative(ctx.root, target);
@@ -2191,7 +1996,7 @@ async function hasSymlinkParent(ctx, target) {
2191
1996
  let current = ctx.root;
2192
1997
  const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
2193
1998
  for (const part of parts) {
2194
- current = join6(current, part);
1999
+ current = join7(current, part);
2195
2000
  try {
2196
2001
  if ((await lstat(current)).isSymbolicLink()) return true;
2197
2002
  } catch (err) {
@@ -2206,7 +2011,7 @@ async function hasSymlinkParent(ctx, target) {
2206
2011
  function listFilesTool(ctx) {
2207
2012
  return tool({
2208
2013
  description: "List files in the current working directory",
2209
- inputSchema: z6.object(),
2014
+ inputSchema: z4.object(),
2210
2015
  execute: async () => {
2211
2016
  logger.info("called listFiles tool");
2212
2017
  if (++ctx.counts.list > ctx.limits.list) {
@@ -2222,13 +2027,13 @@ function listFilesTool(ctx) {
2222
2027
 
2223
2028
  // src/lib/tools/changeDirectory.ts
2224
2029
  import { tool as tool2 } from "ai";
2225
- import z7 from "zod";
2030
+ import z5 from "zod";
2226
2031
  import { stat } from "node:fs/promises";
2227
2032
  function changeDirectoryTool(ctx) {
2228
2033
  return tool2({
2229
2034
  description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
2230
- inputSchema: z7.object({
2231
- path: z7.string().describe("Directory to change into")
2035
+ inputSchema: z5.object({
2036
+ path: z5.string().describe("Directory to change into")
2232
2037
  }),
2233
2038
  execute: async ({ path }) => {
2234
2039
  logger.info({ path }, "called changeDirectory tool");
@@ -2250,13 +2055,13 @@ function changeDirectoryTool(ctx) {
2250
2055
 
2251
2056
  // src/lib/tools/reportStatus.ts
2252
2057
  import { tool as tool3 } from "ai";
2253
- import z8 from "zod";
2058
+ import z6 from "zod";
2254
2059
  function reportStatusTool(output) {
2255
2060
  return tool3({
2256
2061
  description: "Report the status of your execution. Return a reason in case of failure.",
2257
- inputSchema: z8.object({
2258
- status: z8.enum(["success", "fail"]),
2259
- reason: z8.string().optional(),
2062
+ inputSchema: z6.object({
2063
+ status: z6.enum(["success", "fail"]),
2064
+ reason: z6.string().optional(),
2260
2065
  output
2261
2066
  }),
2262
2067
  execute: async ({ status, reason, output: output2 }) => {
@@ -2268,8 +2073,8 @@ function reportStatusTool(output) {
2268
2073
 
2269
2074
  // src/lib/tools/readFile.ts
2270
2075
  import { tool as tool4 } from "ai";
2271
- import z9 from "zod";
2272
- import { readFile as readFile3 } from "node:fs/promises";
2076
+ import z7 from "zod";
2077
+ import { readFile as readFile4 } from "node:fs/promises";
2273
2078
 
2274
2079
  // src/lib/tools/env.ts
2275
2080
  import { basename } from "node:path";
@@ -2296,8 +2101,8 @@ function redactEnvValues(content) {
2296
2101
  function readFileTool(ctx) {
2297
2102
  return tool4({
2298
2103
  description: "Read the contents of a file at the given path",
2299
- inputSchema: z9.object({
2300
- filePath: z9.string().describe("Path to the file to read")
2104
+ inputSchema: z7.object({
2105
+ filePath: z7.string().describe("Path to the file to read")
2301
2106
  }),
2302
2107
  execute: async ({ filePath }) => {
2303
2108
  if (++ctx.counts.read > ctx.limits.read) {
@@ -2307,7 +2112,7 @@ function readFileTool(ctx) {
2307
2112
  const resolved = resolveInRoot(ctx, filePath);
2308
2113
  if (!resolved.ok) return resolved.error;
2309
2114
  try {
2310
- const content = await readFile3(resolved.target, "utf8");
2115
+ const content = await readFile4(resolved.target, "utf8");
2311
2116
  return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
2312
2117
  } catch (err) {
2313
2118
  return `Error reading ${filePath}: ${err.message}`;
@@ -2318,15 +2123,15 @@ function readFileTool(ctx) {
2318
2123
 
2319
2124
  // src/lib/tools/writeFile.ts
2320
2125
  import { tool as tool5 } from "ai";
2321
- import z10 from "zod";
2126
+ import z8 from "zod";
2322
2127
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2323
2128
  import { dirname as dirname4 } from "node:path";
2324
2129
  function writeFileTool(ctx) {
2325
2130
  return tool5({
2326
2131
  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.",
2327
- inputSchema: z10.object({
2328
- filePath: z10.string().describe("Path to the file to write"),
2329
- content: z10.string().describe("Content to write to the file")
2132
+ inputSchema: z8.object({
2133
+ filePath: z8.string().describe("Path to the file to write"),
2134
+ content: z8.string().describe("Content to write to the file")
2330
2135
  }),
2331
2136
  execute: async ({ filePath, content }) => {
2332
2137
  logger.info({ filePath }, "called writeFile tool");
@@ -2351,95 +2156,9 @@ function writeFileTool(ctx) {
2351
2156
 
2352
2157
  // src/lib/tools/writeAlgoliaCredentials.ts
2353
2158
  import { tool as tool6 } from "ai";
2354
- import z12 from "zod";
2355
- import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
2159
+ import z9 from "zod";
2160
+ import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
2356
2161
  import { dirname as dirname5 } from "node:path";
2357
-
2358
- // src/lib/algoliaApiKey.ts
2359
- import { z as z11 } from "zod";
2360
- var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
2361
- var WRITE_ACLS = [
2362
- "addObject",
2363
- "deleteObject",
2364
- "settings",
2365
- "editSettings",
2366
- "listIndexes"
2367
- ];
2368
- var WRITE_ACL_SET = new Set(WRITE_ACLS);
2369
- var apiKeySchema = z11.object({
2370
- value: z11.string().min(1),
2371
- acl: z11.array(z11.string()).default([]),
2372
- indexes: z11.array(z11.string()).default([])
2373
- });
2374
- var apiKeyListSchema = z11.object({
2375
- items: z11.array(apiKeySchema).optional(),
2376
- keys: z11.array(apiKeySchema).optional()
2377
- }).transform((o) => o.items ?? o.keys ?? []);
2378
- var createdKeySchema = z11.object({
2379
- key: z11.string().min(1).optional(),
2380
- value: z11.string().min(1).optional()
2381
- });
2382
- function canReuse(key, index) {
2383
- return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
2384
- }
2385
- async function createSearchKey(index) {
2386
- const stdout = await runAlgoliaCli([
2387
- "apikeys",
2388
- "create",
2389
- "--indices",
2390
- index,
2391
- "--acl",
2392
- "search,browse",
2393
- "--description",
2394
- `wizard search-only key for ${index}`,
2395
- "-o",
2396
- "json"
2397
- ]);
2398
- const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
2399
- const created = key ?? value;
2400
- if (!created) throw new Error("apikeys create returned no key value");
2401
- return created;
2402
- }
2403
- function canReuseForWrites(key, index) {
2404
- return WRITE_ACLS.every((acl) => key.acl.includes(acl)) && key.acl.every((acl) => WRITE_ACL_SET.has(acl)) && key.indexes.includes(index);
2405
- }
2406
- async function resolveWriteKey(index) {
2407
- const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2408
- const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key2) => canReuseForWrites(key2, index))?.value;
2409
- if (existing) {
2410
- logger.info({ index }, "reusing existing write API key");
2411
- return existing;
2412
- }
2413
- logger.info({ index }, "no reusable write key found; creating one");
2414
- const created = await runAlgoliaCli([
2415
- "apikeys",
2416
- "create",
2417
- "--indices",
2418
- index,
2419
- "--acl",
2420
- WRITE_ACLS.join(","),
2421
- "--description",
2422
- `wizard write key for ${index}`,
2423
- "-o",
2424
- "json"
2425
- ]);
2426
- const { key, value } = createdKeySchema.parse(JSON.parse(created));
2427
- const writeKey = key ?? value;
2428
- if (!writeKey) throw new Error("apikeys create returned no key value");
2429
- return writeKey;
2430
- }
2431
- async function resolveSearchOnlyKey(index) {
2432
- const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
2433
- const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
2434
- if (existing) {
2435
- logger.info({ index }, "reusing existing search-only API key");
2436
- return existing;
2437
- }
2438
- logger.info({ index }, "no reusable search-only key found; creating one");
2439
- return createSearchKey(index);
2440
- }
2441
-
2442
- // src/lib/tools/writeAlgoliaCredentials.ts
2443
2162
  var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
2444
2163
  var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
2445
2164
  function appendEnv(content, entries) {
@@ -2453,9 +2172,9 @@ function hasEnv(content, name) {
2453
2172
  }
2454
2173
  function writeCredentialsTool(ctx) {
2455
2174
  return tool6({
2456
- 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.`,
2457
- inputSchema: z12.object({
2458
- filePath: z12.string().describe(
2175
+ 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.`,
2176
+ inputSchema: z9.object({
2177
+ filePath: z9.string().describe(
2459
2178
  'Path to the env file to write credentials into (e.g. ".env")'
2460
2179
  )
2461
2180
  }),
@@ -2463,17 +2182,11 @@ function writeCredentialsTool(ctx) {
2463
2182
  logger.info({ filePath }, "called writeCredentials tool");
2464
2183
  const resolved = resolveInRoot(ctx, filePath);
2465
2184
  if (resolved.ok === false) return resolved.error;
2466
- const targetIndex = useWizard.getState().targetIndex;
2467
- if (!targetIndex) {
2468
- return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
2469
- }
2470
- let appId;
2471
- let writeKey;
2185
+ let profile;
2472
2186
  try {
2473
- appId = (await requireApplication()).id;
2474
- writeKey = await resolveWriteKey(targetIndex);
2475
- } catch (err) {
2476
- return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
2187
+ profile = await loadActiveProfile();
2188
+ } catch {
2189
+ return "Error: no Algolia profile is configured, so credentials cannot be written. Ask the user to authenticate with the Algolia CLI first.";
2477
2190
  }
2478
2191
  try {
2479
2192
  if (await hasSymlinkParent(ctx, resolved.target)) {
@@ -2481,7 +2194,7 @@ function writeCredentialsTool(ctx) {
2481
2194
  }
2482
2195
  let existing = "";
2483
2196
  try {
2484
- existing = await readFile4(resolved.target, "utf8");
2197
+ existing = await readFile5(resolved.target, "utf8");
2485
2198
  } catch (err) {
2486
2199
  if (err.code !== "ENOENT") throw err;
2487
2200
  }
@@ -2492,8 +2205,8 @@ function writeCredentialsTool(ctx) {
2492
2205
  return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
2493
2206
  }
2494
2207
  const envWithCredentials = appendEnv(existing, [
2495
- [APP_ID_VAR, appId],
2496
- [API_KEY_VAR, writeKey]
2208
+ [APP_ID_VAR, profile.appId],
2209
+ [API_KEY_VAR, profile.apiKey]
2497
2210
  ]);
2498
2211
  await mkdir4(dirname5(resolved.target), { recursive: true });
2499
2212
  await writeFile4(resolved.target, envWithCredentials, "utf8");
@@ -2507,16 +2220,16 @@ function writeCredentialsTool(ctx) {
2507
2220
 
2508
2221
  // src/lib/tools/searchFiles.ts
2509
2222
  import { tool as tool7 } from "ai";
2510
- import z13 from "zod";
2511
- import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
2512
- import { join as join7 } from "node:path";
2223
+ import z10 from "zod";
2224
+ import { readdir as readdir2, readFile as readFile6 } from "node:fs/promises";
2225
+ import { join as join8 } from "node:path";
2513
2226
  var MAX_QUERY_LENGTH = 1e3;
2514
2227
  async function walkFiles(dir) {
2515
2228
  const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
2516
2229
  const out = [];
2517
2230
  for (const e of await readdir2(dir, { withFileTypes: true })) {
2518
2231
  if (e.name.startsWith(".") || skip.has(e.name)) continue;
2519
- const full = join7(dir, e.name);
2232
+ const full = join8(dir, e.name);
2520
2233
  if (e.isDirectory()) out.push(...await walkFiles(full));
2521
2234
  else if (e.isFile()) out.push(full);
2522
2235
  }
@@ -2525,9 +2238,9 @@ async function walkFiles(dir) {
2525
2238
  function searchFilesTool(ctx) {
2526
2239
  return tool7({
2527
2240
  description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
2528
- inputSchema: z13.object({
2529
- query: z13.string().describe("JavaScript RegExp pattern to search for"),
2530
- path: z13.string().optional().describe("Directory to search in (default: cwd)")
2241
+ inputSchema: z10.object({
2242
+ query: z10.string().describe("JavaScript RegExp pattern to search for"),
2243
+ path: z10.string().optional().describe("Directory to search in (default: cwd)")
2531
2244
  }),
2532
2245
  execute: async ({ query, path = "." }) => {
2533
2246
  logger.info({ query, path }, "called searchFiles tool");
@@ -2549,7 +2262,7 @@ function searchFilesTool(ctx) {
2549
2262
  for (const file of await walkFiles(resolved.target)) {
2550
2263
  let content;
2551
2264
  try {
2552
- content = await readFile5(file, "utf8");
2265
+ content = await readFile6(file, "utf8");
2553
2266
  } catch {
2554
2267
  continue;
2555
2268
  }
@@ -2571,7 +2284,7 @@ function searchFilesTool(ctx) {
2571
2284
 
2572
2285
  // src/lib/tools/verifyImplementation.ts
2573
2286
  import { tool as tool8 } from "ai";
2574
- import z14 from "zod";
2287
+ import z11 from "zod";
2575
2288
 
2576
2289
  // src/lib/tools/utils/runCommand.ts
2577
2290
  import { spawn as spawn2 } from "node:child_process";
@@ -2593,9 +2306,9 @@ function runCommand(command, args, cwd) {
2593
2306
  }
2594
2307
 
2595
2308
  // src/lib/tools/utils/packageManager.ts
2596
- import { readFile as readFile6 } from "node:fs/promises";
2309
+ import { readFile as readFile7 } from "node:fs/promises";
2597
2310
  import { existsSync } from "node:fs";
2598
- import { join as join8 } from "node:path";
2311
+ import { join as join9 } from "node:path";
2599
2312
  var LOCKFILES = [
2600
2313
  ["pnpm-lock.yaml", "pnpm"],
2601
2314
  ["yarn.lock", "yarn"],
@@ -2604,13 +2317,13 @@ var LOCKFILES = [
2604
2317
  ["package-lock.json", "npm"]
2605
2318
  ];
2606
2319
  async function readPackageJson(cwd = process.cwd()) {
2607
- return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
2320
+ return JSON.parse(await readFile7(join9(cwd, "package.json"), "utf8"));
2608
2321
  }
2609
2322
  function packageManagerFrom(pkg) {
2610
2323
  return pkg.packageManager?.split("@")[0] ?? "npm";
2611
2324
  }
2612
2325
  function packageManagerFromLockfile(cwd) {
2613
- return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
2326
+ return LOCKFILES.find(([file]) => existsSync(join9(cwd, file)))?.[1];
2614
2327
  }
2615
2328
  async function detectPackageManager(cwd) {
2616
2329
  try {
@@ -2651,7 +2364,7 @@ async function runRepoVerificationCheck() {
2651
2364
  function verifyImplementationTool() {
2652
2365
  return tool8({
2653
2366
  description: "Run the repo's mechanical verification check for generated implementation changes. Detects lint/typecheck/check from package.json and returns structured pass/fail evidence for the verifier to interpret.",
2654
- inputSchema: z14.object(),
2367
+ inputSchema: z11.object(),
2655
2368
  execute: async () => {
2656
2369
  logger.info("called verifyImplementation tool");
2657
2370
  return runRepoVerificationCheck();
@@ -2665,7 +2378,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
2665
2378
  import { nanoid as nanoid2 } from "nanoid";
2666
2379
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
2667
2380
  import { dirname as dirname6 } from "node:path";
2668
- import z15 from "zod";
2381
+ import z12 from "zod";
2669
2382
  var DATA_DIR = ".algolia-wizard/data";
2670
2383
  var RECORD_MODEL = "claude-haiku-4-5";
2671
2384
  var MAX_RECORDS = 100;
@@ -2677,17 +2390,17 @@ var anthropic = createAnthropic({
2677
2390
  function generateRecordTool(ctx) {
2678
2391
  return tool9({
2679
2392
  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.",
2680
- inputSchema: z15.object({
2681
- entityName: z15.string().describe("Name of the entity to generate records for."),
2682
- attributes: z15.array(z15.string()).describe("Attribute names each record must contain."),
2683
- count: z15.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2684
- hint: z15.string().optional().describe("Optional context to steer realistic values.")
2393
+ inputSchema: z12.object({
2394
+ entityName: z12.string().describe("Name of the entity to generate records for."),
2395
+ attributes: z12.array(z12.string()).describe("Attribute names each record must contain."),
2396
+ count: z12.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
2397
+ hint: z12.string().optional().describe("Optional context to steer realistic values.")
2685
2398
  }),
2686
2399
  execute: async ({ entityName, attributes, count, hint }) => {
2687
2400
  logger.info({ entityName, count }, "called generateRecord tool");
2688
2401
  try {
2689
- const value = z15.union([z15.string(), z15.number(), z15.boolean(), z15.null()]);
2690
- const recordSchema = z15.object(
2402
+ const value = z12.union([z12.string(), z12.number(), z12.boolean(), z12.null()]);
2403
+ const recordSchema = z12.object(
2691
2404
  Object.fromEntries(attributes.map((attr) => [attr, value]))
2692
2405
  );
2693
2406
  const generateBatch = async (batchCount) => {
@@ -2697,8 +2410,8 @@ function generateRecordTool(ctx) {
2697
2410
  const { output } = await generateText({
2698
2411
  model: anthropic(RECORD_MODEL),
2699
2412
  output: Output.object({
2700
- schema: z15.object({
2701
- records: z15.array(recordSchema).length(batchCount)
2413
+ schema: z12.object({
2414
+ records: z12.array(recordSchema).length(batchCount)
2702
2415
  })
2703
2416
  }),
2704
2417
  prompt: [
@@ -2756,12 +2469,12 @@ function generateRecordTool(ctx) {
2756
2469
 
2757
2470
  // src/lib/tools/notifyUser.ts
2758
2471
  import { tool as tool10 } from "ai";
2759
- import z16 from "zod";
2472
+ import z13 from "zod";
2760
2473
  function notifyUserTool() {
2761
2474
  return tool10({
2762
2475
  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.`,
2763
- inputSchema: z16.object({
2764
- message: z16.string().describe(
2476
+ inputSchema: z13.object({
2477
+ message: z13.string().describe(
2765
2478
  "Short, plain-language description of what you are doing now."
2766
2479
  )
2767
2480
  }),
@@ -2941,10 +2654,10 @@ async function runAgent(req) {
2941
2654
  }
2942
2655
 
2943
2656
  // src/actions/detectLanguage.ts
2944
- import z19 from "zod";
2945
- var detectLanguageSchema = z19.object({
2946
- languages: z19.array(z19.object({ name: z19.string(), version: z19.string() })),
2947
- frameworks: z19.array(z19.object({ name: z19.string(), version: z19.string() }))
2657
+ import z16 from "zod";
2658
+ var detectLanguageSchema = z16.object({
2659
+ languages: z16.array(z16.object({ name: z16.string(), version: z16.string() })),
2660
+ frameworks: z16.array(z16.object({ name: z16.string(), version: z16.string() }))
2948
2661
  });
2949
2662
  var detectLanguage = () => runAgent({
2950
2663
  instructions: [
@@ -2962,31 +2675,31 @@ var detectLanguage = () => runAgent({
2962
2675
  });
2963
2676
 
2964
2677
  // src/actions/analyzeCodebase.ts
2965
- import z20 from "zod";
2678
+ import z17 from "zod";
2966
2679
  var READONLY_TOOLS = [
2967
2680
  "listFiles",
2968
2681
  "changeDirectory",
2969
2682
  "readFile",
2970
2683
  "searchFiles"
2971
2684
  ];
2972
- var ingestionAnalysisSchema = z20.object({
2973
- ingestionAnalysis: z20.array(
2974
- z20.object({
2975
- name: z20.string(),
2976
- paths: z20.array(z20.string()),
2685
+ var ingestionAnalysisSchema = z17.object({
2686
+ ingestionAnalysis: z17.array(
2687
+ z17.object({
2688
+ name: z17.string(),
2689
+ paths: z17.array(z17.string()),
2977
2690
  // indexable fields the agent found for this entity
2978
- attributes: z20.array(z20.string())
2691
+ attributes: z17.array(z17.string())
2979
2692
  })
2980
2693
  )
2981
2694
  });
2982
- var searchImplementationAnalysisSchema = z20.object({
2983
- searchImplementationAnalysis: z20.string()
2695
+ var searchImplementationAnalysisSchema = z17.object({
2696
+ searchImplementationAnalysis: z17.string()
2984
2697
  });
2985
- var verificationSchema = z20.object({
2986
- verification: z20.array(z20.string())
2698
+ var verificationSchema = z17.object({
2699
+ verification: z17.array(z17.string())
2987
2700
  });
2988
2701
  var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
2989
- var analyzeCodebaseSchema = z20.object({
2702
+ var analyzeCodebaseSchema = z17.object({
2990
2703
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
2991
2704
  searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
2992
2705
  verification: verificationSchema.shape.verification.optional(),
@@ -3048,7 +2761,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3048
2761
  // package.json
3049
2762
  var package_default = {
3050
2763
  name: "@algolia/wizard",
3051
- version: "0.9.0-rc.85.78",
2764
+ version: "0.9.0",
3052
2765
  description: "Magically implement Algolia functionality in your codebase",
3053
2766
  type: "module",
3054
2767
  engines: {
@@ -3096,6 +2809,7 @@ var package_default = {
3096
2809
  dependencies: {
3097
2810
  "@ai-sdk/anthropic": "^3.0.81",
3098
2811
  "@ai-sdk/openai-compatible": "^2.0.47",
2812
+ "@algolia/cli": "^5.11.0",
3099
2813
  "@hono/node-server": "^2.0.10",
3100
2814
  "@segment/analytics-node": "^3.1.0",
3101
2815
  ai: "^6.0.190",
@@ -3109,6 +2823,7 @@ var package_default = {
3109
2823
  nanoid: "^5.1.15",
3110
2824
  pino: "^10.3.1",
3111
2825
  react: "^19.2.7",
2826
+ toml: "^4.1.1",
3112
2827
  varlock: "^1.5.1",
3113
2828
  zod: "^4.4.3",
3114
2829
  zustand: "^5.0.14"
@@ -3166,8 +2881,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
3166
2881
  }
3167
2882
 
3168
2883
  // src/actions/confirmLanguage.ts
3169
- import z22 from "zod";
3170
- var confirmLanguageSchema = z22.object({
2884
+ import z19 from "zod";
2885
+ var confirmLanguageSchema = z19.object({
3171
2886
  languages: detectLanguageSchema.shape.languages
3172
2887
  });
3173
2888
  async function confirmLanguage(ctx) {
@@ -3188,8 +2903,8 @@ async function confirmLanguage(ctx) {
3188
2903
  }
3189
2904
 
3190
2905
  // src/actions/confirmFramework.ts
3191
- import z23 from "zod";
3192
- var confirmFrameworkSchema = z23.object({
2906
+ import z20 from "zod";
2907
+ var confirmFrameworkSchema = z20.object({
3193
2908
  frameworks: detectLanguageSchema.shape.frameworks
3194
2909
  });
3195
2910
  var CURATED_FRAMEWORKS = [
@@ -3317,8 +3032,8 @@ async function promptUser(ctx, params) {
3317
3032
  }
3318
3033
 
3319
3034
  // src/actions/confirmEntities.ts
3320
- import z24 from "zod";
3321
- var confirmEntitiesSchema = z24.object({
3035
+ import z21 from "zod";
3036
+ var confirmEntitiesSchema = z21.object({
3322
3037
  // Final detection — the focused re-run may supersede project-scan's.
3323
3038
  ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
3324
3039
  confirmedEntities: confirmedEntitiesFieldSchema
@@ -3388,15 +3103,15 @@ async function confirmEntities(ctx) {
3388
3103
  }
3389
3104
 
3390
3105
  // src/actions/review.ts
3391
- import { z as z25 } from "zod";
3392
- var reviewSchema = z25.object({
3106
+ import { z as z22 } from "zod";
3107
+ var reviewSchema = z22.object({
3393
3108
  // Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
3394
3109
  // not one entry per workflow step — a step's raw output can be a long,
3395
3110
  // multi-paragraph blob (see implement.ts's summaries.join), and mirroring
3396
3111
  // that 1:1 is what made the old per-step summary an unreadable wall of text.
3397
- summaryPoints: z25.array(z25.string()),
3398
- reviewPrompt: z25.string(),
3399
- nextSteps: z25.array(z25.string())
3112
+ summaryPoints: z22.array(z22.string()),
3113
+ reviewPrompt: z22.string(),
3114
+ nextSteps: z22.array(z22.string())
3400
3115
  });
3401
3116
  function formatCompletedSteps(steps) {
3402
3117
  if (!steps.length) return "(no prior steps completed)";
@@ -3447,16 +3162,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
3447
3162
  };
3448
3163
 
3449
3164
  // src/actions/implement.ts
3450
- import z26 from "zod";
3165
+ import z24 from "zod";
3451
3166
 
3452
3167
  // src/lib/worktree.ts
3453
3168
  import { execFile, spawn as spawn3 } from "node:child_process";
3454
- import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3169
+ import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
3455
3170
  import {
3456
3171
  basename as basename2,
3457
3172
  dirname as dirname7,
3458
3173
  isAbsolute as isAbsolute2,
3459
- join as join9,
3174
+ join as join10,
3460
3175
  relative as relative2,
3461
3176
  resolve as resolve3
3462
3177
  } from "node:path";
@@ -3490,7 +3205,7 @@ async function isWorkingTreeDirty(repoRoot) {
3490
3205
  return out.trim().length > 0;
3491
3206
  }
3492
3207
  async function pruneOldWorktrees(repoRoot) {
3493
- const dir = join9(stateDir(repoRoot), "worktrees");
3208
+ const dir = join10(stateDir(repoRoot), "worktrees");
3494
3209
  const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
3495
3210
  for (const slug of stale) {
3496
3211
  const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
@@ -3501,7 +3216,7 @@ async function pruneOldWorktrees(repoRoot) {
3501
3216
  "worktree",
3502
3217
  "remove",
3503
3218
  "--force",
3504
- join9(dir, slug)
3219
+ join10(dir, slug)
3505
3220
  ]);
3506
3221
  await git(["-C", repoRoot, "branch", "-D", branch]);
3507
3222
  } catch (err) {
@@ -3515,7 +3230,7 @@ async function pruneOldWorktrees(repoRoot) {
3515
3230
  async function createWorktree(repoRoot) {
3516
3231
  const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
3517
3232
  const dirSlug = branch.replace(/\//g, "-");
3518
- const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
3233
+ const path = join10(stateDir(repoRoot), "worktrees", dirSlug);
3519
3234
  await git(["-C", repoRoot, "worktree", "prune"]);
3520
3235
  await pruneOldWorktrees(repoRoot);
3521
3236
  await mkdir6(dirname7(path), { recursive: true });
@@ -3635,8 +3350,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
3635
3350
  } catch {
3636
3351
  return { ok: false, reason: `"${sourcePath}" does not exist` };
3637
3352
  }
3638
- const relPath = join9(ingestDir, basename2(source));
3639
- const dest = join9(worktreePath, relPath);
3353
+ const relPath = join10(ingestDir, basename2(source));
3354
+ const dest = join10(worktreePath, relPath);
3640
3355
  try {
3641
3356
  await mkdir6(dirname7(dest), { recursive: true });
3642
3357
  await copyFile(source, dest);
@@ -3652,10 +3367,10 @@ function hasEnvVar(content, name) {
3652
3367
  return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
3653
3368
  }
3654
3369
  async function writeSearchEnvValues(worktreePath, vars) {
3655
- const target = join9(worktreePath, ".env");
3370
+ const target = join10(worktreePath, ".env");
3656
3371
  let existing = "";
3657
3372
  try {
3658
- existing = await readFile7(target, "utf8");
3373
+ existing = await readFile8(target, "utf8");
3659
3374
  } catch (err) {
3660
3375
  if (err.code !== "ENOENT") throw err;
3661
3376
  }
@@ -3723,15 +3438,63 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
3723
3438
  }
3724
3439
  }
3725
3440
 
3441
+ // src/lib/algoliaApiKey.ts
3442
+ import { z as z23 } from "zod";
3443
+ var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
3444
+ var apiKeySchema = z23.object({
3445
+ value: z23.string().min(1),
3446
+ acl: z23.array(z23.string()).default([]),
3447
+ indexes: z23.array(z23.string()).default([])
3448
+ });
3449
+ var apiKeyListSchema = z23.object({
3450
+ items: z23.array(apiKeySchema).optional(),
3451
+ keys: z23.array(apiKeySchema).optional()
3452
+ }).transform((o) => o.items ?? o.keys ?? []);
3453
+ var createdKeySchema = z23.object({
3454
+ key: z23.string().min(1).optional(),
3455
+ value: z23.string().min(1).optional()
3456
+ });
3457
+ function canReuse(key, index) {
3458
+ return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
3459
+ }
3460
+ async function createSearchKey(index) {
3461
+ const stdout = await runAlgoliaCli([
3462
+ "apikeys",
3463
+ "create",
3464
+ "--indices",
3465
+ index,
3466
+ "--acl",
3467
+ "search,browse",
3468
+ "--description",
3469
+ `wizard search-only key for ${index}`,
3470
+ "-o",
3471
+ "json"
3472
+ ]);
3473
+ const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
3474
+ const created = key ?? value;
3475
+ if (!created) throw new Error("apikeys create returned no key value");
3476
+ return created;
3477
+ }
3478
+ async function resolveSearchOnlyKey(index) {
3479
+ const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
3480
+ const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
3481
+ if (existing) {
3482
+ logger.info({ index }, "reusing existing search-only API key");
3483
+ return existing;
3484
+ }
3485
+ logger.info({ index }, "no reusable search-only key found; creating one");
3486
+ return createSearchKey(index);
3487
+ }
3488
+
3726
3489
  // src/lib/algoliaDocs.ts
3727
3490
  import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
3728
- import { dirname as dirname8, join as join10 } from "node:path";
3491
+ import { dirname as dirname8, join as join11 } from "node:path";
3729
3492
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3730
- var DOCS_SUBPATH = join10("docs", "algolia-sdk");
3493
+ var DOCS_SUBPATH = join11("docs", "algolia-sdk");
3731
3494
  function findDocsDir() {
3732
3495
  let dir = dirname8(fileURLToPath2(import.meta.url));
3733
3496
  for (; ; ) {
3734
- const candidate = join10(dir, DOCS_SUBPATH);
3497
+ const candidate = join11(dir, DOCS_SUBPATH);
3735
3498
  if (existsSync2(candidate)) return candidate;
3736
3499
  const parent = dirname8(dir);
3737
3500
  if (parent === dir) return void 0;
@@ -3754,7 +3517,7 @@ function loadAlgoliaDoc(language) {
3754
3517
  );
3755
3518
  return "";
3756
3519
  }
3757
- return readFileSync(join10(docsDir, files[0]), "utf8").trim();
3520
+ return readFileSync(join11(docsDir, files[0]), "utf8").trim();
3758
3521
  }
3759
3522
  function getNamedDoc(name, language) {
3760
3523
  const docsDir = findDocsDir();
@@ -3762,7 +3525,7 @@ function getNamedDoc(name, language) {
3762
3525
  logger.warn("docs/algolia-sdk not found");
3763
3526
  return "";
3764
3527
  }
3765
- const file = join10(docsDir, `${name}-${language}.md`);
3528
+ const file = join11(docsDir, `${name}-${language}.md`);
3766
3529
  if (!existsSync2(file)) {
3767
3530
  logger.warn({ name, language }, "named SDK reference not found");
3768
3531
  return "";
@@ -3789,34 +3552,50 @@ function shellQuote(value) {
3789
3552
  }
3790
3553
 
3791
3554
  // src/actions/implement.ts
3792
- var implementSchema = z26.object({
3793
- filesChanged: z26.array(z26.string()),
3794
- summary: z26.string(),
3795
- worktreePath: z26.string().optional(),
3796
- ingestCommand: z26.string().optional(),
3797
- ingestScriptRan: z26.boolean().optional(),
3798
- ingestRecordCount: z26.number().optional(),
3799
- ingestDurationMs: z26.number().optional(),
3800
- ingestionSource: z26.enum(["local", "fileUpload", "generated"]),
3801
- searchEnvVars: z26.array(
3802
- z26.object({
3803
- name: z26.string(),
3804
- value: z26.string()
3555
+ var implementSchema = z24.object({
3556
+ filesChanged: z24.array(z24.string()),
3557
+ summary: z24.string(),
3558
+ // Absolute path to the throwaway worktree holding the generated changes, so
3559
+ // the user can open it (`cd <worktreePath>`) or inspect the diff
3560
+ // (`git -C <worktreePath> status/diff`).
3561
+ worktreePath: z24.string().optional(),
3562
+ ingestCommand: z24.string().optional(),
3563
+ // True when the user accepted the run-now prompt and the wizard executed the
3564
+ // ingestion script; downstream steps use this to avoid telling the user to run
3565
+ // a script that already ran.
3566
+ ingestScriptRan: z24.boolean().optional(),
3567
+ // Records ingested by the run-now execution, parsed from the script's
3568
+ // machine-readable count line; absent when the script didn't run or emitted
3569
+ // no parseable count.
3570
+ ingestRecordCount: z24.number().optional(),
3571
+ // Wall-clock duration of the run-now ingestion execution, in ms.
3572
+ ingestDurationMs: z24.number().optional(),
3573
+ ingestionSource: z24.enum(["local", "fileUpload", "generated"]),
3574
+ // Suggested names/values, built from framework detection. The search agent is
3575
+ // instructed to rename the prefix if it doesn't match the project's build
3576
+ // tool, so the names it actually wrote can differ — treat these as hints, not
3577
+ // ground truth (the agent's summary carries the final names).
3578
+ searchEnvVars: z24.array(
3579
+ z24.object({
3580
+ name: z24.string(),
3581
+ value: z24.string()
3805
3582
  })
3806
3583
  ).optional()
3807
3584
  });
3808
- var implementationOutputSchema = z26.object({
3809
- summary: z26.string(),
3810
- // Ingestion only: a structured pair the wizard turns into an argv, never a
3811
- // free-form command string. `runtime` is allowlisted and `entrypoint` is
3812
- // validated worktree-relative, so the agent cannot inject extra commands.
3813
- runtime: z26.enum(INGEST_RUNTIMES).optional(),
3814
- entrypoint: z26.string().optional()
3585
+ var implementationOutputSchema = z24.object({
3586
+ summary: z24.string(),
3587
+ // Ingestion only: how to run the generated script, as a structured pair the
3588
+ // wizard turns into an argv (`<runtime> <entrypoint>`) never a free-form
3589
+ // command string. `runtime` is constrained to an allowlisted interpreter and
3590
+ // `entrypoint` is validated to a worktree-relative path before execution, so
3591
+ // the agent cannot inject extra commands or swap the interpreter.
3592
+ runtime: z24.enum(INGEST_RUNTIMES).optional(),
3593
+ entrypoint: z24.string().optional()
3815
3594
  });
3816
- var verificationOutputSchema = z26.object({
3817
- summary: z26.string(),
3818
- sufficient: z26.boolean(),
3819
- additionalInstructions: z26.string().optional()
3595
+ var verificationOutputSchema = z24.object({
3596
+ summary: z24.string(),
3597
+ sufficient: z24.boolean(),
3598
+ additionalInstructions: z24.string().optional()
3820
3599
  });
3821
3600
  var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
3822
3601
  var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
@@ -3888,6 +3667,9 @@ function sourceSpecificInstructions(input) {
3888
3667
  "Add env vars for any local source access (DB URL, API base, file paths) that is not a fixed repo path."
3889
3668
  ],
3890
3669
  fileUpload: [
3670
+ // The wizard already copied the developer's file into the worktree at this
3671
+ // exact path, so the agent must read it directly — never search for or
3672
+ // substitute another file.
3891
3673
  `Records come from the developer's file, already copied into the worktree at "${input.uploadFilePath}". Read and parse that exact file.`,
3892
3674
  "Parse by extension: JSON = array of objects; CSV/TSV = header row maps to keys.",
3893
3675
  "Map parsed columns/fields to the confirmed entity attributes.",
@@ -3927,9 +3709,12 @@ function searchInstructions(input) {
3927
3709
  doc,
3928
3710
  `Add the search UI at ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working SearchBox and Hits against the "${input.targetIndex}" index.`,
3929
3711
  "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.",
3712
+ // appId always resolves (loadActiveProfile throws otherwise); only the
3713
+ // search-only key is best-effort and can fall back to a placeholder.
3930
3714
  `Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
3931
- // Not the agent's to rename: the wizard writes these exact names into
3932
- // ".env" right after this step, so a renamed prefix would leave the code
3715
+ // Names are fixed, not the agent's to rename: the wizard writes the
3716
+ // resolved app id / search-only key into ".env" under these exact names
3717
+ // right after this step, so a renamed prefix here would leave the code
3933
3718
  // reading a var the wizard never wrote.
3934
3719
  `Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
3935
3720
  '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.',
@@ -4073,7 +3858,6 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4073
3858
  }
4074
3859
  }
4075
3860
  const targetIndex = selected?.selection;
4076
- useWizard.getState().setTargetIndex(targetIndex ?? null);
4077
3861
  await assertGitRepoWithHead(repoRoot);
4078
3862
  if (await isWorkingTreeDirty(repoRoot)) {
4079
3863
  await confirmDirtyWorkingTree(ctx, repoRoot);
@@ -4084,7 +3868,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4084
3868
  let appId;
4085
3869
  let searchKey;
4086
3870
  if (useCases.includes("search")) {
4087
- appId = (await requireApplication()).id;
3871
+ appId = (await loadActiveProfile()).appId;
4088
3872
  try {
4089
3873
  searchKey = await resolveSearchOnlyKey(targetIndex);
4090
3874
  } catch (err) {
@@ -4129,6 +3913,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4129
3913
  ingestDir: INGEST_DIR,
4130
3914
  ingestionSource,
4131
3915
  uploadFilePath,
3916
+ // language.frameworks already prefers the confirm-framework step output,
3917
+ // so the user's confirmed stack (not just raw detection) picks the flavor.
4132
3918
  uiFramework: detectUiFramework(language)
4133
3919
  };
4134
3920
  const summaries = [];
@@ -4193,8 +3979,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4193
3979
  messages: []
4194
3980
  }) === true;
4195
3981
  if (runNow) {
4196
- const ingestApp = await requireApplication();
4197
- const writeKey = await resolveWriteKey(targetIndex);
3982
+ const profile = await loadActiveProfile();
4198
3983
  ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
4199
3984
  const scriptLogId = ctx.logStart("runIngestScript", {
4200
3985
  runtime: ingestRuntime,
@@ -4206,8 +3991,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4206
3991
  ingestRuntime,
4207
3992
  ingestEntrypoint,
4208
3993
  {
4209
- [APP_ID_VAR]: ingestApp.id,
4210
- [API_KEY_VAR]: writeKey
3994
+ [APP_ID_VAR]: profile.appId,
3995
+ [API_KEY_VAR]: profile.apiKey
4211
3996
  }
4212
3997
  );
4213
3998
  ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
@@ -4276,6 +4061,8 @@ ${run2.output}` : status;
4276
4061
  );
4277
4062
  }
4278
4063
  await ctx.requestUserInput({
4064
+ // No question being asked here, just an acknowledgement — the
4065
+ // continue/decline hints below already say "continue".
4279
4066
  prompt: "",
4280
4067
  promptType: "enterToContinue",
4281
4068
  options: [],
@@ -4416,8 +4203,8 @@ var defaultWorkflow = {
4416
4203
  defineStep({
4417
4204
  id: "select-index",
4418
4205
  title: "Set up index",
4419
- outputSchema: z27.object({
4420
- selection: z27.string()
4206
+ outputSchema: z25.object({
4207
+ selection: z25.string()
4421
4208
  }),
4422
4209
  run: (ctx) => selectIndexStep(ctx)
4423
4210
  }),
@@ -4696,7 +4483,7 @@ function parseCliArgs(argv) {
4696
4483
 
4697
4484
  // src/lib/resetState.ts
4698
4485
  import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
4699
- import { join as join11 } from "node:path";
4486
+ import { join as join12 } from "node:path";
4700
4487
  var KEEP = ["wizard.log"];
4701
4488
  async function resetProjectState() {
4702
4489
  const dir = stateDir();
@@ -4708,7 +4495,7 @@ async function resetProjectState() {
4708
4495
  }
4709
4496
  const targets = entries.filter((name) => !KEEP.includes(name));
4710
4497
  await Promise.all(
4711
- targets.map((name) => rm2(join11(dir, name), { recursive: true, force: true }))
4498
+ targets.map((name) => rm2(join12(dir, name), { recursive: true, force: true }))
4712
4499
  );
4713
4500
  return { dir, removed: targets };
4714
4501
  }
@@ -4763,38 +4550,31 @@ ${formatStepList(workflow)}`);
4763
4550
  }
4764
4551
  async function run(workflow) {
4765
4552
  const store = useWizard.getState();
4766
- const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4767
- await store.waitForStart();
4553
+ let instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4768
4554
  let user = await getUser();
4769
4555
  if (!user) {
4770
- store.beginAuth();
4556
+ await instance.waitUntilRenderFlush();
4557
+ instance.cleanup();
4771
4558
  try {
4772
4559
  await runAuthLogin();
4773
4560
  } catch (err) {
4774
- store.setError(err instanceof Error ? err.message : String(err));
4775
- await instance.waitUntilExit();
4561
+ console.error(err instanceof Error ? err.message : String(err));
4776
4562
  process.exit(1);
4777
4563
  }
4778
- store.endAuth();
4564
+ instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
4779
4565
  user = await getUser();
4780
4566
  if (!user) {
4781
4567
  store.setError(
4782
- "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
4568
+ "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
4783
4569
  );
4784
4570
  await instance.waitUntilExit();
4785
4571
  process.exit(1);
4786
4572
  }
4787
4573
  }
4788
4574
  store.setUser(user);
4789
- let app;
4790
- try {
4791
- app = await ensureApplication();
4792
- } catch (err) {
4793
- store.setError(err instanceof Error ? err.message : String(err));
4794
- await instance.waitUntilExit();
4795
- process.exit(1);
4796
- }
4797
- runWorkflow(workflow, app.id);
4575
+ const profile = await loadActiveProfile();
4576
+ await store.waitForStart();
4577
+ runWorkflow(workflow, profile?.appId);
4798
4578
  }
4799
4579
  var started = await startup();
4800
4580
  if (typeof started === "number") {