@algolia/wizard 0.76.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 +532 -286
  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
  }),
@@ -2261,7 +2306,7 @@ async function ensureApplication(resumeAppId) {
2261
2306
  // package.json
2262
2307
  var package_default = {
2263
2308
  name: "@algolia/wizard",
2264
- version: "0.76.0",
2309
+ version: "0.77.0",
2265
2310
  description: "Magically implement Algolia functionality in your codebase",
2266
2311
  type: "module",
2267
2312
  engines: {
@@ -3315,10 +3360,10 @@ var GIT_ENV_OVERRIDES = [
3315
3360
  function gitSucceeds(root, args) {
3316
3361
  const env = { ...process.env };
3317
3362
  for (const key of GIT_ENV_OVERRIDES) delete env[key];
3318
- return new Promise((resolve5) => {
3363
+ return new Promise((resolve6) => {
3319
3364
  execFile("git", ["-C", root, ...args], { env }, (err) => {
3320
- if (!err) return resolve5(true);
3321
- resolve5(
3365
+ if (!err) return resolve6(true);
3366
+ resolve6(
3322
3367
  err.code === 1 ? false : void 0
3323
3368
  );
3324
3369
  });
@@ -3604,7 +3649,7 @@ import { tool as tool8 } from "ai";
3604
3649
  import z15 from "zod";
3605
3650
  import { createHash as createHash2 } from "node:crypto";
3606
3651
  import { readFile as readFile8, stat as stat2 } from "node:fs/promises";
3607
- import { relative as relative4 } from "node:path";
3652
+ import { basename as basename2, relative as relative4, resolve as resolve3 } from "node:path";
3608
3653
 
3609
3654
  // src/lib/tools/utils/prompt.ts
3610
3655
  var chain = Promise.resolve();
@@ -3624,11 +3669,92 @@ async function fileDigest(filePath) {
3624
3669
  function grantReviewedCommandApproval(ctx, approval) {
3625
3670
  reviewedCommandApprovals.set(ctx, approval);
3626
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
+ }
3627
3739
  function getReviewedCommandApproval(ctx, command, cwd) {
3628
3740
  const approval = reviewedCommandApprovals.get(ctx);
3629
3741
  if (approval?.command !== command || approval.cwd !== cwd) return void 0;
3630
3742
  return approval;
3631
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
+ }
3632
3758
  function clearReviewedCommandApproval(ctx) {
3633
3759
  reviewedCommandApprovals.delete(ctx);
3634
3760
  }
@@ -3683,6 +3809,14 @@ function reviewScriptTool(ctx) {
3683
3809
  if (!isFile) {
3684
3810
  return `Refused: ${filePath} is not a file. Write the script first, then review the path you wrote.`;
3685
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
+ }
3686
3820
  const reviewedDigest = await fileDigest(resolved2.target).catch(
3687
3821
  () => null
3688
3822
  );
@@ -3774,7 +3908,7 @@ var SIGKILL_DELAY_MS = 5e3;
3774
3908
  function runShell(command, opts) {
3775
3909
  const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
3776
3910
  const startedAt = Date.now();
3777
- return new Promise((resolve5) => {
3911
+ return new Promise((resolve6) => {
3778
3912
  let output = "";
3779
3913
  let timedOut = false;
3780
3914
  let settled = false;
@@ -3789,7 +3923,7 @@ function runShell(command, opts) {
3789
3923
  settled = true;
3790
3924
  clearTimeout(timer);
3791
3925
  clearTimeout(killTimer);
3792
- resolve5({
3926
+ resolve6({
3793
3927
  exitCode,
3794
3928
  output: truncateText(output.trim()),
3795
3929
  timedOut,
@@ -4072,6 +4206,7 @@ async function classifyCommandSafety(createModel, command, cwd, explanation, app
4072
4206
  prompt: [
4073
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.",
4074
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.",
4075
4210
  "Three broad categories are safe, and most commands you will see fall into one of them:",
4076
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.",
4077
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.',
@@ -4199,6 +4334,9 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
4199
4334
  () => runReviewedCommand(ctx, command, resolved2.target)
4200
4335
  );
4201
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
+ }
4202
4340
  const fast = fastPathSafety(command);
4203
4341
  let isSafe = fast;
4204
4342
  let usedStoredApproval = false;
@@ -5210,15 +5348,15 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
5210
5348
  // src/actions/implement.ts
5211
5349
  import z28 from "zod";
5212
5350
  import { access, mkdir as mkdir5, readFile as readFile9 } from "node:fs/promises";
5213
- 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";
5214
5352
 
5215
5353
  // src/lib/git.ts
5216
5354
  import { execFile as execFile2 } from "node:child_process";
5217
5355
  import { copyFile, mkdir as mkdir4, stat as stat3 } from "node:fs/promises";
5218
- 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";
5219
5357
  var MAX_BUFFER = 32 * 1024 * 1024;
5220
5358
  function git(args) {
5221
- return new Promise((resolve5, reject) => {
5359
+ return new Promise((resolve6, reject) => {
5222
5360
  execFile2("git", args, { maxBuffer: MAX_BUFFER }, (err, stdout, stderr) => {
5223
5361
  if (err)
5224
5362
  return reject(
@@ -5226,7 +5364,7 @@ function git(args) {
5226
5364
  `git ${args.join(" ")} failed: ${stderr.toString().trim() || err.message}`
5227
5365
  )
5228
5366
  );
5229
- resolve5(stdout.toString());
5367
+ resolve6(stdout.toString());
5230
5368
  });
5231
5369
  });
5232
5370
  }
@@ -5244,7 +5382,7 @@ async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
5244
5382
  if (!trimmed) {
5245
5383
  return { ok: false, reason: "no file path was provided" };
5246
5384
  }
5247
- const source = isAbsolute2(trimmed) ? trimmed : resolve3(repoRoot, trimmed);
5385
+ const source = isAbsolute2(trimmed) ? trimmed : resolve4(repoRoot, trimmed);
5248
5386
  try {
5249
5387
  if (!(await stat3(source)).isFile()) {
5250
5388
  return { ok: false, reason: `"${sourcePath}" is not a file` };
@@ -5252,9 +5390,9 @@ async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
5252
5390
  } catch {
5253
5391
  return { ok: false, reason: `"${sourcePath}" does not exist` };
5254
5392
  }
5255
- const relPath = join9(ingestDir, basename2(source));
5393
+ const relPath = join9(ingestDir, basename3(source));
5256
5394
  const dest = join9(repoRoot, relPath);
5257
- if (resolve3(source) === resolve3(dest)) {
5395
+ if (resolve4(source) === resolve4(dest)) {
5258
5396
  return { ok: true, relPath };
5259
5397
  }
5260
5398
  try {
@@ -5478,8 +5616,16 @@ function ingestionInstructions(input) {
5478
5616
  ...algoliaClientDoc(input),
5479
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.",
5480
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.`,
5481
- `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.`,
5482
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.",
5483
5629
  "The summary should be extremely concise.",
5484
5630
  ...sourceSpecificInstructions(input)
5485
5631
  ] : []
@@ -5631,15 +5777,30 @@ function parseIngestRecordCount(output) {
5631
5777
  const count = Number(match[1]);
5632
5778
  return Number.isFinite(count) ? count : void 0;
5633
5779
  }
5634
- 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) {
5635
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
+ };
5636
5795
  const withCount = newestFirst.filter(
5637
5796
  (e) => parseIngestRecordCount(e.output ?? "") != null
5638
5797
  );
5639
- 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
+ );
5640
5801
  return {
5641
- run: succeeded.find((e) => e.command === ingestCommand) ?? succeeded.find((e) => withCount.includes(e)),
5642
- 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,
5643
5804
  recordCount: parseIngestRecordCount(withCount[0]?.output ?? "")
5644
5805
  };
5645
5806
  }
@@ -5733,7 +5894,7 @@ async function pathExists(path) {
5733
5894
  async function frontendHasPackageJson(repoRoot, searchLocation) {
5734
5895
  let directory = repoRoot;
5735
5896
  if (searchLocation) {
5736
- const candidate = dirname5(resolve4(repoRoot, searchLocation));
5897
+ const candidate = dirname5(resolve5(repoRoot, searchLocation));
5737
5898
  const candidateRelative = relative6(repoRoot, candidate);
5738
5899
  const outsideRoot = candidateRelative === ".." || candidateRelative.startsWith(`..${sep2}`) || isAbsolute3(candidateRelative);
5739
5900
  if (!outsideRoot) directory = candidate;
@@ -5986,7 +6147,12 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
5986
6147
  run: ingestRun,
5987
6148
  attempt: ingestAttempt,
5988
6149
  recordCount
5989
- } = ingestOutcome(executions, ingestCommand);
6150
+ } = ingestOutcome(executions, repoRoot, ingestCommand);
6151
+ let ingestCwd = ".";
6152
+ if (ingestAttempt) {
6153
+ ingestCommand = ingestAttempt.command;
6154
+ ingestCwd = relativeIngestCwd(ingestAttempt, repoRoot);
6155
+ }
5990
6156
  ingestScriptRan = ingestRun != null;
5991
6157
  ingestRecordCount = recordCount;
5992
6158
  ingestDurationMs = ingestRun?.durationMs;
@@ -6062,7 +6228,10 @@ ${detail}` : ""}`
6062
6228
  ]
6063
6229
  });
6064
6230
  } else {
6065
- const commandMessages = ingestCommand ? [`Ingestion command: ${ingestCommand}`] : [];
6231
+ const commandMessages = ingestCommand ? [
6232
+ `Ingestion command: ${ingestCommand}`,
6233
+ ...ingestCwd !== "." ? [`Working directory: ${ingestCwd}`] : []
6234
+ ] : [];
6066
6235
  await ctx.requestUserInput({
6067
6236
  prompt: "",
6068
6237
  promptType: "enterToContinue",
@@ -6854,7 +7023,7 @@ async function pollUntilAssessed(ctx, uuid, indexName) {
6854
7023
  ctx.logEnd(logId, logStatus);
6855
7024
  }
6856
7025
  }
6857
- var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
7026
+ var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
6858
7027
  async function assessExecution(ctx, uuid, indexName) {
6859
7028
  await retryOnDashboardError(
6860
7029
  () => assessPlaybookExecution(uuid),
@@ -6992,7 +7161,19 @@ function getWorkflow(id) {
6992
7161
  import { dirname as dirname6, join as join12 } from "node:path";
6993
7162
  import { fileURLToPath as fileURLToPath2 } from "node:url";
6994
7163
  import { useState as useState10 } from "react";
6995
- 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
+ }
6996
7177
 
6997
7178
  // src/ui/copy/welcome.ts
6998
7179
  var sidebarItems = [
@@ -7020,7 +7201,7 @@ var sidebarItems = [
7020
7201
 
7021
7202
  // src/ui/Welcome.tsx
7022
7203
  import Image, { TerminalInfoContext, defaultTerminalInfo } from "ink-picture";
7023
- import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
7204
+ import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
7024
7205
  var IMAGE_PATH = join12(dirname6(fileURLToPath2(import.meta.url)), "algolia.png");
7025
7206
  var TERMINAL_INFO = {
7026
7207
  ...defaultTerminalInfo,
@@ -7031,21 +7212,23 @@ function SidebarItem({
7031
7212
  title,
7032
7213
  description
7033
7214
  }) {
7034
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
7035
- /* @__PURE__ */ jsxs11(Box11, { gap: 1, children: [
7036
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.success, children: "\u2192" }),
7037
- /* @__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 })
7038
7219
  ] }),
7039
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", gap: 2, children: [
7040
- /* @__PURE__ */ jsx10(Spacer, {}),
7041
- /* @__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 })
7042
7223
  ] })
7043
7224
  ] });
7044
7225
  }
7045
7226
  function Welcome() {
7046
7227
  const confirmStart = useWizard((s) => s.confirmStart);
7047
7228
  const openLearnMore = useWizard((s) => s.openLearnMore);
7048
- 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;
7049
7232
  const actions = [
7050
7233
  { label: "start wizard", run: confirmStart },
7051
7234
  { label: "learn more", run: openLearnMore }
@@ -7071,19 +7254,20 @@ function Welcome() {
7071
7254
  }
7072
7255
  };
7073
7256
  let layout = scales["large"];
7074
- if (rows < 30) {
7257
+ if (rows < MIN_ROWS) {
7075
7258
  layout = scales["small"];
7076
7259
  }
7077
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
7078
- /* @__PURE__ */ jsx10(
7079
- Box11,
7260
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
7261
+ /* @__PURE__ */ jsx11(
7262
+ Box12,
7080
7263
  {
7081
7264
  paddingY: layout.main.padding.y,
7082
7265
  paddingX: layout.main.padding.x,
7083
7266
  flexDirection: "column",
7084
7267
  justifyContent: "center",
7085
- children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", gap: 2, children: [
7086
- /* @__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(
7087
7271
  Image,
7088
7272
  {
7089
7273
  src: IMAGE_PATH,
@@ -7094,8 +7278,8 @@ function Welcome() {
7094
7278
  protocol: "halfBlock"
7095
7279
  }
7096
7280
  ) }),
7097
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
7098
- /* @__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(
7099
7283
  SelectRow,
7100
7284
  {
7101
7285
  highlighted: i === index,
@@ -7104,21 +7288,21 @@ function Welcome() {
7104
7288
  },
7105
7289
  action.label
7106
7290
  )) }),
7107
- /* @__PURE__ */ jsxs11(Box11, { gap: 2, children: [
7108
- /* @__PURE__ */ jsxs11(Text12, { children: [
7109
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
7110
- /* @__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" })
7111
7295
  ] }),
7112
- /* @__PURE__ */ jsxs11(Text12, { children: [
7113
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: "[enter]" }),
7114
- /* @__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" })
7115
7299
  ] })
7116
7300
  ] })
7117
7301
  ] })
7118
7302
  }
7119
7303
  ),
7120
- /* @__PURE__ */ jsxs11(
7121
- Box11,
7304
+ showSidebar && /* @__PURE__ */ jsxs12(
7305
+ Box12,
7122
7306
  {
7123
7307
  backgroundColor: COLORS.bg.sidebar,
7124
7308
  width: 40,
@@ -7128,8 +7312,8 @@ function Welcome() {
7128
7312
  flexDirection: "column",
7129
7313
  justifyContent: "center",
7130
7314
  children: [
7131
- /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
7132
- 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))
7133
7317
  ]
7134
7318
  }
7135
7319
  )
@@ -7137,8 +7321,7 @@ function Welcome() {
7137
7321
  }
7138
7322
 
7139
7323
  // src/ui/LearnMore.tsx
7140
- import { Fragment } from "react";
7141
- 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";
7142
7325
 
7143
7326
  // src/ui/copy/learn-more.ts
7144
7327
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -7180,7 +7363,7 @@ var policyLinks = [
7180
7363
  ];
7181
7364
 
7182
7365
  // src/ui/LearnMore.tsx
7183
- import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
7366
+ import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
7184
7367
  var TAG_COLORS = {
7185
7368
  READ: COLORS.success,
7186
7369
  WRITE: COLORS.badge,
@@ -7191,92 +7374,156 @@ var TAG_COLORS = {
7191
7374
  var TAG_COLUMN_WIDTH = 10;
7192
7375
  var PADDING_X = 6;
7193
7376
  var NEVER_BOX_PAD_X = 2;
7194
- function NeverLine({
7195
- width,
7196
- segments = []
7197
- }) {
7377
+ var BLANK3 = { parts: [{ text: " ", color: COLORS.dim }] };
7378
+ function neverRow(width, segments = []) {
7198
7379
  const used = segments.reduce((n, s) => n + s.text.length, 0);
7199
7380
  const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
7200
- return /* @__PURE__ */ jsxs12(Text13, { color: COLORS.primary, children: [
7201
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.danger, children: "\u2502" }),
7202
- " ".repeat(NEVER_BOX_PAD_X),
7203
- segments.map((s, i) => /* @__PURE__ */ jsx11(Text13, { color: s.color, bold: s.bold, children: s.text }, i)),
7204
- " ".repeat(rightPad),
7205
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.danger, children: "\u2502" })
7206
- ] });
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;
7207
7485
  }
7208
7486
  function LearnMore() {
7209
7487
  const confirmStart = useWizard((s) => s.confirmStart);
7210
7488
  const backToHome = useWizard((s) => s.backToHome);
7211
- const { columns } = useWindowSize6();
7212
- const dividerWidth = Math.max(0, columns - PADDING_X * 2);
7213
- 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) => {
7214
7495
  if (key.escape) backToHome();
7215
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);
7216
7499
  });
7217
- return /* @__PURE__ */ jsxs12(
7218
- Box12,
7500
+ const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
7501
+ return /* @__PURE__ */ jsxs13(
7502
+ Box13,
7219
7503
  {
7220
7504
  flexDirection: "column",
7221
7505
  paddingX: PADDING_X,
7222
7506
  paddingY: 2,
7223
7507
  width: "100%",
7224
- gap: 1,
7508
+ flexGrow: 1,
7225
7509
  children: [
7226
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
7227
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: accessIntro }),
7228
- /* @__PURE__ */ jsx11(Box12, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
7229
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
7230
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, marginTop: 1, children: [
7231
- /* @__PURE__ */ jsx11(Box12, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text13, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
7232
- /* @__PURE__ */ jsx11(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.primary, children: [
7233
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: item.title }),
7234
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
7235
- ] }) })
7236
- ] })
7237
- ] }, item.tag)) }),
7238
- /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
7239
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
7240
- /* @__PURE__ */ jsx11(NeverLine, { width: dividerWidth }),
7241
- /* @__PURE__ */ jsx11(
7242
- NeverLine,
7243
- {
7244
- width: dividerWidth,
7245
- segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
7246
- }
7247
- ),
7248
- neverItems.map((item) => /* @__PURE__ */ jsxs12(Fragment, { children: [
7249
- /* @__PURE__ */ jsx11(NeverLine, { width: dividerWidth }),
7250
- /* @__PURE__ */ jsx11(
7251
- NeverLine,
7252
- {
7253
- width: dividerWidth,
7254
- segments: [
7255
- { text: "\u2715", color: COLORS.danger },
7256
- { text: " " },
7257
- { text: item, color: COLORS.primary }
7258
- ]
7259
- }
7260
- )
7261
- ] }, item)),
7262
- /* @__PURE__ */ jsx11(NeverLine, { width: dividerWidth }),
7263
- /* @__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" })
7264
7515
  ] }),
7265
- /* @__PURE__ */ jsx11(Box12, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
7266
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
7267
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.accent, children: link.url })
7268
- ] }, link.label)) }),
7269
- /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "row", gap: 3, children: [
7270
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
7271
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "[" }),
7272
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: "esc" }),
7273
- /* @__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" })
7274
7521
  ] }),
7275
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", gap: 1, children: [
7276
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "[" }),
7277
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.primary, children: "enter" }),
7278
- /* @__PURE__ */ jsx11(Text13, { color: COLORS.muted, children: "]" }),
7279
- /* @__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" })
7280
7527
  ] })
7281
7528
  ] })
7282
7529
  ]
@@ -7285,17 +7532,17 @@ function LearnMore() {
7285
7532
  }
7286
7533
 
7287
7534
  // src/ui/Sidebar.tsx
7288
- import { Box as Box15, Text as Text16 } from "ink";
7535
+ import { Box as Box16, Text as Text17 } from "ink";
7289
7536
 
7290
7537
  // src/ui/Steps.tsx
7291
- import { Box as Box13, Text as Text14 } from "ink";
7538
+ import { Box as Box14, Text as Text15 } from "ink";
7292
7539
  import Spinner2 from "ink-spinner";
7293
- import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
7540
+ import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
7294
7541
  function Steps() {
7295
7542
  const { steps } = useWizard();
7296
7543
  const visibleSteps = steps.filter(isStepVisible);
7297
- 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: [
7298
- 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],
7299
7546
  " ",
7300
7547
  s.title
7301
7548
  ] }) }, s.id)) });
@@ -7304,27 +7551,27 @@ function CurrentStep() {
7304
7551
  const { steps } = useWizard();
7305
7552
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
7306
7553
  if (!currentStep) return null;
7307
- return /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.running, children: [
7308
- /* @__PURE__ */ jsx12(Spinner2, { type: "dots" }),
7554
+ return /* @__PURE__ */ jsxs14(Text15, { color: COLORS.status.running, children: [
7555
+ /* @__PURE__ */ jsx13(Spinner2, { type: "dots" }),
7309
7556
  " ",
7310
7557
  ` ${currentStep.title}`
7311
7558
  ] });
7312
7559
  }
7313
7560
 
7314
7561
  // src/ui/Progress.tsx
7315
- import { Box as Box14, Text as Text15 } from "ink";
7316
- 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";
7317
7564
  function Progress() {
7318
7565
  const { steps, currentStepIndex } = useWizard();
7319
7566
  const visibleSteps = steps.filter(isStepVisible);
7320
7567
  if (visibleSteps.length === 0) return null;
7321
7568
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
7322
7569
  const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
7323
- return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "row", gap: 1, children: [
7324
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: "STEP" }),
7325
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: activeStepNumber }),
7326
- /* @__PURE__ */ jsx13(Text15, { color: COLORS.strong, bold: true, children: "/" }),
7327
- /* @__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 })
7328
7575
  ] });
7329
7576
  }
7330
7577
 
@@ -7335,10 +7582,10 @@ var sidebarCommands = [
7335
7582
  ];
7336
7583
 
7337
7584
  // src/ui/Sidebar.tsx
7338
- import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
7585
+ import { jsx as jsx15, jsxs as jsxs16 } from "react/jsx-runtime";
7339
7586
  function Sidebar() {
7340
- return /* @__PURE__ */ jsxs15(
7341
- Box15,
7587
+ return /* @__PURE__ */ jsxs16(
7588
+ Box16,
7342
7589
  {
7343
7590
  backgroundColor: "#14171E",
7344
7591
  width: 30,
@@ -7347,16 +7594,16 @@ function Sidebar() {
7347
7594
  flexDirection: "column",
7348
7595
  justifyContent: "space-between",
7349
7596
  children: [
7350
- /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", gap: 1, children: [
7351
- /* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: "PROGRESS" }),
7352
- /* @__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, {})
7353
7600
  ] }),
7354
- /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", gap: 1, children: [
7355
- /* @__PURE__ */ jsx14(Progress, {}),
7356
- /* @__PURE__ */ jsx14(Box15, { flexDirection: "column", children: sidebarCommands.map((c) => {
7357
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", gap: 1, children: [
7358
- /* @__PURE__ */ jsx14(Text16, { color: COLORS.primary, children: `[${c.keyHint}]` }),
7359
- /* @__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 })
7360
7607
  ] }, c.keyHint);
7361
7608
  }) })
7362
7609
  ] })
@@ -7366,12 +7613,12 @@ function Sidebar() {
7366
7613
  }
7367
7614
 
7368
7615
  // src/ui/Ribbon.tsx
7369
- import { Box as Box16, Text as Text17 } from "ink";
7370
- 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";
7371
7618
  function Ribbon() {
7372
7619
  const firstCommand = sidebarCommands[0];
7373
- return /* @__PURE__ */ jsxs16(
7374
- Box16,
7620
+ return /* @__PURE__ */ jsxs17(
7621
+ Box17,
7375
7622
  {
7376
7623
  backgroundColor: "#14171E",
7377
7624
  flexDirection: "row",
@@ -7379,11 +7626,11 @@ function Ribbon() {
7379
7626
  paddingX: 2,
7380
7627
  paddingY: 1,
7381
7628
  children: [
7382
- /* @__PURE__ */ jsx15(Progress, {}),
7383
- /* @__PURE__ */ jsx15(CurrentStep, {}),
7384
- /* @__PURE__ */ jsxs16(Box16, { flexDirection: "row", gap: 1, children: [
7385
- /* @__PURE__ */ jsx15(Text17, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
7386
- /* @__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 })
7387
7634
  ] })
7388
7635
  ]
7389
7636
  }
@@ -7394,8 +7641,8 @@ function Ribbon() {
7394
7641
  import { useState as useState12 } from "react";
7395
7642
 
7396
7643
  // src/ui/Logs.tsx
7397
- import { Box as Box17, Text as Text18, useInput as useInput8 } from "ink";
7398
- 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";
7399
7646
  var KIND_COLOR = {
7400
7647
  tool: COLORS.primary,
7401
7648
  prompt: COLORS.badge
@@ -7431,9 +7678,9 @@ function Logs() {
7431
7678
  else if (input === "j" || key.downArrow) scroll.scrollBy(1);
7432
7679
  });
7433
7680
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
7434
- return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
7435
- logs.length === 0 && /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: "No logs yet." }),
7436
- /* @__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) => {
7437
7684
  const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
7438
7685
  const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
7439
7686
  const rawPreview = rawInputText(entry.input);
@@ -7443,28 +7690,28 @@ function Logs() {
7443
7690
  const name = truncate3(entry.name, budget);
7444
7691
  budget -= name.length;
7445
7692
  const preview = rawPreview ? truncate3(rawPreview, budget) : "";
7446
- return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", gap: ROW_GAP, children: [
7447
- /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: timestamp }),
7448
- /* @__PURE__ */ jsx16(Text18, { color: logNameColor(entry), wrap: "truncate", children: name }),
7449
- preview && /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, wrap: "truncate", children: preview }),
7450
- 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 })
7451
7698
  ] }, entry.id);
7452
7699
  }) }),
7453
- /* @__PURE__ */ jsxs17(Text18, { children: [
7454
- /* @__PURE__ */ jsx16(Text18, { color: COLORS.primary, children: "[j] [k]" }),
7455
- /* @__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" })
7456
7703
  ] })
7457
7704
  ] });
7458
7705
  }
7459
7706
 
7460
7707
  // src/ui/Tips.tsx
7461
7708
  import { useEffect as useEffect3, useRef as useRef4, useState as useState11 } from "react";
7462
- import { Box as Box19, Text as Text20 } from "ink";
7709
+ import { Box as Box20, Text as Text21 } from "ink";
7463
7710
  import terminalLink from "terminal-link";
7464
7711
 
7465
7712
  // src/ui/Code.tsx
7466
- import { Box as Box18, Text as Text19 } from "ink";
7467
- 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";
7468
7715
  var TOKEN_RE = /("(?:\\.|[^"\\])*"|\btrue\b|\bfalse\b|\bnull\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[{}[\]:,])/g;
7469
7716
  function highlightJson(json) {
7470
7717
  const parts = json.split(TOKEN_RE);
@@ -7473,22 +7720,22 @@ function highlightJson(json) {
7473
7720
  if (part[0] === '"') {
7474
7721
  const next = parts.slice(i + 1).find((p) => p.trim());
7475
7722
  const isKey = next?.trimStart().startsWith(":");
7476
- return /* @__PURE__ */ jsx17(Text19, { color: isKey ? "cyan" : "green", children: part }, i);
7723
+ return /* @__PURE__ */ jsx18(Text20, { color: isKey ? "cyan" : "green", children: part }, i);
7477
7724
  }
7478
7725
  if (part === "true" || part === "false" || part === "null") {
7479
- return /* @__PURE__ */ jsx17(Text19, { color: "magenta", children: part }, i);
7726
+ return /* @__PURE__ */ jsx18(Text20, { color: "magenta", children: part }, i);
7480
7727
  }
7481
7728
  if (/^-?\d/.test(part)) {
7482
- return /* @__PURE__ */ jsx17(Text19, { color: "yellow", children: part }, i);
7729
+ return /* @__PURE__ */ jsx18(Text20, { color: "yellow", children: part }, i);
7483
7730
  }
7484
7731
  if (/^[{}[\]:,]$/.test(part)) {
7485
- return /* @__PURE__ */ jsx17(Text19, { color: COLORS.dim, children: part }, i);
7732
+ return /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: part }, i);
7486
7733
  }
7487
- return /* @__PURE__ */ jsx17(Text19, { children: part }, i);
7734
+ return /* @__PURE__ */ jsx18(Text20, { children: part }, i);
7488
7735
  });
7489
7736
  }
7490
7737
  function Code({ children }) {
7491
- return /* @__PURE__ */ jsx17(Box18, { children: /* @__PURE__ */ jsx17(Text19, { children: highlightJson(children) }) });
7738
+ return /* @__PURE__ */ jsx18(Box19, { children: /* @__PURE__ */ jsx18(Text20, { children: highlightJson(children) }) });
7492
7739
  }
7493
7740
 
7494
7741
  // src/ui/copy/tips.ts
@@ -7702,26 +7949,26 @@ function tipsByIds(ids) {
7702
7949
  }
7703
7950
 
7704
7951
  // src/ui/Tips.tsx
7705
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
7952
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
7706
7953
  var TICK_MS = 16;
7707
7954
  var CHUNK_HOLD_MS = 2e3;
7708
7955
  var HOLD_MS = 12e3;
7709
7956
  function TipTitle({ children }) {
7710
- return /* @__PURE__ */ jsxs18(Box19, { gap: 1, children: [
7711
- /* @__PURE__ */ jsx18(Text20, { color: "cyan", children: "\u2726" }),
7712
- /* @__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 })
7713
7960
  ] });
7714
7961
  }
7715
7962
  function InlineSegment({ segment }) {
7716
7963
  switch (segment.type) {
7717
7964
  case "highlight":
7718
- return /* @__PURE__ */ jsx18(Text20, { color: COLORS.success, children: segment.value });
7965
+ return /* @__PURE__ */ jsx19(Text21, { color: COLORS.success, children: segment.value });
7719
7966
  case "link":
7720
- 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 });
7721
7968
  case "codeword":
7722
- 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 });
7723
7970
  default:
7724
- return /* @__PURE__ */ jsx18(Text20, { color: COLORS.muted, children: segment.value });
7971
+ return /* @__PURE__ */ jsx19(Text21, { color: COLORS.muted, children: segment.value });
7725
7972
  }
7726
7973
  }
7727
7974
  function TipContent({
@@ -7755,10 +8002,10 @@ function TipContent({
7755
8002
  blocks.push({ type: "inline", segments: [segment] });
7756
8003
  }
7757
8004
  }
7758
- return /* @__PURE__ */ jsx18(Box19, { flexDirection: "column", marginLeft: 2, gap: 1, children: blocks.map(
7759
- (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: [
7760
8007
  j > 0 && " ",
7761
- /* @__PURE__ */ jsx18(InlineSegment, { segment })
8008
+ /* @__PURE__ */ jsx19(InlineSegment, { segment })
7762
8009
  ] }, j)) }, i)
7763
8010
  ) });
7764
8011
  }
@@ -7835,14 +8082,51 @@ function Tips({
7835
8082
  if (!tip) return null;
7836
8083
  const titleRevealed = tip.title.slice(0, revealed);
7837
8084
  const contentRevealed = Math.max(0, revealed - tip.title.length);
7838
- return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", marginBottom: 1, gap: 1, children: [
7839
- /* @__PURE__ */ jsx18(TipTitle, { children: titleRevealed }),
7840
- /* @__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 })
7841
8088
  ] });
7842
8089
  }
7843
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
+
7844
8128
  // src/ui/App.tsx
7845
- import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
8129
+ import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
7846
8130
  function App() {
7847
8131
  const {
7848
8132
  phase,
@@ -7857,8 +8141,10 @@ function App() {
7857
8141
  loadingMessage,
7858
8142
  review: review2
7859
8143
  } = useWizard();
7860
- const { exit } = useApp();
7861
- const { columns, rows } = useWindowSize7();
8144
+ const { exit } = useApp2();
8145
+ const { columns, rows } = useWindowSize8();
8146
+ useRepaintOnResize();
8147
+ const compactRun = isCompactTerminal(columns, rows);
7862
8148
  const [showLogs, setShowLogs] = useState12(false);
7863
8149
  const [tipState, setTipState] = useState12("idle");
7864
8150
  const finished = phase === "done" || phase === "error";
@@ -7869,10 +8155,10 @@ function App() {
7869
8155
  const isAwaitingUserInput = phase === "awaitingInput" && !!inputReq;
7870
8156
  const isCommandApprovalPrompt = isAwaitingUserInput && inputReq?.promptType === "commandApproval";
7871
8157
  const promptPending = isAwaitingUserInput && !isCommandApprovalPrompt;
7872
- const holdForTip = stepHasTips && promptPending && tipState === "revealing";
7873
- const showTips = stepHasTips && (phase === "running" && currentStep?.status === "running" && !loadingMessage && !settingUpAppId || isCommandApprovalPrompt);
8158
+ const holdForTip = !compactRun && stepHasTips && promptPending && tipState === "revealing";
8159
+ const showTips = !compactRun && stepHasTips && (phase === "running" && currentStep?.status === "running" && !loadingMessage && !settingUpAppId || isCommandApprovalPrompt);
7874
8160
  const showNoticesInMain = !stepHasTips;
7875
- const showNotices = (!isAwaitingUserInput || holdForTip) && !review2;
8161
+ const showNotices = !compactRun && (!isAwaitingUserInput || holdForTip) && !review2;
7876
8162
  useInput9(
7877
8163
  (_input, key) => {
7878
8164
  if (key.return) {
@@ -7905,32 +8191,31 @@ function App() {
7905
8191
  }
7906
8192
  });
7907
8193
  const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
7908
- const flexDirection = columns > 90 ? "row" : "column";
8194
+ const flexDirection = columns > SIDEBAR_MIN_COLUMNS ? "row" : "column";
7909
8195
  const showSidebar = flexDirection === "row";
7910
- const scrollsPastViewport = phase === "idle" && homeScreen === "learnMore";
7911
8196
  return (
7912
8197
  /* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
7913
- the whole screen, and the scrolling throws off its cursor arithmetic —
7914
- flicker and leftover rows. */
7915
- /* @__PURE__ */ jsxs19(
7916
- Box20,
8198
+ the whole screen, and the scrolling throws off its cursor arithmetic.
8199
+ Flicker and leftover rows. */
8200
+ /* @__PURE__ */ jsxs20(
8201
+ Box21,
7917
8202
  {
7918
8203
  backgroundColor: COLORS.bg.main,
7919
8204
  flexDirection: "row",
7920
8205
  width: columns,
7921
- height: scrollsPastViewport ? void 0 : rows,
7922
- overflow: scrollsPastViewport ? "visible" : "hidden",
8206
+ height: rows,
8207
+ overflow: "hidden",
7923
8208
  children: [
7924
- mainWindowVisible && /* @__PURE__ */ jsxs19(
7925
- Box20,
8209
+ mainWindowVisible && /* @__PURE__ */ jsxs20(
8210
+ Box21,
7926
8211
  {
7927
8212
  flexDirection,
7928
8213
  width: "100%",
7929
8214
  maxHeight: rows,
7930
8215
  justifyContent: "space-between",
7931
8216
  children: [
7932
- showLogs ? /* @__PURE__ */ jsx19(Logs, {}) : /* @__PURE__ */ jsxs19(
7933
- Box20,
8217
+ showLogs ? /* @__PURE__ */ jsx20(Logs, {}) : /* @__PURE__ */ jsxs20(
8218
+ Box21,
7934
8219
  {
7935
8220
  flexDirection: "column",
7936
8221
  paddingX: 4,
@@ -7939,35 +8224,35 @@ function App() {
7939
8224
  flexGrow: 1,
7940
8225
  gap: 1,
7941
8226
  children: [
7942
- /* @__PURE__ */ jsxs19(Box20, { flexGrow: 2, flexDirection: "column", children: [
7943
- phase === "preflight" && !user && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7944
- /* @__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" }),
7945
8230
  " Signing in to Algolia"
7946
8231
  ] }) }),
7947
- phase === "preflight" && user && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7948
- /* @__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" }),
7949
8234
  " Getting things ready"
7950
8235
  ] }) }),
7951
- phase === "authenticating" && /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", marginBottom: 1, children: [
7952
- /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7953
- /* @__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" }),
7954
8239
  " Signing in to Algolia"
7955
8240
  ] }),
7956
- /* @__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." })
7957
8242
  ] }),
7958
- settingUpAppId && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7959
- /* @__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" }),
7960
8245
  " Setting up Wizard using app",
7961
8246
  " ",
7962
8247
  settingUpAppId
7963
8248
  ] }) }),
7964
- phase === "running" && !settingUpAppId && loadingMessage && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
7965
- /* @__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" }),
7966
8251
  " ",
7967
8252
  loadingMessage
7968
8253
  ] }) }),
7969
- /* @__PURE__ */ jsx19(CliOutput, {}),
7970
- showTips && currentStep && /* @__PURE__ */ jsx19(
8254
+ /* @__PURE__ */ jsx20(CliOutput, {}),
8255
+ showTips && currentStep && /* @__PURE__ */ jsx20(
7971
8256
  Tips,
7972
8257
  {
7973
8258
  stepId: currentStep.id,
@@ -7975,24 +8260,24 @@ function App() {
7975
8260
  onRevealStateChange: setTipState
7976
8261
  }
7977
8262
  ),
7978
- showNoticesInMain && showNotices && /* @__PURE__ */ jsx19(Notices, { showAll: true, border: false }),
7979
- /* @__PURE__ */ jsx19(Review, {}),
7980
- !showTips && /* @__PURE__ */ jsx19(PromptInput, {}),
7981
- 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: [
7982
8267
  "\u2716 ",
7983
8268
  error
7984
8269
  ] }) })
7985
8270
  ] }),
7986
- !showNoticesInMain && showNotices && /* @__PURE__ */ jsx19(Notices, {}),
7987
- showTips && !holdForTip && /* @__PURE__ */ jsx19(PromptInput, {})
8271
+ !showNoticesInMain && showNotices && /* @__PURE__ */ jsx20(Notices, {}),
8272
+ showTips && !holdForTip && /* @__PURE__ */ jsx20(PromptInput, {})
7988
8273
  ]
7989
8274
  }
7990
8275
  ),
7991
- showSidebar ? /* @__PURE__ */ jsx19(Sidebar, {}) : /* @__PURE__ */ jsx19(Ribbon, {})
8276
+ showSidebar ? /* @__PURE__ */ jsx20(Sidebar, {}) : /* @__PURE__ */ jsx20(Ribbon, {})
7992
8277
  ]
7993
8278
  }
7994
8279
  ),
7995
- phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx19(LearnMore, {}) : /* @__PURE__ */ jsx19(Welcome, {}))
8280
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx20(LearnMore, {}) : /* @__PURE__ */ jsx20(Welcome, {}))
7996
8281
  ]
7997
8282
  }
7998
8283
  )
@@ -8228,49 +8513,10 @@ async function resetProjectState() {
8228
8513
  return { dir, removed: targets };
8229
8514
  }
8230
8515
 
8231
- // src/lib/terminalSize.ts
8232
- var MIN_ROWS = 30;
8233
- var MIN_COLUMNS = 100;
8234
- async function requestTerminalSize(stdout = process.stdout, {
8235
- rows = MIN_ROWS,
8236
- columns = MIN_COLUMNS,
8237
- timeoutMs = 150,
8238
- settleMs = 120
8239
- } = {}) {
8240
- if (!stdout.isTTY) return;
8241
- const currentRows = stdout.rows ?? 0;
8242
- const currentColumns = stdout.columns ?? 0;
8243
- if (currentRows >= rows && currentColumns >= columns) return;
8244
- stdout.write(
8245
- `\x1B[8;${Math.max(currentRows, rows)};${Math.max(currentColumns, columns)}t`
8246
- );
8247
- const resized = await waitForResize(stdout, timeoutMs);
8248
- if (!resized) return;
8249
- await delay(settleMs);
8250
- stdout.write("\x1B[2J\x1B[H");
8251
- }
8252
- function waitForResize(stdout, timeoutMs) {
8253
- return new Promise((resolve5) => {
8254
- const finish = (didResize) => () => {
8255
- clearTimeout(timer);
8256
- stdout.off("resize", onResize);
8257
- resolve5(didResize);
8258
- };
8259
- const onResize = finish(true);
8260
- const timer = setTimeout(finish(false), timeoutMs);
8261
- stdout.once("resize", onResize);
8262
- });
8263
- }
8264
- function delay(ms) {
8265
- return new Promise((resolve5) => {
8266
- setTimeout(resolve5, ms);
8267
- });
8268
- }
8269
-
8270
8516
  // package.json with { type: 'json' }
8271
8517
  var package_default2 = {
8272
8518
  name: "@algolia/wizard",
8273
- version: "0.76.0",
8519
+ version: "0.77.0",
8274
8520
  description: "Magically implement Algolia functionality in your codebase",
8275
8521
  type: "module",
8276
8522
  engines: {
@@ -8377,7 +8623,7 @@ async function checkVersion() {
8377
8623
  }
8378
8624
 
8379
8625
  // src/main.tsx
8380
- import { jsx as jsx20 } from "react/jsx-runtime";
8626
+ import { jsx as jsx21 } from "react/jsx-runtime";
8381
8627
  var FAILURE_EXIT_CODE = 1;
8382
8628
  async function exitProcessWithError(store, instance, message) {
8383
8629
  store.setError(message);
@@ -8440,7 +8686,7 @@ ${formatStepList(workflow)}`);
8440
8686
  async function run(opts) {
8441
8687
  const { workflow, skipResumePrompt } = opts;
8442
8688
  const store = useWizard.getState();
8443
- const instance = render(/* @__PURE__ */ jsx20(App, {}), { incrementalRendering: true });
8689
+ const instance = render(/* @__PURE__ */ jsx21(App, {}), { incrementalRendering: true });
8444
8690
  await store.waitForStart();
8445
8691
  store.syncSteps(
8446
8692
  workflow.steps.map((s) => ({