@algolia/wizard 0.75.0 → 0.77.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 (2) hide show
  1. package/dist/main.js +834 -338
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -4,9 +4,54 @@
4
4
  import { render } from "ink";
5
5
 
6
6
  // src/ui/App.tsx
7
- import { Box as Box20, Text as Text21, useApp, useInput as useInput9, useWindowSize as useWindowSize7 } from "ink";
7
+ import { Box as Box21, Text as Text22, useApp as useApp2, useInput as useInput9, useWindowSize as useWindowSize8 } from "ink";
8
8
  import Spinner3 from "ink-spinner";
9
9
 
10
+ // src/lib/terminalSize.ts
11
+ var MIN_ROWS = 30;
12
+ var MIN_COLUMNS = 100;
13
+ var COMPACT_ROWS = 24;
14
+ var COMPACT_COLUMNS = 80;
15
+ var SIDEBAR_MIN_COLUMNS = 90;
16
+ function isCompactTerminal(columns, rows) {
17
+ return rows < COMPACT_ROWS || columns < COMPACT_COLUMNS;
18
+ }
19
+ async function requestTerminalSize(stdout = process.stdout, {
20
+ rows = MIN_ROWS,
21
+ columns = MIN_COLUMNS,
22
+ timeoutMs = 150,
23
+ settleMs = 120
24
+ } = {}) {
25
+ if (!stdout.isTTY) return;
26
+ const currentRows = stdout.rows ?? 0;
27
+ const currentColumns = stdout.columns ?? 0;
28
+ if (currentRows >= rows && currentColumns >= columns) return;
29
+ stdout.write(
30
+ `\x1B[8;${Math.max(currentRows, rows)};${Math.max(currentColumns, columns)}t`
31
+ );
32
+ const resized = await waitForResize(stdout, timeoutMs);
33
+ if (!resized) return;
34
+ await delay(settleMs);
35
+ stdout.write("\x1B[2J\x1B[H");
36
+ }
37
+ function waitForResize(stdout, timeoutMs) {
38
+ return new Promise((resolve6) => {
39
+ const finish = (didResize) => () => {
40
+ clearTimeout(timer);
41
+ stdout.off("resize", onResize);
42
+ resolve6(didResize);
43
+ };
44
+ const onResize = finish(true);
45
+ const timer = setTimeout(finish(false), timeoutMs);
46
+ stdout.once("resize", onResize);
47
+ });
48
+ }
49
+ function delay(ms) {
50
+ return new Promise((resolve6) => {
51
+ setTimeout(resolve6, ms);
52
+ });
53
+ }
54
+
10
55
  // src/core/store.ts
11
56
  import { create } from "zustand";
12
57
  import { nanoid } from "nanoid";
@@ -48,7 +93,7 @@ function runAlgoliaCli(args, { onOutput, redact } = {}) {
48
93
  const store = useWizard.getState();
49
94
  const command = mask(args.join(" "), redact);
50
95
  const logId = store.logStart("tool", `algolia ${command}`);
51
- return new Promise((resolve5, reject) => {
96
+ return new Promise((resolve6, reject) => {
52
97
  const child = spawn("npx", npxArgs(args), { shell });
53
98
  let stdout = "";
54
99
  let stderr = "";
@@ -71,7 +116,7 @@ function runAlgoliaCli(args, { onOutput, redact } = {}) {
71
116
  splitters.stdout.flush();
72
117
  splitters.stderr.flush();
73
118
  if (code === 0) {
74
- resolve5(stdout);
119
+ resolve6(stdout);
75
120
  } else {
76
121
  const failed = stderr.trim();
77
122
  let detail = "";
@@ -277,15 +322,15 @@ var useWizard = create((set, get) => ({
277
322
  ),
278
323
  openLearnMore: () => set({ homeScreen: "learnMore" }),
279
324
  backToHome: () => set({ homeScreen: "home" }),
280
- waitForStart: () => new Promise((resolve5) => {
325
+ waitForStart: () => new Promise((resolve6) => {
281
326
  if (get().phase !== "idle") {
282
- resolve5();
327
+ resolve6();
283
328
  return;
284
329
  }
285
330
  const unsubscribe = useWizard.subscribe((s) => {
286
331
  if (s.phase !== "idle") {
287
332
  unsubscribe();
288
- resolve5();
333
+ resolve6();
289
334
  }
290
335
  });
291
336
  }),
@@ -392,11 +437,11 @@ var useWizard = create((set, get) => ({
392
437
  (t) => t.id === id ? { ...t, status, durationMs: Date.now() - t.startedAt } : t
393
438
  )
394
439
  })),
395
- requestUserInput: (req) => new Promise((resolve5) => {
440
+ requestUserInput: (req) => new Promise((resolve6) => {
396
441
  set({
397
442
  phase: "awaitingInput",
398
443
  inputReq: req,
399
- _resolve: resolve5,
444
+ _resolve: resolve6,
400
445
  loadingMessage: null
401
446
  });
402
447
  }),
@@ -2234,6 +2279,7 @@ async function promptForApplication(highlight) {
2234
2279
  }
2235
2280
  async function promptToChangeApplication(currentId) {
2236
2281
  const store = useWizard.getState();
2282
+ store.clearNotices();
2237
2283
  store.setLoadingMessage("Loading applications\u2026");
2238
2284
  try {
2239
2285
  const env = await findEnvApplicationId();
@@ -2260,7 +2306,7 @@ async function ensureApplication(resumeAppId) {
2260
2306
  // package.json
2261
2307
  var package_default = {
2262
2308
  name: "@algolia/wizard",
2263
- version: "0.75.0",
2309
+ version: "0.77.0",
2264
2310
  description: "Magically implement Algolia functionality in your codebase",
2265
2311
  type: "module",
2266
2312
  engines: {
@@ -3314,10 +3360,10 @@ var GIT_ENV_OVERRIDES = [
3314
3360
  function gitSucceeds(root, args) {
3315
3361
  const env = { ...process.env };
3316
3362
  for (const key of GIT_ENV_OVERRIDES) delete env[key];
3317
- return new Promise((resolve5) => {
3363
+ return new Promise((resolve6) => {
3318
3364
  execFile("git", ["-C", root, ...args], { env }, (err) => {
3319
- if (!err) return resolve5(true);
3320
- resolve5(
3365
+ if (!err) return resolve6(true);
3366
+ resolve6(
3321
3367
  err.code === 1 ? false : void 0
3322
3368
  );
3323
3369
  });
@@ -3603,7 +3649,7 @@ import { tool as tool8 } from "ai";
3603
3649
  import z15 from "zod";
3604
3650
  import { createHash as createHash2 } from "node:crypto";
3605
3651
  import { readFile as readFile8, stat as stat2 } from "node:fs/promises";
3606
- import { relative as relative4 } from "node:path";
3652
+ import { basename as basename2, relative as relative4, resolve as resolve3 } from "node:path";
3607
3653
 
3608
3654
  // src/lib/tools/utils/prompt.ts
3609
3655
  var chain = Promise.resolve();
@@ -3623,11 +3669,92 @@ async function fileDigest(filePath) {
3623
3669
  function grantReviewedCommandApproval(ctx, approval) {
3624
3670
  reviewedCommandApprovals.set(ctx, approval);
3625
3671
  }
3672
+ function scriptOperandTokens(command) {
3673
+ const tokens = [];
3674
+ let token = "";
3675
+ let quote = null;
3676
+ let escaped = false;
3677
+ let comment = false;
3678
+ let skipNext = false;
3679
+ let stop = false;
3680
+ const windowsPaths = process.platform === "win32";
3681
+ const finishToken = () => {
3682
+ if (token) {
3683
+ if (!stop) {
3684
+ if (skipNext) skipNext = false;
3685
+ else tokens.push(token);
3686
+ }
3687
+ }
3688
+ token = "";
3689
+ };
3690
+ for (const character of command) {
3691
+ if (stop) break;
3692
+ if (comment) {
3693
+ if (character === "\n") comment = false;
3694
+ continue;
3695
+ }
3696
+ if (escaped) {
3697
+ token += character;
3698
+ escaped = false;
3699
+ continue;
3700
+ }
3701
+ if (quote) {
3702
+ if (character === quote) quote = null;
3703
+ else if (quote === '"' && character === "\\") escaped = true;
3704
+ else token += character;
3705
+ continue;
3706
+ }
3707
+ if (character === "\\") {
3708
+ if (windowsPaths) token += character;
3709
+ else escaped = true;
3710
+ } else if (character === '"' || character === "'") {
3711
+ quote = character;
3712
+ } else if (character === "#" && token === "") {
3713
+ comment = true;
3714
+ } else if (character === ">" || character === "<") {
3715
+ finishToken();
3716
+ skipNext = true;
3717
+ } else if (character === "|" || character === "&" || character === ";") {
3718
+ finishToken();
3719
+ stop = true;
3720
+ } else if (/\s/.test(character) || "()".includes(character)) {
3721
+ finishToken();
3722
+ } else {
3723
+ token += character;
3724
+ }
3725
+ }
3726
+ finishToken();
3727
+ return tokens;
3728
+ }
3729
+ function tokenResolvesToScript(cwd, token, scriptPath) {
3730
+ if (resolve3(cwd, token) === scriptPath) return true;
3731
+ if (!token.includes("\\")) return false;
3732
+ return resolve3(cwd, token.replaceAll("\\", "/")) === scriptPath;
3733
+ }
3734
+ function commandReferencesScript(command, scriptPath, cwd) {
3735
+ return scriptOperandTokens(command).some(
3736
+ (token) => tokenResolvesToScript(cwd, token, scriptPath)
3737
+ );
3738
+ }
3626
3739
  function getReviewedCommandApproval(ctx, command, cwd) {
3627
3740
  const approval = reviewedCommandApprovals.get(ctx);
3628
3741
  if (approval?.command !== command || approval.cwd !== cwd) return void 0;
3629
3742
  return approval;
3630
3743
  }
3744
+ function commandDiffersFromReviewedScript(ctx, command, cwd) {
3745
+ const approval = reviewedCommandApprovals.get(ctx);
3746
+ if (approval === void 0) return false;
3747
+ if (approval.command === command && approval.cwd === cwd) return false;
3748
+ if (approval.command === command) return true;
3749
+ const scriptReferences = [
3750
+ basename2(approval.filePath),
3751
+ relative4(ctx.root, approval.filePath),
3752
+ ".algolia-wizard"
3753
+ ];
3754
+ return scriptReferences.some(
3755
+ (reference) => reference !== "" && command.includes(reference)
3756
+ );
3757
+ }
3631
3758
  function clearReviewedCommandApproval(ctx) {
3632
3759
  reviewedCommandApprovals.delete(ctx);
3633
3760
  }
@@ -3682,6 +3809,14 @@ function reviewScriptTool(ctx) {
3682
3809
  if (!isFile) {
3683
3810
  return `Refused: ${filePath} is not a file. Write the script first, then review the path you wrote.`;
3684
3811
  }
3812
+ const relativeScriptPath = relative4(resolvedCwd.target, resolved2.target);
3813
+ if (!commandReferencesScript(
3814
+ command,
3815
+ resolved2.target,
3816
+ resolvedCwd.target
3817
+ )) {
3818
+ return `Refused: the command does not reference the reviewed script from its cwd. Use "${relativeScriptPath}" as the script path, then call reviewScript again.`;
3819
+ }
3685
3820
  const reviewedDigest = await fileDigest(resolved2.target).catch(
3686
3821
  () => null
3687
3822
  );
@@ -3773,7 +3908,7 @@ var SIGKILL_DELAY_MS = 5e3;
3773
3908
  function runShell(command, opts) {
3774
3909
  const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
3775
3910
  const startedAt = Date.now();
3776
- return new Promise((resolve5) => {
3911
+ return new Promise((resolve6) => {
3777
3912
  let output = "";
3778
3913
  let timedOut = false;
3779
3914
  let settled = false;
@@ -3788,7 +3923,7 @@ function runShell(command, opts) {
3788
3923
  settled = true;
3789
3924
  clearTimeout(timer);
3790
3925
  clearTimeout(killTimer);
3791
- resolve5({
3926
+ resolve6({
3792
3927
  exitCode,
3793
3928
  output: truncateText(output.trim()),
3794
3929
  timedOut,
@@ -4071,6 +4206,7 @@ async function classifyCommandSafety(createModel, command, cwd, explanation, app
4071
4206
  prompt: [
4072
4207
  "A coding agent wants to run this shell command in a user's project without asking for approval first. The command may be in any programming language or ecosystem.",
4073
4208
  "Judge it SAFE only if it cannot modify, delete, move, rename, or install anything the project cares about, cannot push/publish/deploy/commit anything, and cannot make a network request that changes remote state.",
4209
+ "Never judge a command SAFE if it runs, imports, requires, or otherwise references an ingestion script or any path under `.algolia-wizard/`. This rule includes shell wrappers, inline code, variable indirection, and dry-run or check descriptions. Such a command always requires explicit user approval.",
4074
4210
  "Three broad categories are safe, and most commands you will see fall into one of them:",
4075
4211
  "(1) Reporting or listing existing state, with no side effect \u2014 e.g. `git status`, `npm ls`, `npm outdated`, `pip show`, `pip freeze`, `cargo tree`, `docker ps`, `docker images`, `kubectl get pods`, `terraform state list`. This includes a read-only GET-style query to a remote registry or API that only fetches public metadata and changes nothing remote (e.g. `npm view <package>`, `pip index versions <package>`, `curl` with no -X/--request other than GET and no -d/--data) \u2014 safe because nothing changes, not because it stays local.",
4076
4212
  '(2) A "preview" or "check" mode that reports what a change WOULD do, or reports a problem, without making the change. This is a general pattern, not a fixed list \u2014 ANY command that takes a dry-run/check/plan/validate/diff/list-only flag is safe when that flag is present, regardless of whether the same flag would normally appear on a "safe" kind of command: `terraform plan`, `terraform validate`, `terraform fmt -check`, `black --check`, `prettier --check`, `isort --check`, `gofmt -l` (list-only), `stylelint` with no `--fix`, and equally `git push --dry-run`, `npm publish --dry-run`, `kubectl apply --dry-run=client` \u2014 these last three are otherwise-unsafe operations (push, publish, cluster changes) that the dry-run flag turns into a report. The corresponding command WITHOUT that flag (e.g. `terraform apply`, `black .`, `gofmt -w`, `git push`) is a different, unsafe command \u2014 the flag is what makes the difference, for any tool, not just the ones named here. This cuts both ways, so do not assume a bare invocation is the safe one: `cargo fmt`, `black`, `prettier`, `isort`, `rustfmt`, and `terraform fmt` all REWRITE FILES by default and need an explicit check/diff/dry-run flag to become safe, while `gofmt`, `eslint`, `stylelint`, `rubocop`, and `ruff check` default to a safe check-only mode and need an explicit fix/write flag to become unsafe \u2014 the same-looking bare command is safe for one group and unsafe for the other, so judge each by what its own flags actually say, not by resemblance to a tool you already judged.',
@@ -4198,6 +4334,9 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
4198
4334
  () => runReviewedCommand(ctx, command, resolved2.target)
4199
4335
  );
4200
4336
  if (reviewed !== void 0) return reviewed;
4337
+ if (commandDiffersFromReviewedScript(ctx, command, resolved2.target)) {
4338
+ return "Refused: this ingestion command differs from the reviewed command. Call reviewScript with this exact command and cwd before runShell.";
4339
+ }
4201
4340
  const fast = fastPathSafety(command);
4202
4341
  let isSafe = fast;
4203
4342
  let usedStoredApproval = false;
@@ -5209,15 +5348,15 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
5209
5348
  // src/actions/implement.ts
5210
5349
  import z28 from "zod";
5211
5350
  import { access, mkdir as mkdir5, readFile as readFile9 } from "node:fs/promises";
5212
- import { dirname as dirname5, isAbsolute as isAbsolute3, join as join11, relative as relative6, resolve as resolve4, sep as sep2 } from "node:path";
5351
+ import { dirname as dirname5, isAbsolute as isAbsolute3, join as join11, relative as relative6, resolve as resolve5, sep as sep2 } from "node:path";
5213
5352
 
5214
5353
  // src/lib/git.ts
5215
5354
  import { execFile as execFile2 } from "node:child_process";
5216
5355
  import { copyFile, mkdir as mkdir4, stat as stat3 } from "node:fs/promises";
5217
- import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join9, resolve as resolve3 } from "node:path";
5356
+ import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, join as join9, resolve as resolve4 } from "node:path";
5218
5357
  var MAX_BUFFER = 32 * 1024 * 1024;
5219
5358
  function git(args) {
5220
- return new Promise((resolve5, reject) => {
5359
+ return new Promise((resolve6, reject) => {
5221
5360
  execFile2("git", args, { maxBuffer: MAX_BUFFER }, (err, stdout, stderr) => {
5222
5361
  if (err)
5223
5362
  return reject(
@@ -5225,7 +5364,7 @@ function git(args) {
5225
5364
  `git ${args.join(" ")} failed: ${stderr.toString().trim() || err.message}`
5226
5365
  )
5227
5366
  );
5228
- resolve5(stdout.toString());
5367
+ resolve6(stdout.toString());
5229
5368
  });
5230
5369
  });
5231
5370
  }
@@ -5243,7 +5382,7 @@ async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
5243
5382
  if (!trimmed) {
5244
5383
  return { ok: false, reason: "no file path was provided" };
5245
5384
  }
5246
- const source = isAbsolute2(trimmed) ? trimmed : resolve3(repoRoot, trimmed);
5385
+ const source = isAbsolute2(trimmed) ? trimmed : resolve4(repoRoot, trimmed);
5247
5386
  try {
5248
5387
  if (!(await stat3(source)).isFile()) {
5249
5388
  return { ok: false, reason: `"${sourcePath}" is not a file` };
@@ -5251,9 +5390,9 @@ async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
5251
5390
  } catch {
5252
5391
  return { ok: false, reason: `"${sourcePath}" does not exist` };
5253
5392
  }
5254
- const relPath = join9(ingestDir, basename2(source));
5393
+ const relPath = join9(ingestDir, basename3(source));
5255
5394
  const dest = join9(repoRoot, relPath);
5256
- if (resolve3(source) === resolve3(dest)) {
5395
+ if (resolve4(source) === resolve4(dest)) {
5257
5396
  return { ok: true, relPath };
5258
5397
  }
5259
5398
  try {
@@ -5477,8 +5616,16 @@ function ingestionInstructions(input) {
5477
5616
  ...algoliaClientDoc(input),
5478
5617
  "Install the Algolia client with the project's own package manager via runShell, declaring it in whatever manifest the project uses (e.g. package.json, requirements.txt, Gemfile, go.mod, composer.json) so the dependency is not just installed ad hoc.",
5479
5618
  `If the script loads its env vars from a file (e.g. via dotenv or an equivalent for its language) rather than the process environment directly, decide that up front and call writeCredentials on that file before you finish the script \u2014 do not wait to discover the need for it by having a writeFile call refused.`,
5480
- `When the script is finished, decide the exact command and cwd you will use to run it, then call reviewScript with that filePath, command, cwd, the exact selected entityName, and usesGeneratedRecords set to ${input.ingestionSource === "generated"}. Wait for the developer to review it because running it writes records to a live index. Do not run it before that call returns.`,
5619
+ "Before reviewScript, only use a non-executing parse check such as `node --check <script>` or `python -m py_compile <script>`. Never import, require, run, or dry-run the ingestion script before reviewScript returns.",
5620
+ "Check installed dependency presence at the manifest level, such as `npm ls <package>` or `pip show <package>`. Do not load the ingestion script to resolve dependencies.",
5621
+ ...isJsProject(input.language) ? [
5622
+ "For Node.js, module resolution starts from the script path, not the cwd."
5623
+ ] : [],
5624
+ `When the script is finished, decide the exact command and cwd you will use to run it. Make sure that the script path in the command resolves from that cwd to the filePath. Then call reviewScript with that filePath, command, cwd, the exact selected entityName, and usesGeneratedRecords set to ${input.ingestionSource === "generated"}. Wait for the developer to review it because running it writes records to a live index. Do not run it before that call returns. If you change the command or cwd after a failed run, call reviewScript again before runShell.`,
5481
5625
  'Then, without changing directories, run the script yourself via runShell using the exact same command and cwd. Report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
5626
+ "If external data access fails, do not run authentication commands, change accounts or machine configuration, or start proxies, containers, or services.",
5627
+ "Do not troubleshoot unavailable external infrastructure. Preserve the script and command, report success, and let the developer prepare the data source.",
5628
+ "If the script cannot access an external data source because credentials or a service are unavailable, do not report status=fail. Report status=success with the completed script, ingestCommand, and a concise failure summary. The wizard handles this run failure and tells the developer how to retry.",
5482
5629
  "The summary should be extremely concise.",
5483
5630
  ...sourceSpecificInstructions(input)
5484
5631
  ] : []
@@ -5630,15 +5777,30 @@ function parseIngestRecordCount(output) {
5630
5777
  const count = Number(match[1]);
5631
5778
  return Number.isFinite(count) ? count : void 0;
5632
5779
  }
5633
- function ingestOutcome(executions, ingestCommand) {
5780
+ function relativeIngestCwd(execution, repoRoot) {
5781
+ const cwd = relative6(repoRoot, execution.cwd);
5782
+ return cwd && cwd !== "." ? cwd : ".";
5783
+ }
5784
+ function reportedCommandWithCwdNote(execution, repoRoot) {
5785
+ const cwd = relativeIngestCwd(execution, repoRoot);
5786
+ return cwd === "." ? execution.command : `${execution.command} (run from ${cwd})`;
5787
+ }
5788
+ function ingestOutcome(executions, repoRoot, ingestCommand) {
5634
5789
  const newestFirst = [...executions].reverse();
5790
+ const matchesReportedCommand = (execution) => {
5791
+ if (execution.command === ingestCommand) return true;
5792
+ if (!ingestCommand) return false;
5793
+ return ingestCommand === reportedCommandWithCwdNote(execution, repoRoot);
5794
+ };
5635
5795
  const withCount = newestFirst.filter(
5636
5796
  (e) => parseIngestRecordCount(e.output ?? "") != null
5637
5797
  );
5638
- const succeeded = newestFirst.filter((e) => e.approved && e.exitCode === 0);
5798
+ const succeeded = newestFirst.filter(
5799
+ (e) => e.approved && e.exitCode === 0 && !e.timedOut
5800
+ );
5639
5801
  return {
5640
- run: succeeded.find((e) => e.command === ingestCommand) ?? succeeded.find((e) => withCount.includes(e)),
5641
- attempt: ingestCommand ? newestFirst.find((e) => e.command === ingestCommand) : void 0,
5802
+ run: succeeded.find(matchesReportedCommand) ?? succeeded.find((e) => withCount.includes(e)),
5803
+ attempt: ingestCommand ? newestFirst.find(matchesReportedCommand) : void 0,
5642
5804
  recordCount: parseIngestRecordCount(withCount[0]?.output ?? "")
5643
5805
  };
5644
5806
  }
@@ -5732,7 +5894,7 @@ async function pathExists(path) {
5732
5894
  async function frontendHasPackageJson(repoRoot, searchLocation) {
5733
5895
  let directory = repoRoot;
5734
5896
  if (searchLocation) {
5735
- const candidate = dirname5(resolve4(repoRoot, searchLocation));
5897
+ const candidate = dirname5(resolve5(repoRoot, searchLocation));
5736
5898
  const candidateRelative = relative6(repoRoot, candidate);
5737
5899
  const outsideRoot = candidateRelative === ".." || candidateRelative.startsWith(`..${sep2}`) || isAbsolute3(candidateRelative);
5738
5900
  if (!outsideRoot) directory = candidate;
@@ -5985,7 +6147,12 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
5985
6147
  run: ingestRun,
5986
6148
  attempt: ingestAttempt,
5987
6149
  recordCount
5988
- } = ingestOutcome(executions, ingestCommand);
6150
+ } = ingestOutcome(executions, repoRoot, ingestCommand);
6151
+ let ingestCwd = ".";
6152
+ if (ingestAttempt) {
6153
+ ingestCommand = ingestAttempt.command;
6154
+ ingestCwd = relativeIngestCwd(ingestAttempt, repoRoot);
6155
+ }
5989
6156
  ingestScriptRan = ingestRun != null;
5990
6157
  ingestRecordCount = recordCount;
5991
6158
  ingestDurationMs = ingestRun?.durationMs;
@@ -6061,7 +6228,10 @@ ${detail}` : ""}`
6061
6228
  ]
6062
6229
  });
6063
6230
  } else {
6064
- const commandMessages = ingestCommand ? [`Ingestion command: ${ingestCommand}`] : [];
6231
+ const commandMessages = ingestCommand ? [
6232
+ `Ingestion command: ${ingestCommand}`,
6233
+ ...ingestCwd !== "." ? [`Working directory: ${ingestCwd}`] : []
6234
+ ] : [];
6065
6235
  await ctx.requestUserInput({
6066
6236
  prompt: "",
6067
6237
  promptType: "enterToContinue",
@@ -6314,6 +6484,9 @@ var defaultWorkflow = {
6314
6484
  ]
6315
6485
  };
6316
6486
 
6487
+ // src/workflows/playbook.ts
6488
+ import "zod";
6489
+
6317
6490
  // src/actions/playbookSteps/selectPlaybook.ts
6318
6491
  import { z as z32 } from "zod";
6319
6492
 
@@ -6333,6 +6506,16 @@ var DashboardApiError = class extends Error {
6333
6506
  return isRecord(this.body) ? this.body : void 0;
6334
6507
  }
6335
6508
  };
6509
+ async function retryOnDashboardError(call, recover) {
6510
+ for (; ; ) {
6511
+ try {
6512
+ return await call();
6513
+ } catch (err) {
6514
+ if (!(err instanceof DashboardApiError)) throw err;
6515
+ await recover(err);
6516
+ }
6517
+ }
6518
+ }
6336
6519
  function isRecord(value) {
6337
6520
  return !!value && typeof value === "object" && !Array.isArray(value);
6338
6521
  }
@@ -6435,6 +6618,21 @@ async function getPlaybooks() {
6435
6618
  );
6436
6619
  return data;
6437
6620
  }
6621
+ async function createPlaybookExecution(appId, slug, indexName, options) {
6622
+ const { data } = await dashboardRequest(
6623
+ `/api/1/playbook-integrations/applications/${appId}/playbooks/${slug}/executions`,
6624
+ playbookExecutionCreateResponseSchema,
6625
+ {
6626
+ method: "POST",
6627
+ headers: { "content-type": "application/json" },
6628
+ body: JSON.stringify({
6629
+ index_name: indexName,
6630
+ force_new: options?.forceNew ?? false
6631
+ })
6632
+ }
6633
+ );
6634
+ return data;
6635
+ }
6438
6636
  async function createSampleIndex(appId, playbookSlug) {
6439
6637
  const { data } = await dashboardRequest(
6440
6638
  `/api/1/playbook-integrations/applications/${appId}/playbooks/${playbookSlug}/sample_index`,
@@ -6443,6 +6641,20 @@ async function createSampleIndex(appId, playbookSlug) {
6443
6641
  );
6444
6642
  return data;
6445
6643
  }
6644
+ async function getPlaybookExecution(uuid) {
6645
+ const { data } = await dashboardRequest(
6646
+ `/api/1/playbook-integrations/playbooks/executions/${uuid}`,
6647
+ playbookExecutionSchema
6648
+ );
6649
+ return data;
6650
+ }
6651
+ async function assessPlaybookExecution(uuid) {
6652
+ await dashboardRequest(
6653
+ `/api/1/playbook-integrations/playbooks/executions/${uuid}/assess`,
6654
+ messageResponseSchema,
6655
+ { method: "POST" }
6656
+ );
6657
+ }
6446
6658
 
6447
6659
  // src/actions/playbookSteps/selectPlaybook.ts
6448
6660
  var selectPlaybookSchema = z32.object({
@@ -6477,45 +6689,15 @@ async function selectPlaybookStep(ctx, requestedSlug) {
6477
6689
  return persistSlug(ctx, selection);
6478
6690
  }
6479
6691
 
6480
- // src/actions/playbookSteps/confirmPlaybook.ts
6481
- import { z as z33 } from "zod";
6482
- var confirmPlaybookSchema = z33.void();
6483
- var CONTINUE_OPTION = "Looks good, continue";
6484
- var GO_BACK_OPTION = "Go back";
6485
- var SELECT_APP_OPTION = "Select app";
6486
- async function confirmPlaybookStep(ctx) {
6487
- const { slug } = ctx.getStepOutput("select-playbook");
6488
- for (; ; ) {
6489
- const selection = await ctx.requestUserInput({
6490
- prompt: "Ready to continue?",
6491
- promptType: "multipleChoice",
6492
- options: [CONTINUE_OPTION, GO_BACK_OPTION, SELECT_APP_OPTION],
6493
- messages: [`Playbook: ${slug}`, `Application: ${ctx.appId}`],
6494
- helpText: "Going back re-opens the playbook catalogue. Select app opens the application catalogue \u2014 keep the current app to stay here, or pick another to restart the wizard."
6495
- });
6496
- if (typeof selection !== "string") {
6497
- throw new Error("confirm-playbook received an unexpected result");
6498
- }
6499
- if (selection === SELECT_APP_OPTION) {
6500
- await ctx.changeApplication();
6501
- continue;
6502
- }
6503
- if (selection === GO_BACK_OPTION) {
6504
- return ctx.returnToStep("select-playbook");
6505
- }
6506
- return;
6507
- }
6508
- }
6509
-
6510
6692
  // src/actions/playbookSteps/selectIndex.ts
6511
- import { z as z34 } from "zod";
6512
- var selectIndexSchema = z34.object({
6513
- indexName: z34.string(),
6514
- sampleIndex: z34.boolean()
6693
+ import { z as z33 } from "zod";
6694
+ var selectIndexSchema = z33.object({
6695
+ indexName: z33.string(),
6696
+ sampleIndex: z33.boolean()
6515
6697
  });
6516
6698
  var CREATE_SAMPLE_INDEX = "Create a sample index";
6517
- var GO_BACK_OPTION2 = "Go back to playbook selection";
6518
- var SELECT_APP_OPTION2 = "Select app";
6699
+ var GO_BACK_OPTION = "Go back to playbook selection";
6700
+ var SELECT_APP_OPTION = "Select app";
6519
6701
  var MIN_ENTRIES_FOR_SELECTION = 1;
6520
6702
  function formatIndexOption(index) {
6521
6703
  if (index.entries < MIN_ENTRIES_FOR_SELECTION) {
@@ -6541,8 +6723,8 @@ async function selectIndexStep2(ctx) {
6541
6723
  const options = [
6542
6724
  ...indexOptions,
6543
6725
  CREATE_SAMPLE_INDEX,
6544
- GO_BACK_OPTION2,
6545
- SELECT_APP_OPTION2
6726
+ GO_BACK_OPTION,
6727
+ SELECT_APP_OPTION
6546
6728
  ];
6547
6729
  const disabled = hasIndices ? [
6548
6730
  ...indices.map((idx) => !isIndexSelectable(idx)),
@@ -6571,11 +6753,11 @@ you can explore or switch to another application`;
6571
6753
  "selectIndex received an unexpected non-text result"
6572
6754
  );
6573
6755
  }
6574
- if (selection === GO_BACK_OPTION2) {
6756
+ if (selection === GO_BACK_OPTION) {
6575
6757
  ctx.setUserInput("fetchedIndices", void 0);
6576
6758
  return ctx.returnToStep("select-playbook");
6577
6759
  }
6578
- if (selection === SELECT_APP_OPTION2) {
6760
+ if (selection === SELECT_APP_OPTION) {
6579
6761
  await ctx.changeApplication();
6580
6762
  continue;
6581
6763
  }
@@ -6604,8 +6786,8 @@ you can explore or switch to another application`;
6604
6786
  // src/actions/playbookSteps/confirmIndex.ts
6605
6787
  import "zod";
6606
6788
  var confirmIndexSchema = playbookExecutionSchema.nullable();
6607
- var CONTINUE_OPTION2 = "Looks good, continue";
6608
- var GO_BACK_OPTION3 = "Go back";
6789
+ var CONTINUE_OPTION = "Looks good, continue";
6790
+ var GO_BACK_OPTION2 = "Go back";
6609
6791
  async function confirmIndexStep(ctx) {
6610
6792
  const { sampleIndex } = ctx.getStepOutput("select-index");
6611
6793
  if (!sampleIndex) {
@@ -6615,7 +6797,7 @@ async function confirmIndexStep(ctx) {
6615
6797
  const selection = await ctx.requestUserInput({
6616
6798
  prompt: "Ready to create sample index?",
6617
6799
  promptType: "multipleChoice",
6618
- options: [CONTINUE_OPTION2, GO_BACK_OPTION3],
6800
+ options: [CONTINUE_OPTION, GO_BACK_OPTION2],
6619
6801
  messages: [
6620
6802
  `A new index called "algolia_playbooks_${slug}_sample_index" will be created with sample ${slug} data.`
6621
6803
  ],
@@ -6624,7 +6806,7 @@ async function confirmIndexStep(ctx) {
6624
6806
  if (typeof selection !== "string") {
6625
6807
  throw new Error("confirm-index received an unexpected result");
6626
6808
  }
6627
- if (selection === GO_BACK_OPTION3) {
6809
+ if (selection === GO_BACK_OPTION2) {
6628
6810
  return ctx.returnToStep("select-index");
6629
6811
  }
6630
6812
  ctx.notify({
@@ -6654,6 +6836,227 @@ async function confirmIndexStep(ctx) {
6654
6836
  }
6655
6837
  }
6656
6838
 
6839
+ // src/actions/playbookSteps/dataAssessment.ts
6840
+ var CONTINUE_EXECUTION_OPTION = "Continue";
6841
+ var RESTART_OPTION = "Restart";
6842
+ var GO_BACK_OPTION3 = "Go back";
6843
+ var SWITCH_APPLICATION_OPTION = "Switch application";
6844
+ var SWITCH_INDEX_OPTION = "Switch index";
6845
+ var RETRY_OPTION = "Retry";
6846
+ var POLL_INTERVAL_MS = 5e3;
6847
+ var POLL_TIMEOUT_MS = 6e4;
6848
+ var ASSESSING_NOTICE_HOLD_MS = 2e4;
6849
+ var CREATING_EXECUTION_NOTICE = "creating playbook execution\u2026";
6850
+ var POLL_NOTICES = [
6851
+ { message: "created playbook execution", holdMs: POLL_INTERVAL_MS },
6852
+ { message: "sampled records", holdMs: POLL_INTERVAL_MS },
6853
+ {
6854
+ message: "assessing schema and readiness \u2014 this usually takes ~20s for indices under 1M records.",
6855
+ holdMs: ASSESSING_NOTICE_HOLD_MS
6856
+ },
6857
+ { message: "detecting required fields\u2026" }
6858
+ ];
6859
+ async function dataAssessmentStep(ctx) {
6860
+ const { slug } = ctx.getStepOutput("select-playbook");
6861
+ const { indexName, existingExecution } = indexForAssessment(ctx);
6862
+ if (existingExecution) return existingExecution;
6863
+ try {
6864
+ showNotice(ctx, CREATING_EXECUTION_NOTICE);
6865
+ const { id: appId } = await requireApplication();
6866
+ const execution = await resolveExecution(ctx, appId, slug, indexName);
6867
+ return await runAssessment(ctx, execution, indexName);
6868
+ } finally {
6869
+ ctx.clearNotices();
6870
+ }
6871
+ }
6872
+ function indexForAssessment(ctx) {
6873
+ const selected = ctx.getStepOutput("select-index");
6874
+ if (!selected.sampleIndex) {
6875
+ return { indexName: selected.indexName, existingExecution: null };
6876
+ }
6877
+ const created = ctx.getStepOutput("confirm-index");
6878
+ if (!created) {
6879
+ throw new Error(
6880
+ "data-assessment expected confirm-index to create a sample index"
6881
+ );
6882
+ }
6883
+ return {
6884
+ indexName: created.source_index_name,
6885
+ existingExecution: created
6886
+ };
6887
+ }
6888
+ function showNotice(ctx, message) {
6889
+ ctx.clearNotices();
6890
+ ctx.notify({ messages: [message] });
6891
+ }
6892
+ function startPollNotices(ctx) {
6893
+ let index = 0;
6894
+ let timer;
6895
+ const showCurrent = () => {
6896
+ const notice = POLL_NOTICES[index];
6897
+ showNotice(ctx, notice.message);
6898
+ if (notice.holdMs == null) return;
6899
+ timer = setTimeout(() => {
6900
+ index += 1;
6901
+ showCurrent();
6902
+ }, notice.holdMs);
6903
+ };
6904
+ showCurrent();
6905
+ return () => clearTimeout(timer);
6906
+ }
6907
+ async function runAssessment(ctx, execution, indexName) {
6908
+ if (execution.step_status === "done") {
6909
+ return execution;
6910
+ }
6911
+ if (execution.step_status === "not_started" || execution.step_status === "error") {
6912
+ await assessExecution(ctx, execution.uuid, indexName);
6913
+ } else if (execution.step_status !== "in_progress") {
6914
+ throw unexpectedAssessmentStatus(execution.step_status);
6915
+ }
6916
+ for (; ; ) {
6917
+ const assessed = await pollUntilAssessed(ctx, execution.uuid, indexName);
6918
+ if (assessed.step_status === "done") {
6919
+ return assessed;
6920
+ }
6921
+ if (assessed.step_status !== "error") {
6922
+ throw unexpectedAssessmentStatus(assessed.step_status);
6923
+ }
6924
+ await promptAssessmentRecovery(
6925
+ ctx,
6926
+ assessmentFailureMessage(assessed),
6927
+ assessed.status_reason === "index_empty"
6928
+ );
6929
+ await assessExecution(ctx, execution.uuid, indexName);
6930
+ }
6931
+ }
6932
+ function unexpectedAssessmentStatus(status) {
6933
+ return new Error(
6934
+ `data-assessment received an unexpected assessment status: ${status}`
6935
+ );
6936
+ }
6937
+ async function resolveExecution(ctx, appId, slug, indexName) {
6938
+ const created = await createWithRecovery(ctx, appId, slug, indexName);
6939
+ if (!created.resumed) return created;
6940
+ const choice = await ctx.requestUserInput({
6941
+ prompt: `${indexName} has a playbook in progress.`,
6942
+ promptType: "multipleChoice",
6943
+ options: [CONTINUE_EXECUTION_OPTION, RESTART_OPTION, GO_BACK_OPTION3],
6944
+ secondary: [{ kind: "text", value: "resume Assessment" }]
6945
+ });
6946
+ if (choice === CONTINUE_EXECUTION_OPTION) return created;
6947
+ if (choice === RESTART_OPTION) {
6948
+ return createWithRecovery(ctx, appId, slug, indexName, { forceNew: true });
6949
+ }
6950
+ if (choice === GO_BACK_OPTION3) {
6951
+ return ctx.returnToStep("select-index");
6952
+ }
6953
+ throw new Error("data-assessment received an unexpected execution choice");
6954
+ }
6955
+ async function createWithRecovery(ctx, appId, slug, indexName, options) {
6956
+ return retryOnDashboardError(
6957
+ () => {
6958
+ showNotice(ctx, CREATING_EXECUTION_NOTICE);
6959
+ return createPlaybookExecution(appId, slug, indexName, options);
6960
+ },
6961
+ onRequestFailure(ctx, indexName)
6962
+ );
6963
+ }
6964
+ function onRequestFailure(ctx, indexName) {
6965
+ return async (err) => {
6966
+ await promptAssessmentRecovery(
6967
+ ctx,
6968
+ requestFailureMessage(indexName, err),
6969
+ isEmptyIndexError(err)
6970
+ );
6971
+ };
6972
+ }
6973
+ function requestFailureMessage(indexName, err) {
6974
+ if (isEmptyIndexError(err)) {
6975
+ return `Index "${indexName}" has no records to assess.`;
6976
+ }
6977
+ const message = err.errorBody()?.message;
6978
+ if (typeof message === "string" && message.trim()) return message;
6979
+ return "Couldn't complete the request.";
6980
+ }
6981
+ function isEmptyIndexError(err) {
6982
+ const body = err.errorBody();
6983
+ return body?.status_reason === "index_empty" || body?.message === "index_empty";
6984
+ }
6985
+ async function pollUntilAssessed(ctx, uuid, indexName) {
6986
+ const logId = ctx.logStart("Assessing index data");
6987
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
6988
+ let logStatus = "error";
6989
+ let stopNotices = startPollNotices(ctx);
6990
+ try {
6991
+ for (; ; ) {
6992
+ const execution = await retryOnDashboardError(
6993
+ () => getPlaybookExecution(uuid),
6994
+ async (err) => {
6995
+ stopNotices();
6996
+ ctx.clearNotices();
6997
+ await promptAssessmentRecovery(
6998
+ ctx,
6999
+ requestFailureMessage(indexName, err),
7000
+ isEmptyIndexError(err)
7001
+ );
7002
+ stopNotices = startPollNotices(ctx);
7003
+ }
7004
+ );
7005
+ if (execution.step_status === "done") {
7006
+ logStatus = "success";
7007
+ return execution;
7008
+ }
7009
+ if (execution.step_status === "error") {
7010
+ ctx.clearNotices();
7011
+ return execution;
7012
+ }
7013
+ if (Date.now() >= deadline) {
7014
+ throw new Error("Timed out waiting for the data assessment to finish.");
7015
+ }
7016
+ await sleep(POLL_INTERVAL_MS);
7017
+ }
7018
+ } catch (err) {
7019
+ ctx.clearNotices();
7020
+ throw err;
7021
+ } finally {
7022
+ stopNotices();
7023
+ ctx.logEnd(logId, logStatus);
7024
+ }
7025
+ }
7026
+ var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
7027
+ async function assessExecution(ctx, uuid, indexName) {
7028
+ await retryOnDashboardError(
7029
+ () => assessPlaybookExecution(uuid),
7030
+ onRequestFailure(ctx, indexName)
7031
+ );
7032
+ }
7033
+ async function promptAssessmentRecovery(ctx, message, retryDisabled) {
7034
+ const choice = await ctx.requestUserInput({
7035
+ prompt: "Data assessment failed.",
7036
+ promptType: "multipleChoice",
7037
+ options: [SWITCH_APPLICATION_OPTION, SWITCH_INDEX_OPTION, RETRY_OPTION],
7038
+ messages: [message],
7039
+ disabled: [false, false, retryDisabled],
7040
+ helpText: "Retry re-runs assessment on this index. Switch index or application to pick a different target."
7041
+ });
7042
+ if (choice === SWITCH_APPLICATION_OPTION) {
7043
+ return ctx.changeApplication();
7044
+ }
7045
+ if (choice === SWITCH_INDEX_OPTION) {
7046
+ return ctx.returnToStep("select-index");
7047
+ }
7048
+ if (choice === RETRY_OPTION) {
7049
+ return;
7050
+ }
7051
+ throw new Error("data-assessment received an unexpected recovery choice");
7052
+ }
7053
+ function assessmentFailureMessage(execution) {
7054
+ if (execution.status_reason === "index_empty") {
7055
+ return `Index "${execution.source_index_name}" has no records to assess.`;
7056
+ }
7057
+ return execution.status_reason ?? "Data assessment failed.";
7058
+ }
7059
+
6657
7060
  // src/lib/cli.ts
6658
7061
  var USAGE = `Usage: wizard [workflow-id] [options]
6659
7062
  wizard playbook [playbook-slug] [options]
@@ -6721,13 +7124,6 @@ var playbookWorkflow = {
6721
7124
  outputSchema: selectPlaybookSchema,
6722
7125
  run: (ctx) => selectPlaybookStep(ctx, getPlaybookSlugArg())
6723
7126
  }),
6724
- defineStep({
6725
- id: "confirm-playbook",
6726
- title: "Confirm selections",
6727
- outputSchema: confirmPlaybookSchema,
6728
- visible: false,
6729
- run: (ctx) => confirmPlaybookStep(ctx)
6730
- }),
6731
7127
  defineStep({
6732
7128
  id: "select-index",
6733
7129
  title: "Select index",
@@ -6742,6 +7138,12 @@ var playbookWorkflow = {
6742
7138
  visible: false,
6743
7139
  tips: ["apps-and-indices", "records-are-json", "speed"],
6744
7140
  run: (ctx) => confirmIndexStep(ctx)
7141
+ }),
7142
+ defineStep({
7143
+ id: "data-assessment",
7144
+ title: "Assess index data",
7145
+ outputSchema: playbookExecutionSchema,
7146
+ run: (ctx) => dataAssessmentStep(ctx)
6745
7147
  })
6746
7148
  ]
6747
7149
  };
@@ -6759,7 +7161,19 @@ function getWorkflow(id) {
6759
7161
  import { dirname as dirname6, join as join12 } from "node:path";
6760
7162
  import { fileURLToPath as fileURLToPath2 } from "node:url";
6761
7163
  import { useState as useState10 } from "react";
6762
- import { Box as Box11, Spacer, Text as Text12, useInput as useInput6, useWindowSize as useWindowSize5 } from "ink";
7164
+ import { Box as Box12, Spacer, Text as Text13, useInput as useInput6, useWindowSize as useWindowSize6 } from "ink";
7165
+
7166
+ // src/ui/ResizeNotice.tsx
7167
+ import { Box as Box11, Text as Text12, useWindowSize as useWindowSize5 } from "ink";
7168
+ import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
7169
+ function ResizeNotice() {
7170
+ const { columns, rows } = useWindowSize5();
7171
+ if (rows >= MIN_ROWS && columns >= MIN_COLUMNS) return null;
7172
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
7173
+ /* @__PURE__ */ jsx10(Text12, { color: COLORS.badge, children: `Terminal is ${columns}x${rows}. Recommended size is ${MIN_COLUMNS}x${MIN_ROWS}.` }),
7174
+ /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "Enlarge the pane for the full layout." })
7175
+ ] });
7176
+ }
6763
7177
 
6764
7178
  // src/ui/copy/welcome.ts
6765
7179
  var sidebarItems = [
@@ -6787,7 +7201,7 @@ var sidebarItems = [
6787
7201
 
6788
7202
  // src/ui/Welcome.tsx
6789
7203
  import Image, { TerminalInfoContext, defaultTerminalInfo } from "ink-picture";
6790
- import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
7204
+ import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
6791
7205
  var IMAGE_PATH = join12(dirname6(fileURLToPath2(import.meta.url)), "algolia.png");
6792
7206
  var TERMINAL_INFO = {
6793
7207
  ...defaultTerminalInfo,
@@ -6798,21 +7212,23 @@ function SidebarItem({
6798
7212
  title,
6799
7213
  description
6800
7214
  }) {
6801
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
6802
- /* @__PURE__ */ jsxs11(Box11, { gap: 1, children: [
6803
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.success, children: "\u2192" }),
6804
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.strong, bold: true, children: title })
7215
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
7216
+ /* @__PURE__ */ jsxs12(Box12, { gap: 1, children: [
7217
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.success, children: "\u2192" }),
7218
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: title })
6805
7219
  ] }),
6806
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", gap: 2, children: [
6807
- /* @__PURE__ */ jsx10(Spacer, {}),
6808
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: description })
7220
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 2, children: [
7221
+ /* @__PURE__ */ jsx11(Spacer, {}),
7222
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: description })
6809
7223
  ] })
6810
7224
  ] });
6811
7225
  }
6812
7226
  function Welcome() {
6813
7227
  const confirmStart = useWizard((s) => s.confirmStart);
6814
7228
  const openLearnMore = useWizard((s) => s.openLearnMore);
6815
- const { rows } = useWindowSize5();
7229
+ const { columns, rows } = useWindowSize6();
7230
+ const showSidebar = !isCompactTerminal(columns, rows) && columns > SIDEBAR_MIN_COLUMNS;
7231
+ const showLogo = showSidebar && rows >= MIN_ROWS && columns >= MIN_COLUMNS;
6816
7232
  const actions = [
6817
7233
  { label: "start wizard", run: confirmStart },
6818
7234
  { label: "learn more", run: openLearnMore }
@@ -6838,19 +7254,20 @@ function Welcome() {
6838
7254
  }
6839
7255
  };
6840
7256
  let layout = scales["large"];
6841
- if (rows < 30) {
7257
+ if (rows < MIN_ROWS) {
6842
7258
  layout = scales["small"];
6843
7259
  }
6844
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
6845
- /* @__PURE__ */ jsx10(
6846
- Box11,
7260
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
7261
+ /* @__PURE__ */ jsx11(
7262
+ Box12,
6847
7263
  {
6848
7264
  paddingY: layout.main.padding.y,
6849
7265
  paddingX: layout.main.padding.x,
6850
7266
  flexDirection: "column",
6851
7267
  justifyContent: "center",
6852
- children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", gap: 2, children: [
6853
- /* @__PURE__ */ jsx10(TerminalInfoContext.Provider, { value: TERMINAL_INFO, children: /* @__PURE__ */ jsx10(
7268
+ children: /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", gap: 2, children: [
7269
+ /* @__PURE__ */ jsx11(ResizeNotice, {}),
7270
+ showLogo && /* @__PURE__ */ jsx11(TerminalInfoContext.Provider, { value: TERMINAL_INFO, children: /* @__PURE__ */ jsx11(
6854
7271
  Image,
6855
7272
  {
6856
7273
  src: IMAGE_PATH,
@@ -6861,8 +7278,8 @@ function Welcome() {
6861
7278
  protocol: "halfBlock"
6862
7279
  }
6863
7280
  ) }),
6864
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
6865
- /* @__PURE__ */ jsx10(Box11, { flexDirection: "column", children: actions.map((action, i) => /* @__PURE__ */ jsx10(
7281
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
7282
+ /* @__PURE__ */ jsx11(Box12, { flexDirection: "column", children: actions.map((action, i) => /* @__PURE__ */ jsx11(
6866
7283
  SelectRow,
6867
7284
  {
6868
7285
  highlighted: i === index,
@@ -6871,21 +7288,21 @@ function Welcome() {
6871
7288
  },
6872
7289
  action.label
6873
7290
  )) }),
6874
- /* @__PURE__ */ jsxs11(Box11, { gap: 2, children: [
6875
- /* @__PURE__ */ jsxs11(Text12, { children: [
6876
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
6877
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.dim, children: " move" })
7291
+ /* @__PURE__ */ jsxs12(Box12, { gap: 2, children: [
7292
+ /* @__PURE__ */ jsxs12(Text13, { children: [
7293
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
7294
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.dim, children: " move" })
6878
7295
  ] }),
6879
- /* @__PURE__ */ jsxs11(Text12, { children: [
6880
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: "[enter]" }),
6881
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.dim, children: " confirm" })
7296
+ /* @__PURE__ */ jsxs12(Text13, { children: [
7297
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: "[enter]" }),
7298
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.dim, children: " confirm" })
6882
7299
  ] })
6883
7300
  ] })
6884
7301
  ] })
6885
7302
  }
6886
7303
  ),
6887
- /* @__PURE__ */ jsxs11(
6888
- Box11,
7304
+ showSidebar && /* @__PURE__ */ jsxs12(
7305
+ Box12,
6889
7306
  {
6890
7307
  backgroundColor: COLORS.bg.sidebar,
6891
7308
  width: 40,
@@ -6895,8 +7312,8 @@ function Welcome() {
6895
7312
  flexDirection: "column",
6896
7313
  justifyContent: "center",
6897
7314
  children: [
6898
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
6899
- sidebarItems.map((i, idx) => /* @__PURE__ */ jsx10(SidebarItem, { title: i.title, description: i.description }, idx))
7315
+ /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
7316
+ sidebarItems.map((i, idx) => /* @__PURE__ */ jsx11(SidebarItem, { title: i.title, description: i.description }, idx))
6900
7317
  ]
6901
7318
  }
6902
7319
  )
@@ -6904,8 +7321,7 @@ function Welcome() {
6904
7321
  }
6905
7322
 
6906
7323
  // src/ui/LearnMore.tsx
6907
- import { Fragment } from "react";
6908
- import { Box as Box12, Text as Text13, useInput as useInput7, useWindowSize as useWindowSize6 } from "ink";
7324
+ import { Box as Box13, Text as Text14, useInput as useInput7, useWindowSize as useWindowSize7 } from "ink";
6909
7325
 
6910
7326
  // src/ui/copy/learn-more.ts
6911
7327
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -6947,7 +7363,7 @@ var policyLinks = [
6947
7363
  ];
6948
7364
 
6949
7365
  // src/ui/LearnMore.tsx
6950
- import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
7366
+ import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
6951
7367
  var TAG_COLORS = {
6952
7368
  READ: COLORS.success,
6953
7369
  WRITE: COLORS.badge,
@@ -6958,92 +7374,156 @@ var TAG_COLORS = {
6958
7374
  var TAG_COLUMN_WIDTH = 10;
6959
7375
  var PADDING_X = 6;
6960
7376
  var NEVER_BOX_PAD_X = 2;
6961
- function NeverLine({
6962
- width,
6963
- segments = []
6964
- }) {
7377
+ var BLANK3 = { parts: [{ text: " ", color: COLORS.dim }] };
7378
+ function neverRow(width, segments = []) {
6965
7379
  const used = segments.reduce((n, s) => n + s.text.length, 0);
6966
7380
  const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
6967
- return /* @__PURE__ */ jsxs12(Text13, { color: COLORS.primary, children: [
6968
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.danger, children: "\u2502" }),
6969
- " ".repeat(NEVER_BOX_PAD_X),
6970
- segments.map((s, i) => /* @__PURE__ */ jsx11(Text13, { color: s.color, bold: s.bold, children: s.text }, i)),
6971
- " ".repeat(rightPad),
6972
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.danger, children: "\u2502" })
6973
- ] });
7381
+ return {
7382
+ parts: [
7383
+ { text: "\u2502", color: COLORS.danger },
7384
+ { text: " ".repeat(NEVER_BOX_PAD_X), color: COLORS.primary },
7385
+ ...segments.map((s) => ({
7386
+ text: s.text,
7387
+ color: s.color ?? COLORS.primary,
7388
+ bold: s.bold
7389
+ })),
7390
+ { text: " ".repeat(rightPad), color: COLORS.primary },
7391
+ { text: "\u2502", color: COLORS.danger }
7392
+ ]
7393
+ };
7394
+ }
7395
+ function buildLearnMoreRows(contentWidth) {
7396
+ const width = Math.max(contentWidth, 1);
7397
+ const rows = [];
7398
+ rows.push({
7399
+ parts: [
7400
+ {
7401
+ text: "What algolia wizard accesses",
7402
+ color: COLORS.strong,
7403
+ bold: true
7404
+ }
7405
+ ]
7406
+ });
7407
+ for (const line of wrapText(accessIntro, width)) {
7408
+ rows.push({ parts: [{ text: line, color: COLORS.muted }] });
7409
+ }
7410
+ for (const item of accessItems) {
7411
+ rows.push({
7412
+ parts: [{ text: "\u2500".repeat(width), color: COLORS.border }]
7413
+ });
7414
+ rows.push(BLANK3);
7415
+ const tag = `[${item.tag}]`.padEnd(TAG_COLUMN_WIDTH);
7416
+ const bodyWidth = Math.max(width - TAG_COLUMN_WIDTH, 1);
7417
+ const body = wrapText(`${item.title}: ${item.description}`, bodyWidth);
7418
+ body.forEach((line, i) => {
7419
+ const titleEnd = i === 0 ? item.title.length : 0;
7420
+ rows.push({
7421
+ parts: [
7422
+ {
7423
+ text: i === 0 ? tag : " ".repeat(TAG_COLUMN_WIDTH),
7424
+ color: TAG_COLORS[item.tag],
7425
+ bold: i === 0
7426
+ },
7427
+ {
7428
+ text: line.slice(0, titleEnd),
7429
+ color: COLORS.strong,
7430
+ bold: true
7431
+ },
7432
+ { text: line.slice(titleEnd), color: COLORS.muted }
7433
+ ]
7434
+ });
7435
+ });
7436
+ }
7437
+ const inner = Math.max(width - 2, 0);
7438
+ rows.push(BLANK3);
7439
+ rows.push({
7440
+ parts: [{ text: `\u256D${"\u2500".repeat(inner)}\u256E`, color: COLORS.danger }]
7441
+ });
7442
+ rows.push(neverRow(width));
7443
+ rows.push(
7444
+ neverRow(width, [{ text: "I NEVER", color: COLORS.danger, bold: true }])
7445
+ );
7446
+ for (const item of neverItems) {
7447
+ rows.push(neverRow(width));
7448
+ const marker = "\u2715 ";
7449
+ const bodyWidth = Math.max(width - 2 - NEVER_BOX_PAD_X - marker.length, 1);
7450
+ const lines = wrapText(item, bodyWidth);
7451
+ lines.forEach((line, i) => {
7452
+ rows.push(
7453
+ neverRow(
7454
+ width,
7455
+ i === 0 ? [
7456
+ { text: "\u2715", color: COLORS.danger },
7457
+ { text: " " },
7458
+ { text: line, color: COLORS.primary }
7459
+ ] : [
7460
+ { text: " ".repeat(marker.length) },
7461
+ { text: line, color: COLORS.primary }
7462
+ ]
7463
+ )
7464
+ );
7465
+ });
7466
+ }
7467
+ rows.push(neverRow(width));
7468
+ rows.push({
7469
+ parts: [{ text: `\u2570${"\u2500".repeat(inner)}\u256F`, color: COLORS.danger }]
7470
+ });
7471
+ rows.push(BLANK3);
7472
+ for (const link of policyLinks) {
7473
+ const prefix = `${link.label}: `;
7474
+ const urlLines = wrapText(link.url, Math.max(width - prefix.length, 1));
7475
+ urlLines.forEach((line, i) => {
7476
+ rows.push({
7477
+ parts: i === 0 ? [
7478
+ { text: prefix, color: COLORS.strong, bold: true },
7479
+ { text: line, color: COLORS.accent }
7480
+ ] : [{ text: `${" ".repeat(prefix.length)}${line}`, color: COLORS.accent }]
7481
+ });
7482
+ });
7483
+ }
7484
+ return rows;
6974
7485
  }
6975
7486
  function LearnMore() {
6976
7487
  const confirmStart = useWizard((s) => s.confirmStart);
6977
7488
  const backToHome = useWizard((s) => s.backToHome);
6978
- const { columns } = useWindowSize6();
6979
- const dividerWidth = Math.max(0, columns - PADDING_X * 2);
6980
- useInput7((_input, key) => {
7489
+ const { columns } = useWindowSize7();
7490
+ const contentWidth = Math.max(columns - PADDING_X * 2, 1);
7491
+ const rows = buildLearnMoreRows(contentWidth);
7492
+ const scroll = useScrollWindow({ itemCount: rows.length });
7493
+ const scrollable = scroll.maxOffset > 0;
7494
+ useInput7((input, key) => {
6981
7495
  if (key.escape) backToHome();
6982
7496
  else if (key.return) confirmStart();
7497
+ else if (scrollable && (input === "k" || key.upArrow)) scroll.scrollBy(-1);
7498
+ else if (scrollable && (input === "j" || key.downArrow)) scroll.scrollBy(1);
6983
7499
  });
6984
- return /* @__PURE__ */ jsxs12(
6985
- Box12,
7500
+ const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
7501
+ return /* @__PURE__ */ jsxs13(
7502
+ Box13,
6986
7503
  {
6987
7504
  flexDirection: "column",
6988
7505
  paddingX: PADDING_X,
6989
7506
  paddingY: 2,
6990
7507
  width: "100%",
6991
- gap: 1,
7508
+ flexGrow: 1,
6992
7509
  children: [
6993
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
6994
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: accessIntro }),
6995
- /* @__PURE__ */ jsx11(Box12, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
6996
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
6997
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, marginTop: 1, children: [
6998
- /* @__PURE__ */ jsx11(Box12, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text13, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
6999
- /* @__PURE__ */ jsx11(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.primary, children: [
7000
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: item.title }),
7001
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
7002
- ] }) })
7003
- ] })
7004
- ] }, item.tag)) }),
7005
- /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
7006
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
7007
- /* @__PURE__ */ jsx11(NeverLine, { width: dividerWidth }),
7008
- /* @__PURE__ */ jsx11(
7009
- NeverLine,
7010
- {
7011
- width: dividerWidth,
7012
- segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
7013
- }
7014
- ),
7015
- neverItems.map((item) => /* @__PURE__ */ jsxs12(Fragment, { children: [
7016
- /* @__PURE__ */ jsx11(NeverLine, { width: dividerWidth }),
7017
- /* @__PURE__ */ jsx11(
7018
- NeverLine,
7019
- {
7020
- width: dividerWidth,
7021
- segments: [
7022
- { text: "\u2715", color: COLORS.danger },
7023
- { text: " " },
7024
- { text: item, color: COLORS.primary }
7025
- ]
7026
- }
7027
- )
7028
- ] }, item)),
7029
- /* @__PURE__ */ jsx11(NeverLine, { width: dividerWidth }),
7030
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
7510
+ /* @__PURE__ */ jsx12(ResizeNotice, {}),
7511
+ /* @__PURE__ */ jsx12(ScrollView, { scroll, children: visible.map((row, i) => /* @__PURE__ */ jsx12(Text14, { wrap: "truncate", children: row.parts.map((part, j) => /* @__PURE__ */ jsx12(Text14, { color: part.color, bold: part.bold, children: part.text }, j)) }, `learn-more-${scroll.offset}-${i}`)) }),
7512
+ scrollable && /* @__PURE__ */ jsxs13(Text14, { children: [
7513
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: "[j] [k]" }),
7514
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.dim, children: " scroll" })
7031
7515
  ] }),
7032
- /* @__PURE__ */ jsx11(Box12, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
7033
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
7034
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.accent, children: link.url })
7035
- ] }, link.label)) }),
7036
- /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "row", gap: 3, children: [
7037
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
7038
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "[" }),
7039
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: "esc" }),
7040
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "] back" })
7516
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", gap: 3, children: [
7517
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", gap: 1, children: [
7518
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: "[" }),
7519
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: "esc" }),
7520
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: "] back" })
7041
7521
  ] }),
7042
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
7043
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "[" }),
7044
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: "enter" }),
7045
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "]" }),
7046
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.success, bold: true, children: "start wizard" })
7522
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", gap: 1, children: [
7523
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: "[" }),
7524
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.primary, children: "enter" }),
7525
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: "]" }),
7526
+ /* @__PURE__ */ jsx12(Text14, { color: COLORS.success, bold: true, children: "start wizard" })
7047
7527
  ] })
7048
7528
  ] })
7049
7529
  ]
@@ -7052,17 +7532,17 @@ function LearnMore() {
7052
7532
  }
7053
7533
 
7054
7534
  // src/ui/Sidebar.tsx
7055
- import { Box as Box15, Text as Text16 } from "ink";
7535
+ import { Box as Box16, Text as Text17 } from "ink";
7056
7536
 
7057
7537
  // src/ui/Steps.tsx
7058
- import { Box as Box13, Text as Text14 } from "ink";
7538
+ import { Box as Box14, Text as Text15 } from "ink";
7059
7539
  import Spinner2 from "ink-spinner";
7060
- import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
7540
+ import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
7061
7541
  function Steps() {
7062
7542
  const { steps } = useWizard();
7063
7543
  const visibleSteps = steps.filter(isStepVisible);
7064
- return /* @__PURE__ */ jsx12(Box13, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx12(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status[s.status], children: [
7065
- s.status === "running" ? /* @__PURE__ */ jsx12(Spinner2, { type: "dots" }) : MARKER[s.status],
7544
+ return /* @__PURE__ */ jsx13(Box14, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx13(Box14, { flexDirection: "column", children: /* @__PURE__ */ jsxs14(Text15, { color: COLORS.status[s.status], children: [
7545
+ s.status === "running" ? /* @__PURE__ */ jsx13(Spinner2, { type: "dots" }) : MARKER[s.status],
7066
7546
  " ",
7067
7547
  s.title
7068
7548
  ] }) }, s.id)) });
@@ -7071,27 +7551,27 @@ function CurrentStep() {
7071
7551
  const { steps } = useWizard();
7072
7552
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
7073
7553
  if (!currentStep) return null;
7074
- return /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.running, children: [
7075
- /* @__PURE__ */ jsx12(Spinner2, { type: "dots" }),
7554
+ return /* @__PURE__ */ jsxs14(Text15, { color: COLORS.status.running, children: [
7555
+ /* @__PURE__ */ jsx13(Spinner2, { type: "dots" }),
7076
7556
  " ",
7077
7557
  ` ${currentStep.title}`
7078
7558
  ] });
7079
7559
  }
7080
7560
 
7081
7561
  // src/ui/Progress.tsx
7082
- import { Box as Box14, Text as Text15 } from "ink";
7083
- import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
7562
+ import { Box as Box15, Text as Text16 } from "ink";
7563
+ import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
7084
7564
  function Progress() {
7085
7565
  const { steps, currentStepIndex } = useWizard();
7086
7566
  const visibleSteps = steps.filter(isStepVisible);
7087
7567
  if (visibleSteps.length === 0) return null;
7088
7568
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
7089
7569
  const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
7090
- return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "row", gap: 1, children: [
7091
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: "STEP" }),
7092
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: activeStepNumber }),
7093
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: "/" }),
7094
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: visibleSteps.length })
7570
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", gap: 1, children: [
7571
+ /* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: "STEP" }),
7572
+ /* @__PURE__ */ jsx14(Text16, { color: COLORS.strong, bold: true, children: activeStepNumber }),
7573
+ /* @__PURE__ */ jsx14(Text16, { color: COLORS.strong, bold: true, children: "/" }),
7574
+ /* @__PURE__ */ jsx14(Text16, { color: COLORS.strong, bold: true, children: visibleSteps.length })
7095
7575
  ] });
7096
7576
  }
7097
7577
 
@@ -7102,10 +7582,10 @@ var sidebarCommands = [
7102
7582
  ];
7103
7583
 
7104
7584
  // src/ui/Sidebar.tsx
7105
- import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
7585
+ import { jsx as jsx15, jsxs as jsxs16 } from "react/jsx-runtime";
7106
7586
  function Sidebar() {
7107
- return /* @__PURE__ */ jsxs15(
7108
- Box15,
7587
+ return /* @__PURE__ */ jsxs16(
7588
+ Box16,
7109
7589
  {
7110
7590
  backgroundColor: "#14171E",
7111
7591
  width: 30,
@@ -7114,16 +7594,16 @@ function Sidebar() {
7114
7594
  flexDirection: "column",
7115
7595
  justifyContent: "space-between",
7116
7596
  children: [
7117
- /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", gap: 1, children: [
7118
- /* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: "PROGRESS" }),
7119
- /* @__PURE__ */ jsx14(Steps, {})
7597
+ /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", gap: 1, children: [
7598
+ /* @__PURE__ */ jsx15(Text17, { color: COLORS.muted, children: "PROGRESS" }),
7599
+ /* @__PURE__ */ jsx15(Steps, {})
7120
7600
  ] }),
7121
- /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", gap: 1, children: [
7122
- /* @__PURE__ */ jsx14(Progress, {}),
7123
- /* @__PURE__ */ jsx14(Box15, { flexDirection: "column", children: sidebarCommands.map((c) => {
7124
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", gap: 1, children: [
7125
- /* @__PURE__ */ jsx14(Text16, { color: COLORS.primary, children: `[${c.keyHint}]` }),
7126
- /* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: c.description })
7601
+ /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", gap: 1, children: [
7602
+ /* @__PURE__ */ jsx15(Progress, {}),
7603
+ /* @__PURE__ */ jsx15(Box16, { flexDirection: "column", children: sidebarCommands.map((c) => {
7604
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "row", gap: 1, children: [
7605
+ /* @__PURE__ */ jsx15(Text17, { color: COLORS.primary, children: `[${c.keyHint}]` }),
7606
+ /* @__PURE__ */ jsx15(Text17, { color: COLORS.muted, children: c.description })
7127
7607
  ] }, c.keyHint);
7128
7608
  }) })
7129
7609
  ] })
@@ -7133,12 +7613,12 @@ function Sidebar() {
7133
7613
  }
7134
7614
 
7135
7615
  // src/ui/Ribbon.tsx
7136
- import { Box as Box16, Text as Text17 } from "ink";
7137
- import { jsx as jsx15, jsxs as jsxs16 } from "react/jsx-runtime";
7616
+ import { Box as Box17, Text as Text18 } from "ink";
7617
+ import { jsx as jsx16, jsxs as jsxs17 } from "react/jsx-runtime";
7138
7618
  function Ribbon() {
7139
7619
  const firstCommand = sidebarCommands[0];
7140
- return /* @__PURE__ */ jsxs16(
7141
- Box16,
7620
+ return /* @__PURE__ */ jsxs17(
7621
+ Box17,
7142
7622
  {
7143
7623
  backgroundColor: "#14171E",
7144
7624
  flexDirection: "row",
@@ -7146,11 +7626,11 @@ function Ribbon() {
7146
7626
  paddingX: 2,
7147
7627
  paddingY: 1,
7148
7628
  children: [
7149
- /* @__PURE__ */ jsx15(Progress, {}),
7150
- /* @__PURE__ */ jsx15(CurrentStep, {}),
7151
- /* @__PURE__ */ jsxs16(Box16, { flexDirection: "row", gap: 1, children: [
7152
- /* @__PURE__ */ jsx15(Text17, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
7153
- /* @__PURE__ */ jsx15(Text17, { color: COLORS.muted, children: firstCommand.description })
7629
+ /* @__PURE__ */ jsx16(Progress, {}),
7630
+ /* @__PURE__ */ jsx16(CurrentStep, {}),
7631
+ /* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", gap: 1, children: [
7632
+ /* @__PURE__ */ jsx16(Text18, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
7633
+ /* @__PURE__ */ jsx16(Text18, { color: COLORS.muted, children: firstCommand.description })
7154
7634
  ] })
7155
7635
  ]
7156
7636
  }
@@ -7161,8 +7641,8 @@ function Ribbon() {
7161
7641
  import { useState as useState12 } from "react";
7162
7642
 
7163
7643
  // src/ui/Logs.tsx
7164
- import { Box as Box17, Text as Text18, useInput as useInput8 } from "ink";
7165
- import { jsx as jsx16, jsxs as jsxs17 } from "react/jsx-runtime";
7644
+ import { Box as Box18, Text as Text19, useInput as useInput8 } from "ink";
7645
+ import { jsx as jsx17, jsxs as jsxs18 } from "react/jsx-runtime";
7166
7646
  var KIND_COLOR = {
7167
7647
  tool: COLORS.primary,
7168
7648
  prompt: COLORS.badge
@@ -7198,9 +7678,9 @@ function Logs() {
7198
7678
  else if (input === "j" || key.downArrow) scroll.scrollBy(1);
7199
7679
  });
7200
7680
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
7201
- return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
7202
- logs.length === 0 && /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: "No logs yet." }),
7203
- /* @__PURE__ */ jsx16(ScrollView, { scroll, children: visible.map((entry) => {
7681
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
7682
+ logs.length === 0 && /* @__PURE__ */ jsx17(Text19, { color: COLORS.dim, children: "No logs yet." }),
7683
+ /* @__PURE__ */ jsx17(ScrollView, { scroll, children: visible.map((entry) => {
7204
7684
  const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
7205
7685
  const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
7206
7686
  const rawPreview = rawInputText(entry.input);
@@ -7210,28 +7690,28 @@ function Logs() {
7210
7690
  const name = truncate3(entry.name, budget);
7211
7691
  budget -= name.length;
7212
7692
  const preview = rawPreview ? truncate3(rawPreview, budget) : "";
7213
- return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", gap: ROW_GAP, children: [
7214
- /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: timestamp }),
7215
- /* @__PURE__ */ jsx16(Text18, { color: logNameColor(entry), wrap: "truncate", children: name }),
7216
- preview && /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, wrap: "truncate", children: preview }),
7217
- durationText && /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: durationText })
7693
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "row", gap: ROW_GAP, children: [
7694
+ /* @__PURE__ */ jsx17(Text19, { color: COLORS.dim, children: timestamp }),
7695
+ /* @__PURE__ */ jsx17(Text19, { color: logNameColor(entry), wrap: "truncate", children: name }),
7696
+ preview && /* @__PURE__ */ jsx17(Text19, { color: COLORS.dim, wrap: "truncate", children: preview }),
7697
+ durationText && /* @__PURE__ */ jsx17(Text19, { color: COLORS.dim, children: durationText })
7218
7698
  ] }, entry.id);
7219
7699
  }) }),
7220
- /* @__PURE__ */ jsxs17(Text18, { children: [
7221
- /* @__PURE__ */ jsx16(Text18, { color: COLORS.primary, children: "[j] [k]" }),
7222
- /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: " scroll" })
7700
+ /* @__PURE__ */ jsxs18(Text19, { children: [
7701
+ /* @__PURE__ */ jsx17(Text19, { color: COLORS.primary, children: "[j] [k]" }),
7702
+ /* @__PURE__ */ jsx17(Text19, { color: COLORS.dim, children: " scroll" })
7223
7703
  ] })
7224
7704
  ] });
7225
7705
  }
7226
7706
 
7227
7707
  // src/ui/Tips.tsx
7228
7708
  import { useEffect as useEffect3, useRef as useRef4, useState as useState11 } from "react";
7229
- import { Box as Box19, Text as Text20 } from "ink";
7709
+ import { Box as Box20, Text as Text21 } from "ink";
7230
7710
  import terminalLink from "terminal-link";
7231
7711
 
7232
7712
  // src/ui/Code.tsx
7233
- import { Box as Box18, Text as Text19 } from "ink";
7234
- import { jsx as jsx17 } from "react/jsx-runtime";
7713
+ import { Box as Box19, Text as Text20 } from "ink";
7714
+ import { jsx as jsx18 } from "react/jsx-runtime";
7235
7715
  var TOKEN_RE = /("(?:\\.|[^"\\])*"|\btrue\b|\bfalse\b|\bnull\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[{}[\]:,])/g;
7236
7716
  function highlightJson(json) {
7237
7717
  const parts = json.split(TOKEN_RE);
@@ -7240,22 +7720,22 @@ function highlightJson(json) {
7240
7720
  if (part[0] === '"') {
7241
7721
  const next = parts.slice(i + 1).find((p) => p.trim());
7242
7722
  const isKey = next?.trimStart().startsWith(":");
7243
- return /* @__PURE__ */ jsx17(Text19, { color: isKey ? "cyan" : "green", children: part }, i);
7723
+ return /* @__PURE__ */ jsx18(Text20, { color: isKey ? "cyan" : "green", children: part }, i);
7244
7724
  }
7245
7725
  if (part === "true" || part === "false" || part === "null") {
7246
- return /* @__PURE__ */ jsx17(Text19, { color: "magenta", children: part }, i);
7726
+ return /* @__PURE__ */ jsx18(Text20, { color: "magenta", children: part }, i);
7247
7727
  }
7248
7728
  if (/^-?\d/.test(part)) {
7249
- return /* @__PURE__ */ jsx17(Text19, { color: "yellow", children: part }, i);
7729
+ return /* @__PURE__ */ jsx18(Text20, { color: "yellow", children: part }, i);
7250
7730
  }
7251
7731
  if (/^[{}[\]:,]$/.test(part)) {
7252
- return /* @__PURE__ */ jsx17(Text19, { color: COLORS.dim, children: part }, i);
7732
+ return /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: part }, i);
7253
7733
  }
7254
- return /* @__PURE__ */ jsx17(Text19, { children: part }, i);
7734
+ return /* @__PURE__ */ jsx18(Text20, { children: part }, i);
7255
7735
  });
7256
7736
  }
7257
7737
  function Code({ children }) {
7258
- return /* @__PURE__ */ jsx17(Box18, { children: /* @__PURE__ */ jsx17(Text19, { children: highlightJson(children) }) });
7738
+ return /* @__PURE__ */ jsx18(Box19, { children: /* @__PURE__ */ jsx18(Text20, { children: highlightJson(children) }) });
7259
7739
  }
7260
7740
 
7261
7741
  // src/ui/copy/tips.ts
@@ -7469,26 +7949,26 @@ function tipsByIds(ids) {
7469
7949
  }
7470
7950
 
7471
7951
  // src/ui/Tips.tsx
7472
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
7952
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
7473
7953
  var TICK_MS = 16;
7474
7954
  var CHUNK_HOLD_MS = 2e3;
7475
7955
  var HOLD_MS = 12e3;
7476
7956
  function TipTitle({ children }) {
7477
- return /* @__PURE__ */ jsxs18(Box19, { gap: 1, children: [
7478
- /* @__PURE__ */ jsx18(Text20, { color: "cyan", children: "\u2726" }),
7479
- /* @__PURE__ */ jsx18(Text20, { color: "white", bold: true, children })
7957
+ return /* @__PURE__ */ jsxs19(Box20, { gap: 1, children: [
7958
+ /* @__PURE__ */ jsx19(Text21, { color: "cyan", children: "\u2726" }),
7959
+ /* @__PURE__ */ jsx19(Text21, { color: "white", bold: true, children })
7480
7960
  ] });
7481
7961
  }
7482
7962
  function InlineSegment({ segment }) {
7483
7963
  switch (segment.type) {
7484
7964
  case "highlight":
7485
- return /* @__PURE__ */ jsx18(Text20, { color: COLORS.success, children: segment.value });
7965
+ return /* @__PURE__ */ jsx19(Text21, { color: COLORS.success, children: segment.value });
7486
7966
  case "link":
7487
- return /* @__PURE__ */ jsx18(Text20, { color: "cyan", underline: true, children: segment.href ? terminalLink(segment.value, segment.href) : segment.value });
7967
+ return /* @__PURE__ */ jsx19(Text21, { color: "cyan", underline: true, children: segment.href ? terminalLink(segment.value, segment.href) : segment.value });
7488
7968
  case "codeword":
7489
- return /* @__PURE__ */ jsx18(Text20, { color: COLORS.highlight.fg, backgroundColor: COLORS.highlight.bg, children: segment.value });
7969
+ return /* @__PURE__ */ jsx19(Text21, { color: COLORS.highlight.fg, backgroundColor: COLORS.highlight.bg, children: segment.value });
7490
7970
  default:
7491
- return /* @__PURE__ */ jsx18(Text20, { color: COLORS.muted, children: segment.value });
7971
+ return /* @__PURE__ */ jsx19(Text21, { color: COLORS.muted, children: segment.value });
7492
7972
  }
7493
7973
  }
7494
7974
  function TipContent({
@@ -7522,10 +8002,10 @@ function TipContent({
7522
8002
  blocks.push({ type: "inline", segments: [segment] });
7523
8003
  }
7524
8004
  }
7525
- return /* @__PURE__ */ jsx18(Box19, { flexDirection: "column", marginLeft: 2, gap: 1, children: blocks.map(
7526
- (block, i) => block.type === "code" ? /* @__PURE__ */ jsx18(Code, { children: block.segment.value }, i) : /* @__PURE__ */ jsx18(Text20, { children: block.segments.map((segment, j) => /* @__PURE__ */ jsxs18(Text20, { children: [
8005
+ return /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginLeft: 2, gap: 1, children: blocks.map(
8006
+ (block, i) => block.type === "code" ? /* @__PURE__ */ jsx19(Code, { children: block.segment.value }, i) : /* @__PURE__ */ jsx19(Text21, { children: block.segments.map((segment, j) => /* @__PURE__ */ jsxs19(Text21, { children: [
7527
8007
  j > 0 && " ",
7528
- /* @__PURE__ */ jsx18(InlineSegment, { segment })
8008
+ /* @__PURE__ */ jsx19(InlineSegment, { segment })
7529
8009
  ] }, j)) }, i)
7530
8010
  ) });
7531
8011
  }
@@ -7602,14 +8082,51 @@ function Tips({
7602
8082
  if (!tip) return null;
7603
8083
  const titleRevealed = tip.title.slice(0, revealed);
7604
8084
  const contentRevealed = Math.max(0, revealed - tip.title.length);
7605
- return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", marginBottom: 1, gap: 1, children: [
7606
- /* @__PURE__ */ jsx18(TipTitle, { children: titleRevealed }),
7607
- /* @__PURE__ */ jsx18(TipContent, { chunks: tip.chunks, revealed: contentRevealed })
8085
+ return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", marginBottom: 1, gap: 1, children: [
8086
+ /* @__PURE__ */ jsx19(TipTitle, { children: titleRevealed }),
8087
+ /* @__PURE__ */ jsx19(TipContent, { chunks: tip.chunks, revealed: contentRevealed })
7608
8088
  ] });
7609
8089
  }
7610
8090
 
8091
+ // src/ui/useRepaintOnResize.ts
8092
+ import { useEffect as useEffect4 } from "react";
8093
+ import { useApp, useStdout } from "ink";
8094
+ var BEGIN_SYNC = "\x1B[?2026h";
8095
+ var END_SYNC = "\x1B[?2026l";
8096
+ var CLEAR_SCREEN_AND_SCROLLBACK = "\x1B[2J\x1B[3J\x1B[H";
8097
+ function useRepaintOnResize(debounceMs = 100) {
8098
+ const { stdout } = useStdout();
8099
+ const { suspendTerminal } = useApp();
8100
+ useEffect4(() => {
8101
+ if (!stdout.isTTY) return;
8102
+ let timer;
8103
+ let repaint = Promise.resolve();
8104
+ const onResize = () => {
8105
+ clearTimeout(timer);
8106
+ timer = setTimeout(() => {
8107
+ repaint = repaint.then(async () => {
8108
+ stdout.write(BEGIN_SYNC);
8109
+ try {
8110
+ await suspendTerminal(() => {
8111
+ stdout.write(CLEAR_SCREEN_AND_SCROLLBACK);
8112
+ });
8113
+ } finally {
8114
+ stdout.write(END_SYNC);
8115
+ }
8116
+ }).catch(() => {
8117
+ });
8118
+ }, debounceMs);
8119
+ };
8120
+ stdout.on("resize", onResize);
8121
+ return () => {
8122
+ clearTimeout(timer);
8123
+ stdout.off("resize", onResize);
8124
+ };
8125
+ }, [stdout, suspendTerminal, debounceMs]);
8126
+ }
8127
+
7611
8128
  // src/ui/App.tsx
7612
- import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
8129
+ import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
7613
8130
  function App() {
7614
8131
  const {
7615
8132
  phase,
@@ -7624,8 +8141,10 @@ function App() {
7624
8141
  loadingMessage,
7625
8142
  review: review2
7626
8143
  } = useWizard();
7627
- const { exit } = useApp();
7628
- const { columns, rows } = useWindowSize7();
8144
+ const { exit } = useApp2();
8145
+ const { columns, rows } = useWindowSize8();
8146
+ useRepaintOnResize();
8147
+ const compactRun = isCompactTerminal(columns, rows);
7629
8148
  const [showLogs, setShowLogs] = useState12(false);
7630
8149
  const [tipState, setTipState] = useState12("idle");
7631
8150
  const finished = phase === "done" || phase === "error";
@@ -7636,10 +8155,10 @@ function App() {
7636
8155
  const isAwaitingUserInput = phase === "awaitingInput" && !!inputReq;
7637
8156
  const isCommandApprovalPrompt = isAwaitingUserInput && inputReq?.promptType === "commandApproval";
7638
8157
  const promptPending = isAwaitingUserInput && !isCommandApprovalPrompt;
7639
- const holdForTip = stepHasTips && promptPending && tipState === "revealing";
7640
- const showTips = stepHasTips && (phase === "running" && currentStep?.status === "running" || isCommandApprovalPrompt || holdForTip);
8158
+ const holdForTip = !compactRun && stepHasTips && promptPending && tipState === "revealing";
8159
+ const showTips = !compactRun && stepHasTips && (phase === "running" && currentStep?.status === "running" && !loadingMessage && !settingUpAppId || isCommandApprovalPrompt);
7641
8160
  const showNoticesInMain = !stepHasTips;
7642
- const showNotices = (!isAwaitingUserInput || holdForTip) && !review2;
8161
+ const showNotices = !compactRun && (!isAwaitingUserInput || holdForTip) && !review2;
7643
8162
  useInput9(
7644
8163
  (_input, key) => {
7645
8164
  if (key.return) {
@@ -7672,32 +8191,31 @@ function App() {
7672
8191
  }
7673
8192
  });
7674
8193
  const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
7675
- const flexDirection = columns > 90 ? "row" : "column";
8194
+ const flexDirection = columns > SIDEBAR_MIN_COLUMNS ? "row" : "column";
7676
8195
  const showSidebar = flexDirection === "row";
7677
- const scrollsPastViewport = phase === "idle" && homeScreen === "learnMore";
7678
8196
  return (
7679
8197
  /* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
7680
- the whole screen, and the scrolling throws off its cursor arithmetic —
7681
- flicker and leftover rows. */
7682
- /* @__PURE__ */ jsxs19(
7683
- Box20,
8198
+ the whole screen, and the scrolling throws off its cursor arithmetic.
8199
+ Flicker and leftover rows. */
8200
+ /* @__PURE__ */ jsxs20(
8201
+ Box21,
7684
8202
  {
7685
8203
  backgroundColor: COLORS.bg.main,
7686
8204
  flexDirection: "row",
7687
8205
  width: columns,
7688
- height: scrollsPastViewport ? void 0 : rows,
7689
- overflow: scrollsPastViewport ? "visible" : "hidden",
8206
+ height: rows,
8207
+ overflow: "hidden",
7690
8208
  children: [
7691
- mainWindowVisible && /* @__PURE__ */ jsxs19(
7692
- Box20,
8209
+ mainWindowVisible && /* @__PURE__ */ jsxs20(
8210
+ Box21,
7693
8211
  {
7694
8212
  flexDirection,
7695
8213
  width: "100%",
7696
8214
  maxHeight: rows,
7697
8215
  justifyContent: "space-between",
7698
8216
  children: [
7699
- showLogs ? /* @__PURE__ */ jsx19(Logs, {}) : /* @__PURE__ */ jsxs19(
7700
- Box20,
8217
+ showLogs ? /* @__PURE__ */ jsx20(Logs, {}) : /* @__PURE__ */ jsxs20(
8218
+ Box21,
7701
8219
  {
7702
8220
  flexDirection: "column",
7703
8221
  paddingX: 4,
@@ -7706,35 +8224,35 @@ function App() {
7706
8224
  flexGrow: 1,
7707
8225
  gap: 1,
7708
8226
  children: [
7709
- /* @__PURE__ */ jsxs19(Box20, { flexGrow: 2, flexDirection: "column", children: [
7710
- phase === "preflight" && !user && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7711
- /* @__PURE__ */ jsx19(Spinner3, { type: "dots" }),
8227
+ /* @__PURE__ */ jsxs20(Box21, { flexGrow: 2, flexDirection: "column", children: [
8228
+ phase === "preflight" && !user && /* @__PURE__ */ jsx20(Box21, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text22, { color: COLORS.strong, bold: true, children: [
8229
+ /* @__PURE__ */ jsx20(Spinner3, { type: "dots" }),
7712
8230
  " Signing in to Algolia"
7713
8231
  ] }) }),
7714
- phase === "preflight" && user && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7715
- /* @__PURE__ */ jsx19(Spinner3, { type: "dots" }),
8232
+ phase === "preflight" && user && /* @__PURE__ */ jsx20(Box21, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text22, { color: COLORS.strong, bold: true, children: [
8233
+ /* @__PURE__ */ jsx20(Spinner3, { type: "dots" }),
7716
8234
  " Getting things ready"
7717
8235
  ] }) }),
7718
- phase === "authenticating" && /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", marginBottom: 1, children: [
7719
- /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7720
- /* @__PURE__ */ jsx19(Spinner3, { type: "dots" }),
8236
+ phase === "authenticating" && /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", marginBottom: 1, children: [
8237
+ /* @__PURE__ */ jsxs20(Text22, { color: COLORS.strong, bold: true, children: [
8238
+ /* @__PURE__ */ jsx20(Spinner3, { type: "dots" }),
7721
8239
  " Signing in to Algolia"
7722
8240
  ] }),
7723
- /* @__PURE__ */ jsx19(Text21, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
8241
+ /* @__PURE__ */ jsx20(Text22, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
7724
8242
  ] }),
7725
- settingUpAppId && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7726
- /* @__PURE__ */ jsx19(Spinner3, { type: "dots" }),
8243
+ settingUpAppId && /* @__PURE__ */ jsx20(Box21, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text22, { color: COLORS.strong, bold: true, children: [
8244
+ /* @__PURE__ */ jsx20(Spinner3, { type: "dots" }),
7727
8245
  " Setting up Wizard using app",
7728
8246
  " ",
7729
8247
  settingUpAppId
7730
8248
  ] }) }),
7731
- phase === "running" && !settingUpAppId && loadingMessage && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7732
- /* @__PURE__ */ jsx19(Spinner3, { type: "dots" }),
8249
+ phase === "running" && !settingUpAppId && loadingMessage && /* @__PURE__ */ jsx20(Box21, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text22, { color: COLORS.strong, bold: true, children: [
8250
+ /* @__PURE__ */ jsx20(Spinner3, { type: "dots" }),
7733
8251
  " ",
7734
8252
  loadingMessage
7735
8253
  ] }) }),
7736
- /* @__PURE__ */ jsx19(CliOutput, {}),
7737
- showTips && currentStep && /* @__PURE__ */ jsx19(
8254
+ /* @__PURE__ */ jsx20(CliOutput, {}),
8255
+ showTips && currentStep && /* @__PURE__ */ jsx20(
7738
8256
  Tips,
7739
8257
  {
7740
8258
  stepId: currentStep.id,
@@ -7742,24 +8260,24 @@ function App() {
7742
8260
  onRevealStateChange: setTipState
7743
8261
  }
7744
8262
  ),
7745
- showNoticesInMain && showNotices && /* @__PURE__ */ jsx19(Notices, { showAll: true, border: false }),
7746
- /* @__PURE__ */ jsx19(Review, {}),
7747
- !showTips && /* @__PURE__ */ jsx19(PromptInput, {}),
7748
- phase === "error" && error && /* @__PURE__ */ jsx19(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.status.error, children: [
8263
+ showNoticesInMain && showNotices && /* @__PURE__ */ jsx20(Notices, { showAll: true, border: false }),
8264
+ /* @__PURE__ */ jsx20(Review, {}),
8265
+ !showTips && /* @__PURE__ */ jsx20(PromptInput, {}),
8266
+ phase === "error" && error && /* @__PURE__ */ jsx20(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text22, { color: COLORS.status.error, children: [
7749
8267
  "\u2716 ",
7750
8268
  error
7751
8269
  ] }) })
7752
8270
  ] }),
7753
- !showNoticesInMain && showNotices && /* @__PURE__ */ jsx19(Notices, {}),
7754
- showTips && !holdForTip && /* @__PURE__ */ jsx19(PromptInput, {})
8271
+ !showNoticesInMain && showNotices && /* @__PURE__ */ jsx20(Notices, {}),
8272
+ showTips && !holdForTip && /* @__PURE__ */ jsx20(PromptInput, {})
7755
8273
  ]
7756
8274
  }
7757
8275
  ),
7758
- showSidebar ? /* @__PURE__ */ jsx19(Sidebar, {}) : /* @__PURE__ */ jsx19(Ribbon, {})
8276
+ showSidebar ? /* @__PURE__ */ jsx20(Sidebar, {}) : /* @__PURE__ */ jsx20(Ribbon, {})
7759
8277
  ]
7760
8278
  }
7761
8279
  ),
7762
- phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx19(LearnMore, {}) : /* @__PURE__ */ jsx19(Welcome, {}))
8280
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx20(LearnMore, {}) : /* @__PURE__ */ jsx20(Welcome, {}))
7763
8281
  ]
7764
8282
  }
7765
8283
  )
@@ -7826,6 +8344,23 @@ var selectPlaybookIndex = {
7826
8344
  indexName: "wizard_seed_orders",
7827
8345
  sampleIndex: false
7828
8346
  };
8347
+ var dataAssessment = {
8348
+ uuid: "seed-exec-uuid",
8349
+ playbook_slug: selectPlaybook.slug,
8350
+ application_id: "SEEDAPPID",
8351
+ source_index_name: selectPlaybookIndex.indexName,
8352
+ current_step: "assessment",
8353
+ step_status: "done",
8354
+ status_reason: null,
8355
+ completed: true,
8356
+ assessment_result: {
8357
+ ready: true,
8358
+ schema_comparison: [],
8359
+ settings_recommendation: {}
8360
+ },
8361
+ settings_snapshot: null,
8362
+ created_at: "2026-09-22T09:00:00.000Z"
8363
+ };
7829
8364
  var review = {
7830
8365
  summaryPoints: [
7831
8366
  "Ingested 25 generated Product records into wizard_seed_products.",
@@ -7847,9 +8382,9 @@ var SEEDS = {
7847
8382
  },
7848
8383
  playbook: {
7849
8384
  "select-playbook": selectPlaybook,
7850
- "confirm-playbook": void 0,
7851
8385
  "select-index": selectPlaybookIndex,
7852
- "confirm-index": null
8386
+ "confirm-index": null,
8387
+ "data-assessment": dataAssessment
7853
8388
  }
7854
8389
  };
7855
8390
  var SEED_ALGOLIA_STATE = {
@@ -7978,49 +8513,10 @@ async function resetProjectState() {
7978
8513
  return { dir, removed: targets };
7979
8514
  }
7980
8515
 
7981
- // src/lib/terminalSize.ts
7982
- var MIN_ROWS = 30;
7983
- var MIN_COLUMNS = 100;
7984
- async function requestTerminalSize(stdout = process.stdout, {
7985
- rows = MIN_ROWS,
7986
- columns = MIN_COLUMNS,
7987
- timeoutMs = 150,
7988
- settleMs = 120
7989
- } = {}) {
7990
- if (!stdout.isTTY) return;
7991
- const currentRows = stdout.rows ?? 0;
7992
- const currentColumns = stdout.columns ?? 0;
7993
- if (currentRows >= rows && currentColumns >= columns) return;
7994
- stdout.write(
7995
- `\x1B[8;${Math.max(currentRows, rows)};${Math.max(currentColumns, columns)}t`
7996
- );
7997
- const resized = await waitForResize(stdout, timeoutMs);
7998
- if (!resized) return;
7999
- await delay(settleMs);
8000
- stdout.write("\x1B[2J\x1B[H");
8001
- }
8002
- function waitForResize(stdout, timeoutMs) {
8003
- return new Promise((resolve5) => {
8004
- const finish = (didResize) => () => {
8005
- clearTimeout(timer);
8006
- stdout.off("resize", onResize);
8007
- resolve5(didResize);
8008
- };
8009
- const onResize = finish(true);
8010
- const timer = setTimeout(finish(false), timeoutMs);
8011
- stdout.once("resize", onResize);
8012
- });
8013
- }
8014
- function delay(ms) {
8015
- return new Promise((resolve5) => {
8016
- setTimeout(resolve5, ms);
8017
- });
8018
- }
8019
-
8020
8516
  // package.json with { type: 'json' }
8021
8517
  var package_default2 = {
8022
8518
  name: "@algolia/wizard",
8023
- version: "0.75.0",
8519
+ version: "0.77.0",
8024
8520
  description: "Magically implement Algolia functionality in your codebase",
8025
8521
  type: "module",
8026
8522
  engines: {
@@ -8127,7 +8623,7 @@ async function checkVersion() {
8127
8623
  }
8128
8624
 
8129
8625
  // src/main.tsx
8130
- import { jsx as jsx20 } from "react/jsx-runtime";
8626
+ import { jsx as jsx21 } from "react/jsx-runtime";
8131
8627
  var FAILURE_EXIT_CODE = 1;
8132
8628
  async function exitProcessWithError(store, instance, message) {
8133
8629
  store.setError(message);
@@ -8190,7 +8686,7 @@ ${formatStepList(workflow)}`);
8190
8686
  async function run(opts) {
8191
8687
  const { workflow, skipResumePrompt } = opts;
8192
8688
  const store = useWizard.getState();
8193
- const instance = render(/* @__PURE__ */ jsx20(App, {}), { incrementalRendering: true });
8689
+ const instance = render(/* @__PURE__ */ jsx21(App, {}), { incrementalRendering: true });
8194
8690
  await store.waitForStart();
8195
8691
  store.syncSteps(
8196
8692
  workflow.steps.map((s) => ({