brew-tui 0.6.1 → 0.7.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 (39) hide show
  1. package/README.md +5 -5
  2. package/build/{brewbar-installer-BAHS6EEZ.js → brewbar-installer-KJTIZIVU.js} +3 -3
  3. package/build/brewbar-installer-KJTIZIVU.js.map +1 -0
  4. package/build/{brewfile-manager-3SERRYNC.js → brewfile-manager-6LXONGSA.js} +4 -4
  5. package/build/{chunk-LXF72RCD.js → chunk-3VDIKVS3.js} +3 -3
  6. package/build/{chunk-MSXH66I2.js → chunk-F6EISN54.js} +65 -5
  7. package/build/chunk-F6EISN54.js.map +1 -0
  8. package/build/{chunk-U2DRWB7A.js → chunk-FIPCCYL6.js} +2 -2
  9. package/build/chunk-GWXDXFUC.js +721 -0
  10. package/build/chunk-GWXDXFUC.js.map +1 -0
  11. package/build/{chunk-KVCVIRWI.js → chunk-JYHINZVV.js} +19 -4
  12. package/build/chunk-JYHINZVV.js.map +1 -0
  13. package/build/chunk-QZZZAAWG.js +56 -0
  14. package/build/chunk-QZZZAAWG.js.map +1 -0
  15. package/build/{chunk-4I344KQX.js → chunk-VLREAA5F.js} +59 -3
  16. package/build/chunk-VLREAA5F.js.map +1 -0
  17. package/build/compliance-checker-MAREAFDH.js +12 -0
  18. package/build/{history-logger-PBDOLKNJ.js → history-logger-FJ3HZSFU.js} +3 -3
  19. package/build/index.js +1273 -1303
  20. package/build/index.js.map +1 -1
  21. package/build/{snapshot-RAPGMAJF.js → snapshot-JDRSBMG6.js} +7 -3
  22. package/build/{sync-engine-CAFK4LHA.js → sync-engine-4OA4JNR3.js} +7 -5
  23. package/package.json +4 -4
  24. package/build/brewbar-installer-BAHS6EEZ.js.map +0 -1
  25. package/build/chunk-4I344KQX.js.map +0 -1
  26. package/build/chunk-AIAZQJKL.js +0 -299
  27. package/build/chunk-AIAZQJKL.js.map +0 -1
  28. package/build/chunk-KVCVIRWI.js.map +0 -1
  29. package/build/chunk-MSXH66I2.js.map +0 -1
  30. package/build/chunk-UWS4A4F5.js +0 -25
  31. package/build/chunk-UWS4A4F5.js.map +0 -1
  32. package/build/compliance-checker-X7P623UF.js +0 -12
  33. /package/build/{brewfile-manager-3SERRYNC.js.map → brewfile-manager-6LXONGSA.js.map} +0 -0
  34. /package/build/{chunk-LXF72RCD.js.map → chunk-3VDIKVS3.js.map} +0 -0
  35. /package/build/{chunk-U2DRWB7A.js.map → chunk-FIPCCYL6.js.map} +0 -0
  36. /package/build/{compliance-checker-X7P623UF.js.map → compliance-checker-MAREAFDH.js.map} +0 -0
  37. /package/build/{history-logger-PBDOLKNJ.js.map → history-logger-FJ3HZSFU.js.map} +0 -0
  38. /package/build/{snapshot-RAPGMAJF.js.map → snapshot-JDRSBMG6.js.map} +0 -0
  39. /package/build/{sync-engine-CAFK4LHA.js.map → sync-engine-4OA4JNR3.js.map} +0 -0
package/build/index.js CHANGED
@@ -5,24 +5,31 @@ import {
5
5
  loadBrewfile,
6
6
  reconcile,
7
7
  saveBrewfile
8
- } from "./chunk-LXF72RCD.js";
8
+ } from "./chunk-3VDIKVS3.js";
9
9
  import {
10
+ activate,
10
11
  applyConflictResolutions,
12
+ deactivate,
11
13
  decryptPayload,
14
+ getDegradationLevel,
15
+ isExpired,
16
+ loadLicense,
12
17
  loadSyncConfig,
18
+ needsRevalidation,
13
19
  readSyncEnvelope,
20
+ revalidate,
14
21
  sync
15
- } from "./chunk-AIAZQJKL.js";
22
+ } from "./chunk-GWXDXFUC.js";
16
23
  import {
17
24
  checkCompliance
18
- } from "./chunk-U2DRWB7A.js";
25
+ } from "./chunk-FIPCCYL6.js";
19
26
  import {
20
27
  captureSnapshot,
21
28
  execBrew,
22
29
  loadSnapshots,
23
30
  saveSnapshot,
24
31
  streamBrew
25
- } from "./chunk-4I344KQX.js";
32
+ } from "./chunk-VLREAA5F.js";
26
33
  import {
27
34
  exportReport,
28
35
  loadPolicy
@@ -32,13 +39,14 @@ import {
32
39
  clearHistory,
33
40
  detectAction,
34
41
  loadHistory
35
- } from "./chunk-KVCVIRWI.js";
42
+ } from "./chunk-JYHINZVV.js";
36
43
  import {
37
44
  DATA_DIR,
38
- LICENSE_PATH,
45
+ ONBOARDING_FLAG_PATH,
39
46
  PROFILES_DIR,
40
- ensureDataDirs
41
- } from "./chunk-UWS4A4F5.js";
47
+ ensureDataDirs,
48
+ getMachineId
49
+ } from "./chunk-QZZZAAWG.js";
42
50
  import {
43
51
  fetchWithRetry,
44
52
  fetchWithTimeout,
@@ -46,18 +54,18 @@ import {
46
54
  t,
47
55
  tp,
48
56
  useLocaleStore
49
- } from "./chunk-MSXH66I2.js";
57
+ } from "./chunk-F6EISN54.js";
50
58
  import {
51
59
  logger
52
60
  } from "./chunk-KDHEUNRI.js";
53
61
 
54
62
  // src/index.tsx
55
63
  import { createInterface } from "readline/promises";
56
- import { rm as rm3 } from "fs/promises";
64
+ import { rm as rm2 } from "fs/promises";
57
65
  import { render } from "ink";
58
66
 
59
67
  // src/app.tsx
60
- import { useEffect as useEffect19 } from "react";
68
+ import { useEffect as useEffect20, useState as useState17 } from "react";
61
69
  import { useApp } from "ink";
62
70
 
63
71
  // src/components/layout/app-layout.tsx
@@ -73,6 +81,7 @@ var VIEWS = [
73
81
  "installed",
74
82
  "outdated",
75
83
  "package-info",
84
+ "search",
76
85
  "services",
77
86
  "doctor",
78
87
  "profiles",
@@ -137,7 +146,7 @@ function isTeamView(viewId) {
137
146
  }
138
147
 
139
148
  // src/utils/colors.ts
140
- var COLORS = {
149
+ var DARK_PALETTE = {
141
150
  success: "#22C55E",
142
151
  error: "#EF4444",
143
152
  warning: "#F59E0B",
@@ -149,12 +158,61 @@ var COLORS = {
149
158
  teal: "#2DD4BF",
150
159
  sky: "#38BDF8",
151
160
  gold: "#FFD700",
161
+ goldOrange: "#FFA500",
162
+ goldDeep: "#B8860B",
163
+ goldDark: "#8B6914",
164
+ goldDeepest: "#6B4F10",
152
165
  purple: "#A855F7",
153
166
  blue: "#3B82F6",
154
167
  lavender: "#C4B5FD",
155
168
  border: "#4B5563",
156
169
  white: "#FFFFFF"
157
170
  };
171
+ var LIGHT_PALETTE = {
172
+ success: "#15803D",
173
+ error: "#B91C1C",
174
+ warning: "#B45309",
175
+ info: "#0E7490",
176
+ brand: "#C2410C",
177
+ muted: "#4B5563",
178
+ text: "#111827",
179
+ textSecondary: "#374151",
180
+ teal: "#0F766E",
181
+ sky: "#0369A1",
182
+ gold: "#A16207",
183
+ goldOrange: "#9A3412",
184
+ goldDeep: "#7C2D12",
185
+ goldDark: "#5B2509",
186
+ goldDeepest: "#3F1A06",
187
+ purple: "#6B21A8",
188
+ blue: "#1D4ED8",
189
+ lavender: "#6D28D9",
190
+ border: "#D1D5DB",
191
+ white: "#000000"
192
+ // intentional: "white-on-light" reads as black ink
193
+ };
194
+ function detectTheme() {
195
+ const override = process.env["BREW_TUI_THEME"];
196
+ if (override === "dark" || override === "light") return override;
197
+ const cfb = process.env["COLORFGBG"];
198
+ if (cfb) {
199
+ const parts = cfb.split(";");
200
+ const bg = Number(parts[parts.length - 1]);
201
+ if (!isNaN(bg) && bg >= 7) return "light";
202
+ if (!isNaN(bg) && bg <= 6) return "dark";
203
+ }
204
+ return "dark";
205
+ }
206
+ var PALETTE = detectTheme() === "light" ? LIGHT_PALETTE : DARK_PALETTE;
207
+ function isNoColorRequested() {
208
+ const v = process.env["NO_COLOR"];
209
+ return typeof v === "string" && v.length > 0;
210
+ }
211
+ var NO_COLOR = isNoColorRequested();
212
+ var THEME = detectTheme();
213
+ var COLORS = NO_COLOR ? Object.fromEntries(
214
+ Object.keys(PALETTE).map((k) => [k, ""])
215
+ ) : PALETTE;
158
216
 
159
217
  // src/utils/gradient.tsx
160
218
  import React, { useMemo } from "react";
@@ -176,6 +234,9 @@ function interpolateColor(c1, c2, t2) {
176
234
  return rgbToHex(lerp(r1, r2, t2), lerp(g1, g2, t2), lerp(b1, b2, t2));
177
235
  }
178
236
  var GradientText = React.memo(function GradientText2({ children, colors, bold }) {
237
+ if (NO_COLOR) {
238
+ return /* @__PURE__ */ jsx(Text, { bold, children });
239
+ }
179
240
  if (colors.length < 2) {
180
241
  return /* @__PURE__ */ jsx(Text, { color: colors[0], bold, children });
181
242
  }
@@ -195,14 +256,25 @@ var GradientText = React.memo(function GradientText2({ children, colors, bold })
195
256
  return /* @__PURE__ */ jsx(Fragment, { children: coloredChars.map(({ char, color, key }) => /* @__PURE__ */ jsx(Text, { color, bold, children: char }, key)) });
196
257
  });
197
258
  var GRADIENTS = {
198
- gold: ["#FFD700", "#FFA500", "#B8860B"],
199
- sunset: ["#FF6B2B", "#FFD700", "#FF6B2B"],
200
- ocean: ["#06B6D4", "#3B82F6", "#A855F7"],
201
- emerald: ["#22C55E", "#2DD4BF", "#06B6D4"],
202
- fire: ["#EF4444", "#F59E0B", "#FFD700"],
203
- version: ["#EF4444", "#9CA3AF", "#2DD4BF"],
204
- pro: ["#FF6B2B", "#FFD700", "#FF6B2B"],
205
- darkGold: ["#B8860B", "#8B6914", "#6B4F10"]
259
+ gold: [COLORS.gold, COLORS.goldOrange, COLORS.goldDeep],
260
+ sunset: [COLORS.brand, COLORS.gold, COLORS.brand],
261
+ ocean: [COLORS.info, COLORS.blue, COLORS.purple],
262
+ emerald: [COLORS.success, COLORS.teal, COLORS.info],
263
+ fire: [COLORS.error, COLORS.warning, COLORS.gold],
264
+ version: [COLORS.error, COLORS.muted, COLORS.teal],
265
+ pro: [COLORS.brand, COLORS.gold, COLORS.brand],
266
+ darkGold: [COLORS.goldDeep, COLORS.goldDark, COLORS.goldDeepest]
267
+ };
268
+
269
+ // src/utils/spacing.ts
270
+ var SPACING = {
271
+ none: 0,
272
+ xs: 1,
273
+ sm: 2,
274
+ md: 3,
275
+ lg: 4,
276
+ xl: 6,
277
+ xxl: 8
206
278
  };
207
279
 
208
280
  // src/components/layout/header.tsx
@@ -310,13 +382,13 @@ function Header() {
310
382
  const logoBlock = /* @__PURE__ */ jsx2(Box, { flexDirection: "column", flexShrink: 0, children: LOGO_BREW.map((brew, i) => /* @__PURE__ */ jsxs(Box, { children: [
311
383
  /* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.gold, children: brew }),
312
384
  /* @__PURE__ */ jsx2(GradientText, { colors: GRADIENTS.darkGold, children: LOGO_TUI[i] })
313
- ] }, i)) });
314
- const menuBlock = /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: COLORS.lavender, paddingX: 1, flexDirection: "column", alignSelf: isNarrow ? "flex-start" : "center", children: [
385
+ ] }, `logo-${i}`)) });
386
+ const menuBlock = /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: COLORS.lavender, paddingX: SPACING.xs, flexDirection: "column", alignSelf: isNarrow ? "flex-start" : "center", children: [
315
387
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
316
388
  /* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: COL1_VIEWS.map((view) => /* @__PURE__ */ jsx2(MenuItem, { view, currentView }, view)) }),
317
- /* @__PURE__ */ jsx2(Box, { flexDirection: "column", marginLeft: 2, children: COL2_VIEWS.map((view) => /* @__PURE__ */ jsx2(MenuItem, { view, currentView }, view)) })
389
+ /* @__PURE__ */ jsx2(Box, { flexDirection: "column", marginLeft: SPACING.sm, children: COL2_VIEWS.map((view) => /* @__PURE__ */ jsx2(MenuItem, { view, currentView }, view)) })
318
390
  ] }),
319
- /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.lavender, marginTop: 0, children: [
391
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.lavender, marginTop: SPACING.none, children: [
320
392
  /* @__PURE__ */ jsx2(Text2, { bold: true, color: COLORS.white, children: "S" }),
321
393
  /* @__PURE__ */ jsxs(Text2, { color: COLORS.textSecondary, children: [
322
394
  " ",
@@ -335,14 +407,14 @@ function Header() {
335
407
  ] })
336
408
  ] });
337
409
  if (isNarrow) {
338
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
410
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: SPACING.xs, children: [
339
411
  logoBlock,
340
- /* @__PURE__ */ jsx2(Box, { marginTop: 1, children: menuBlock })
412
+ /* @__PURE__ */ jsx2(Box, { marginTop: SPACING.xs, children: menuBlock })
341
413
  ] });
342
414
  }
343
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", paddingX: 1, alignItems: "center", children: [
415
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", paddingX: SPACING.xs, alignItems: "center", children: [
344
416
  logoBlock,
345
- /* @__PURE__ */ jsx2(Box, { marginLeft: 2, children: menuBlock })
417
+ /* @__PURE__ */ jsx2(Box, { marginLeft: SPACING.sm, children: menuBlock })
346
418
  ] });
347
419
  }
348
420
 
@@ -380,7 +452,7 @@ function Footer() {
380
452
  const currentView = useNavigationStore((s) => s.currentView);
381
453
  const locale = useLocaleStore((s) => s.locale);
382
454
  const defs = VIEW_HINT_DEFS[currentView] ?? [];
383
- return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.gold, paddingX: 1, flexWrap: "wrap", children: [
455
+ return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: COLORS.gold, paddingX: SPACING.xs, flexWrap: "wrap", children: [
384
456
  defs.map((def, i) => {
385
457
  const key = def.length === 1 ? def[0] : `${def[0]}:${def[1]}`;
386
458
  return /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
@@ -413,7 +485,7 @@ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
413
485
  function AppLayout({ children }) {
414
486
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: "100%", children: [
415
487
  /* @__PURE__ */ jsx4(Header, {}),
416
- /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", flexGrow: 1, paddingX: 2, paddingY: 1, children }),
488
+ /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", flexGrow: 1, paddingX: SPACING.sm, paddingY: SPACING.xs, children }),
417
489
  /* @__PURE__ */ jsx4(Footer, {})
418
490
  ] });
419
491
  }
@@ -421,371 +493,6 @@ function AppLayout({ children }) {
421
493
  // src/stores/license-store.ts
422
494
  import { create as create2 } from "zustand";
423
495
 
424
- // src/lib/license/license-manager.ts
425
- import { readFile as readFile2, writeFile as writeFile2, rename, rm } from "fs/promises";
426
- import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
427
-
428
- // src/lib/license/polar-api.ts
429
- import { randomUUID } from "crypto";
430
- import { readFile, writeFile, mkdir } from "fs/promises";
431
- import { join } from "path";
432
- import { homedir } from "os";
433
- var BASE_URL = "https://api.polar.sh/v1/customer-portal/license-keys";
434
- var POLAR_ORGANIZATION_ID = "b8f245c0-d116-4457-92fb-1bda47139f82";
435
- var DATA_DIR2 = join(homedir(), ".brew-tui");
436
- var MACHINE_ID_PATH = join(DATA_DIR2, "machine-id");
437
- async function getMachineId() {
438
- try {
439
- const id2 = (await readFile(MACHINE_ID_PATH, "utf-8")).trim();
440
- if (id2) return id2;
441
- } catch {
442
- }
443
- const id = randomUUID();
444
- await mkdir(DATA_DIR2, { recursive: true, mode: 448 });
445
- await writeFile(MACHINE_ID_PATH, id, { encoding: "utf-8", mode: 384 });
446
- return id;
447
- }
448
- function validateApiUrl(url) {
449
- const parsed = new URL(url);
450
- if (parsed.protocol !== "https:") {
451
- throw new Error("HTTPS required for license API");
452
- }
453
- if (!parsed.hostname.endsWith("polar.sh")) {
454
- throw new Error("Invalid API host");
455
- }
456
- }
457
- async function post(endpoint, body, expectEmpty = false) {
458
- const url = `${BASE_URL}/${endpoint}`;
459
- validateApiUrl(url);
460
- const res = await fetchWithRetry(url, {
461
- method: "POST",
462
- headers: { "Content-Type": "application/json" },
463
- body: JSON.stringify(body)
464
- }, 15e3);
465
- if (!res.ok) {
466
- let message = `Request failed with status ${res.status}`;
467
- try {
468
- const errBody = await res.json();
469
- if (typeof errBody.detail === "string") message = errBody.detail;
470
- else if (typeof errBody.error === "string") message = errBody.error;
471
- else if (typeof errBody.message === "string") message = errBody.message;
472
- } catch {
473
- }
474
- throw new Error(message);
475
- }
476
- if (expectEmpty || res.status === 204) return void 0;
477
- return res.json();
478
- }
479
- async function activateLicense(key) {
480
- const machineId = await getMachineId();
481
- const activation = await post("activate", {
482
- key,
483
- organization_id: POLAR_ORGANIZATION_ID,
484
- label: machineId
485
- // SEG-004: Use machine UUID instead of hostname
486
- });
487
- if (!activation || typeof activation.id !== "string" || !activation.license_key) {
488
- throw new Error("Invalid activation response: missing required fields");
489
- }
490
- let customerEmail = "";
491
- let customerName = "";
492
- try {
493
- const validated = await post("validate", {
494
- key,
495
- organization_id: POLAR_ORGANIZATION_ID,
496
- activation_id: activation.id
497
- });
498
- customerEmail = validated.customer?.email ?? "";
499
- customerName = validated.customer?.name ?? "";
500
- } catch {
501
- }
502
- return {
503
- activated: true,
504
- error: null,
505
- instance: { id: activation.id },
506
- license_key: {
507
- id: 0,
508
- status: activation.license_key.status,
509
- key,
510
- activation_limit: 0,
511
- activations_count: 0,
512
- expires_at: activation.license_key.expires_at
513
- },
514
- meta: { customer_email: customerEmail, customer_name: customerName }
515
- };
516
- }
517
- async function validateLicense(key, instanceId) {
518
- const res = await post("validate", {
519
- key,
520
- organization_id: POLAR_ORGANIZATION_ID,
521
- activation_id: instanceId
522
- });
523
- if (!res || typeof res.id !== "string" || typeof res.status !== "string" || !res.customer) {
524
- throw new Error("Invalid validation response: missing required fields");
525
- }
526
- const notExpired = res.expires_at === null || new Date(res.expires_at) > /* @__PURE__ */ new Date();
527
- const valid = res.status === "granted" && notExpired;
528
- return {
529
- valid,
530
- error: valid ? null : `License ${res.status}`,
531
- license_key: {
532
- id: 0,
533
- status: res.status,
534
- key,
535
- expires_at: res.expires_at
536
- },
537
- instance: { id: instanceId }
538
- };
539
- }
540
- async function deactivateLicense(key, instanceId) {
541
- await post(
542
- "deactivate",
543
- { key, organization_id: POLAR_ORGANIZATION_ID, activation_id: instanceId },
544
- true
545
- );
546
- }
547
-
548
- // src/lib/license/license-manager.ts
549
- var REVALIDATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
550
- var GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
551
- var ACTIVATION_COOLDOWN_MS = 3e4;
552
- var MAX_ATTEMPTS = 5;
553
- var LOCKOUT_MS = 15 * 60 * 1e3;
554
- var tracker = {
555
- attempts: 0,
556
- lastAttempt: 0,
557
- lockedUntil: 0
558
- };
559
- function checkRateLimit() {
560
- const now = Date.now();
561
- if (now < tracker.lockedUntil) {
562
- const remaining = Math.ceil((tracker.lockedUntil - now) / 6e4);
563
- throw new Error(t("cli_rateLimited", { minutes: remaining }));
564
- }
565
- if (now - tracker.lastAttempt < ACTIVATION_COOLDOWN_MS) {
566
- throw new Error(t("cli_cooldown"));
567
- }
568
- }
569
- function recordAttempt(success) {
570
- const now = Date.now();
571
- tracker.lastAttempt = now;
572
- if (success) {
573
- tracker.attempts = 0;
574
- return;
575
- }
576
- tracker.attempts++;
577
- if (tracker.attempts >= MAX_ATTEMPTS) {
578
- tracker.lockedUntil = now + LOCKOUT_MS;
579
- tracker.attempts = 0;
580
- }
581
- }
582
- var ENCRYPTION_SECRET = "brew-tui-license-aes256gcm-v1";
583
- var SCRYPT_SALT = "brew-tui-salt-v1";
584
- var _derivedKey = null;
585
- function deriveEncryptionKey() {
586
- if (!_derivedKey) _derivedKey = scryptSync(ENCRYPTION_SECRET, SCRYPT_SALT, 32);
587
- return _derivedKey;
588
- }
589
- function encryptLicenseData(data) {
590
- const key = deriveEncryptionKey();
591
- const iv = randomBytes(12);
592
- const cipher = createCipheriv("aes-256-gcm", key, iv);
593
- const plaintext = JSON.stringify(data);
594
- const ciphertext = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
595
- const tag = cipher.getAuthTag();
596
- return {
597
- encrypted: ciphertext.toString("base64"),
598
- iv: iv.toString("base64"),
599
- tag: tag.toString("base64")
600
- };
601
- }
602
- function decryptLicenseData(encrypted, iv, tag) {
603
- const key = deriveEncryptionKey();
604
- const decipher = createDecipheriv(
605
- "aes-256-gcm",
606
- key,
607
- Buffer.from(iv, "base64")
608
- );
609
- decipher.setAuthTag(Buffer.from(tag, "base64"));
610
- const plaintext = Buffer.concat([
611
- decipher.update(Buffer.from(encrypted, "base64")),
612
- decipher.final()
613
- ]);
614
- return JSON.parse(plaintext.toString("utf-8"));
615
- }
616
- function isLicenseFile(obj) {
617
- return typeof obj === "object" && obj !== null && obj.version === 1;
618
- }
619
- function isEncryptedLicenseFile(obj) {
620
- if (!isLicenseFile(obj)) return false;
621
- const record = obj;
622
- return typeof record.encrypted === "string" && typeof record.iv === "string" && typeof record.tag === "string";
623
- }
624
- async function getMachineId2() {
625
- try {
626
- const { readFile: readMachineId } = await import("fs/promises");
627
- const { join: join7 } = await import("path");
628
- const { homedir: homedir4 } = await import("os");
629
- const machineIdPath = join7(homedir4(), ".brew-tui", "machine-id");
630
- return (await readMachineId(machineIdPath, "utf-8")).trim() || null;
631
- } catch {
632
- return null;
633
- }
634
- }
635
- async function loadLicense() {
636
- try {
637
- const raw = await readFile2(LICENSE_PATH, "utf-8");
638
- const parsed = JSON.parse(raw);
639
- if (!isLicenseFile(parsed)) {
640
- throw new Error("Invalid license data format");
641
- }
642
- const file = parsed;
643
- if (file.version !== 1) {
644
- throw new Error("Unsupported data version");
645
- }
646
- if (isEncryptedLicenseFile(file)) {
647
- const data = decryptLicenseData(file.encrypted, file.iv, file.tag);
648
- const fileRecord = file;
649
- if (fileRecord.machineId) {
650
- const currentMachineId = await getMachineId2();
651
- if (currentMachineId && fileRecord.machineId !== currentMachineId) {
652
- throw new Error("License was activated on a different machine");
653
- }
654
- }
655
- return data;
656
- }
657
- if (file.license) {
658
- const data = file.license;
659
- await saveLicense(data);
660
- return data;
661
- }
662
- return null;
663
- } catch {
664
- return null;
665
- }
666
- }
667
- async function saveLicense(data) {
668
- await ensureDataDirs();
669
- const { encrypted, iv, tag } = encryptLicenseData(data);
670
- const machineId = await getMachineId2();
671
- const file = { version: 1, encrypted, iv, tag };
672
- if (machineId) file.machineId = machineId;
673
- const tmpPath = LICENSE_PATH + ".tmp";
674
- await writeFile2(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
675
- await rename(tmpPath, LICENSE_PATH);
676
- }
677
- async function clearLicense() {
678
- try {
679
- await rm(LICENSE_PATH);
680
- } catch {
681
- }
682
- }
683
- function isExpired(license) {
684
- if (!license.expiresAt) return false;
685
- return new Date(license.expiresAt).getTime() < Date.now();
686
- }
687
- function needsRevalidation(license) {
688
- const lastValidated = new Date(license.lastValidatedAt).getTime();
689
- if (isNaN(lastValidated)) return true;
690
- return Date.now() - lastValidated > REVALIDATION_INTERVAL_MS;
691
- }
692
- function isWithinGracePeriod(license) {
693
- const lastValidated = new Date(license.lastValidatedAt).getTime();
694
- if (isNaN(lastValidated)) return false;
695
- return Date.now() - lastValidated < GRACE_PERIOD_MS;
696
- }
697
- function getDegradationLevel(license) {
698
- const lastValidated = new Date(license.lastValidatedAt).getTime();
699
- if (isNaN(lastValidated)) return "expired";
700
- const elapsed = Date.now() - lastValidated;
701
- if (elapsed < 0) return "none";
702
- const days = elapsed / (24 * 60 * 60 * 1e3);
703
- if (days <= 7) return "none";
704
- if (days <= 14) return "warning";
705
- if (days <= 30) return "limited";
706
- return "expired";
707
- }
708
- function validateLicenseKey(key) {
709
- if (key.length < 10 || key.length > 100) {
710
- throw new Error("Invalid license key format");
711
- }
712
- if (!/^[\w-]+$/.test(key)) {
713
- throw new Error("Invalid license key format");
714
- }
715
- }
716
- function detectPlan(key) {
717
- const upper = key.toUpperCase();
718
- return upper.startsWith("BTUI-T-") || upper.startsWith("BTUI-T_") ? "team" : "pro";
719
- }
720
- async function activate(key) {
721
- validateLicenseKey(key);
722
- checkRateLimit();
723
- let success = false;
724
- try {
725
- const res = await activateLicense(key);
726
- if (!res.activated) {
727
- throw new Error(res.error ?? "Activation failed");
728
- }
729
- const license = {
730
- key,
731
- instanceId: res.instance.id,
732
- status: "active",
733
- customerEmail: res.meta.customer_email,
734
- customerName: res.meta.customer_name,
735
- plan: detectPlan(key),
736
- activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
737
- expiresAt: res.license_key.expires_at,
738
- lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString()
739
- };
740
- await saveLicense(license);
741
- success = true;
742
- return license;
743
- } finally {
744
- recordAttempt(success);
745
- }
746
- }
747
- function isNetworkError(err) {
748
- const msg = err instanceof Error ? err.message : String(err);
749
- return /fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network|timeout|abort/i.test(msg);
750
- }
751
- async function revalidate(license) {
752
- try {
753
- const res = await validateLicense(license.key, license.instanceId);
754
- if (res.valid) {
755
- const updated = {
756
- ...license,
757
- lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
758
- status: "active",
759
- expiresAt: res.license_key.expires_at
760
- };
761
- await saveLicense(updated);
762
- return "valid";
763
- }
764
- await saveLicense({ ...license, status: "expired" });
765
- return "expired";
766
- } catch (err) {
767
- if (isNetworkError(err)) {
768
- return isWithinGracePeriod(license) ? "grace" : "expired";
769
- }
770
- await saveLicense({ ...license, status: "expired" });
771
- return "expired";
772
- }
773
- }
774
- async function deactivate(license) {
775
- let remoteSuccess = false;
776
- for (let attempt = 0; attempt < 3; attempt++) {
777
- try {
778
- await deactivateLicense(license.key, license.instanceId);
779
- remoteSuccess = true;
780
- break;
781
- } catch {
782
- if (attempt < 2) await new Promise((r) => setTimeout(r, 1e3));
783
- }
784
- }
785
- await clearLicense();
786
- return { remoteSuccess };
787
- }
788
-
789
496
  // src/lib/license/anti-tamper.ts
790
497
  var _originalIsPro = null;
791
498
  var _originalGetState = null;
@@ -885,6 +592,15 @@ var useLicenseStore = create2((set, get) => ({
885
592
  }
886
593
  set({ status: "free", license: null, degradation: "none", error: null });
887
594
  },
595
+ revalidate: async () => {
596
+ const { license } = get();
597
+ if (!license) return;
598
+ try {
599
+ await doRevalidation(license, set);
600
+ } catch (err) {
601
+ set({ error: err instanceof Error ? err.message : String(err) });
602
+ }
603
+ },
888
604
  // Team is a superset of Pro — team users have full Pro access plus team features.
889
605
  // Pro users do NOT get Team features (Compliance) without paying for the Team tier.
890
606
  isPro: () => {
@@ -964,16 +680,111 @@ function useGlobalKeyboard(opts) {
964
680
  }, { isActive: !opts?.disabled });
965
681
  }
966
682
 
967
- // src/components/common/upgrade-prompt.tsx
968
- import { Box as Box4, Text as Text4 } from "ink";
683
+ // src/lib/onboarding.ts
684
+ import { access, writeFile, mkdir } from "fs/promises";
685
+ var cached = null;
686
+ async function hasCompletedOnboarding() {
687
+ if (cached !== null) return cached;
688
+ try {
689
+ await access(ONBOARDING_FLAG_PATH);
690
+ cached = true;
691
+ } catch {
692
+ cached = false;
693
+ }
694
+ return cached;
695
+ }
696
+ async function markOnboardingComplete() {
697
+ await ensureDataDirs();
698
+ await mkdir(DATA_DIR, { recursive: true, mode: 448 });
699
+ await writeFile(ONBOARDING_FLAG_PATH, (/* @__PURE__ */ new Date()).toISOString(), {
700
+ encoding: "utf-8",
701
+ mode: 384
702
+ });
703
+ cached = true;
704
+ }
705
+
706
+ // src/views/welcome.tsx
707
+ import { useEffect } from "react";
708
+ import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
969
709
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
710
+ function WelcomeView({ onContinue }) {
711
+ useEffect(() => {
712
+ return () => {
713
+ };
714
+ }, []);
715
+ useInput2((input, key) => {
716
+ if (key.return || input === " " || key.escape) {
717
+ void markOnboardingComplete().finally(onContinue);
718
+ }
719
+ });
720
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingY: SPACING.md, paddingX: SPACING.lg, children: [
721
+ /* @__PURE__ */ jsx5(Box4, { children: /* @__PURE__ */ jsx5(GradientText, { colors: GRADIENTS.gold, bold: true, children: t("welcome_title") }) }),
722
+ /* @__PURE__ */ jsx5(Box4, { marginTop: SPACING.sm, children: /* @__PURE__ */ jsx5(Text4, { color: COLORS.text, children: t("welcome_intro") }) }),
723
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: SPACING.sm, children: [
724
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.muted, children: t("welcome_keysHeader") }),
725
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: SPACING.sm, marginTop: SPACING.xs, children: [
726
+ /* @__PURE__ */ jsxs4(Text4, { children: [
727
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "1-9 0" }),
728
+ " ",
729
+ t("welcome_keyJumpView")
730
+ ] }),
731
+ /* @__PURE__ */ jsxs4(Text4, { children: [
732
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "Tab" }),
733
+ " ",
734
+ t("welcome_keyCycleView")
735
+ ] }),
736
+ /* @__PURE__ */ jsxs4(Text4, { children: [
737
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "j k" }),
738
+ " ",
739
+ t("welcome_keyMove")
740
+ ] }),
741
+ /* @__PURE__ */ jsxs4(Text4, { children: [
742
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "/" }),
743
+ " ",
744
+ t("welcome_keySearch")
745
+ ] }),
746
+ /* @__PURE__ */ jsxs4(Text4, { children: [
747
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "Enter" }),
748
+ " ",
749
+ t("welcome_keySelect")
750
+ ] }),
751
+ /* @__PURE__ */ jsxs4(Text4, { children: [
752
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "Esc" }),
753
+ " ",
754
+ t("welcome_keyBack")
755
+ ] }),
756
+ /* @__PURE__ */ jsxs4(Text4, { children: [
757
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "L" }),
758
+ " ",
759
+ t("welcome_keyLocale")
760
+ ] }),
761
+ /* @__PURE__ */ jsxs4(Text4, { children: [
762
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.gold, bold: true, children: "q" }),
763
+ " ",
764
+ t("welcome_keyQuit")
765
+ ] })
766
+ ] })
767
+ ] }),
768
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: SPACING.sm, children: [
769
+ /* @__PURE__ */ jsx5(Text4, { color: COLORS.muted, children: t("welcome_proHeader") }),
770
+ /* @__PURE__ */ jsx5(Box4, { paddingLeft: SPACING.sm, children: /* @__PURE__ */ jsx5(Text4, { color: COLORS.textSecondary, children: t("welcome_proIntro") }) })
771
+ ] }),
772
+ /* @__PURE__ */ jsx5(Box4, { marginTop: SPACING.md, children: /* @__PURE__ */ jsx5(Text4, { color: COLORS.success, bold: true, children: t("welcome_continueHint") }) })
773
+ ] });
774
+ }
775
+
776
+ // src/components/common/upgrade-prompt.tsx
777
+ import { Box as Box5, Text as Text5 } from "ink";
778
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
970
779
  var FEATURE_KEYS = {
971
780
  profiles: { title: "upgrade_profiles", desc: "upgrade_profilesDesc" },
972
781
  "smart-cleanup": { title: "upgrade_cleanup", desc: "upgrade_cleanupDesc" },
973
782
  history: { title: "upgrade_history", desc: "upgrade_historyDesc" },
974
783
  "security-audit": { title: "upgrade_security", desc: "upgrade_securityDesc" },
975
784
  sync: { title: "upgrade_sync", desc: "upgrade_syncDesc" },
976
- compliance: { title: "upgrade_compliance", desc: "upgrade_complianceDesc" }
785
+ compliance: { title: "upgrade_compliance", desc: "upgrade_complianceDesc" },
786
+ rollback: { title: "upgrade_rollback", desc: "upgrade_rollbackDesc" },
787
+ brewfile: { title: "upgrade_brewfile", desc: "upgrade_brewfileDesc" }
977
788
  };
978
789
  function UpgradePrompt({ viewId }) {
979
790
  const keys = FEATURE_KEYS[viewId];
@@ -984,41 +795,41 @@ function UpgradePrompt({ viewId }) {
984
795
  const pricingKey = team ? "upgrade_teamPricing" : "upgrade_pricing";
985
796
  const buyUrlKey = team ? "upgrade_buyUrlTeam" : "upgrade_buyUrl";
986
797
  const labelKey = team ? "upgrade_teamLabel" : "upgrade_proLabel";
987
- return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", alignItems: "center", paddingY: 2, children: /* @__PURE__ */ jsxs4(
988
- Box4,
798
+ return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", alignItems: "center", paddingY: SPACING.sm, children: /* @__PURE__ */ jsxs5(
799
+ Box5,
989
800
  {
990
801
  borderStyle: "double",
991
802
  borderColor: COLORS.brand,
992
- paddingX: 3,
993
- paddingY: 2,
803
+ paddingX: SPACING.md,
804
+ paddingY: SPACING.sm,
994
805
  flexDirection: "column",
995
806
  alignItems: "center",
996
807
  width: "80%",
997
808
  children: [
998
- /* @__PURE__ */ jsxs4(Text4, { bold: true, color: COLORS.brand, children: [
809
+ /* @__PURE__ */ jsxs5(Text5, { bold: true, color: COLORS.brand, children: [
999
810
  "\u2B50",
1000
811
  " ",
1001
812
  t(headerKey, { title })
1002
813
  ] }),
1003
- /* @__PURE__ */ jsx5(Text4, { children: " " }),
1004
- /* @__PURE__ */ jsx5(Text4, { color: COLORS.text, wrap: "wrap", children: t(keys.desc) }),
1005
- /* @__PURE__ */ jsx5(Text4, { children: " " }),
1006
- /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", alignItems: "center", children: [
1007
- /* @__PURE__ */ jsx5(Text4, { color: COLORS.info, bold: true, children: t(pricingKey) }),
1008
- /* @__PURE__ */ jsx5(Text4, { children: " " }),
1009
- /* @__PURE__ */ jsx5(Text4, { color: COLORS.muted, children: t("upgrade_buyAt") }),
1010
- /* @__PURE__ */ jsxs4(Text4, { color: COLORS.sky, bold: true, children: [
814
+ /* @__PURE__ */ jsx6(Text5, { children: " " }),
815
+ /* @__PURE__ */ jsx6(Text5, { color: COLORS.text, wrap: "wrap", children: t(keys.desc) }),
816
+ /* @__PURE__ */ jsx6(Text5, { children: " " }),
817
+ /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", alignItems: "center", children: [
818
+ /* @__PURE__ */ jsx6(Text5, { color: COLORS.info, bold: true, children: t(pricingKey) }),
819
+ /* @__PURE__ */ jsx6(Text5, { children: " " }),
820
+ /* @__PURE__ */ jsx6(Text5, { color: COLORS.muted, children: t("upgrade_buyAt") }),
821
+ /* @__PURE__ */ jsxs5(Text5, { color: COLORS.sky, bold: true, children: [
1011
822
  " ",
1012
823
  t(buyUrlKey)
1013
824
  ] }),
1014
- /* @__PURE__ */ jsx5(Text4, { children: " " }),
1015
- /* @__PURE__ */ jsx5(Text4, { color: COLORS.muted, children: t("upgrade_activateWith") }),
1016
- /* @__PURE__ */ jsxs4(Text4, { color: COLORS.success, bold: true, children: [
825
+ /* @__PURE__ */ jsx6(Text5, { children: " " }),
826
+ /* @__PURE__ */ jsx6(Text5, { color: COLORS.muted, children: t("upgrade_activateWith") }),
827
+ /* @__PURE__ */ jsxs5(Text5, { color: COLORS.success, bold: true, children: [
1017
828
  " ",
1018
829
  t("upgrade_activateCmd")
1019
830
  ] }),
1020
- /* @__PURE__ */ jsx5(Text4, { children: " " }),
1021
- /* @__PURE__ */ jsx5(Text4, { color: COLORS.brand, children: t(labelKey) })
831
+ /* @__PURE__ */ jsx6(Text5, { children: " " }),
832
+ /* @__PURE__ */ jsx6(Text5, { color: COLORS.brand, children: t(labelKey) })
1022
833
  ] })
1023
834
  ]
1024
835
  }
@@ -1026,8 +837,8 @@ function UpgradePrompt({ viewId }) {
1026
837
  }
1027
838
 
1028
839
  // src/views/dashboard.tsx
1029
- import { useEffect, useMemo as useMemo2 } from "react";
1030
- import { Box as Box8, Text as Text10, useStdout as useStdout3 } from "ink";
840
+ import { useEffect as useEffect2, useMemo as useMemo2 } from "react";
841
+ import { Box as Box9, Text as Text11, useInput as useInput3, useStdout as useStdout3 } from "ink";
1031
842
 
1032
843
  // src/stores/brew-store.ts
1033
844
  import { create as create4 } from "zustand";
@@ -1231,11 +1042,26 @@ function validatePackageName(name) {
1231
1042
  async function brewUpdate() {
1232
1043
  return new Promise((resolve, reject) => {
1233
1044
  const proc = spawn("brew", ["update"], { stdio: "ignore" });
1045
+ let settled = false;
1046
+ const timeout = setTimeout(() => {
1047
+ if (settled) return;
1048
+ settled = true;
1049
+ proc.kill("SIGTERM");
1050
+ reject(new Error("brew update timed out after 120s"));
1051
+ }, 12e4);
1234
1052
  proc.on("close", (code) => {
1053
+ if (settled) return;
1054
+ settled = true;
1055
+ clearTimeout(timeout);
1235
1056
  if (code === 0) resolve();
1236
1057
  else reject(new Error(`brew update exited with code ${code}`));
1237
1058
  });
1238
- proc.on("error", reject);
1059
+ proc.on("error", (err) => {
1060
+ if (settled) return;
1061
+ settled = true;
1062
+ clearTimeout(timeout);
1063
+ reject(err);
1064
+ });
1239
1065
  });
1240
1066
  }
1241
1067
  async function getInstalled() {
@@ -1265,6 +1091,35 @@ async function getCaskInfo(name) {
1265
1091
  return null;
1266
1092
  }
1267
1093
  }
1094
+ function formulaeFromCask(cask) {
1095
+ return {
1096
+ name: cask.token,
1097
+ full_name: cask.full_token,
1098
+ tap: "",
1099
+ desc: cask.desc,
1100
+ license: "",
1101
+ homepage: cask.homepage,
1102
+ versions: { stable: cask.version, head: null, bottle: false },
1103
+ dependencies: [],
1104
+ build_dependencies: [],
1105
+ installed: cask.installed ? [{
1106
+ version: cask.installed,
1107
+ used_options: [],
1108
+ built_as_bottle: false,
1109
+ poured_from_bottle: false,
1110
+ time: cask.installed_time ?? 0,
1111
+ runtime_dependencies: [],
1112
+ installed_as_dependency: false,
1113
+ installed_on_request: true
1114
+ }] : [],
1115
+ linked_keg: null,
1116
+ pinned: false,
1117
+ outdated: cask.outdated,
1118
+ deprecated: false,
1119
+ keg_only: false,
1120
+ caveats: null
1121
+ };
1122
+ }
1268
1123
  async function search(term) {
1269
1124
  const safeTerm = term.replace(/^-+/, "");
1270
1125
  if (!safeTerm) return { formulae: [], casks: [] };
@@ -1320,9 +1175,23 @@ function formulaeToListItems(formulae) {
1320
1175
  };
1321
1176
  });
1322
1177
  }
1178
+ var impactCache = /* @__PURE__ */ new Map();
1179
+ var IMPACT_CACHE_LIMIT = 64;
1180
+ function impactKey(name, from, to, type) {
1181
+ return `${type}::${name}::${from}::${to}`;
1182
+ }
1323
1183
  async function getUpgradeImpact(packageName, fromVersion, toVersion, packageType) {
1324
1184
  validatePackageName(packageName);
1325
- return analyzeUpgradeImpact(packageName, fromVersion, toVersion, packageType);
1185
+ const key = impactKey(packageName, fromVersion, toVersion, packageType);
1186
+ const cached2 = impactCache.get(key);
1187
+ if (cached2) return cached2;
1188
+ const result = await analyzeUpgradeImpact(packageName, fromVersion, toVersion, packageType);
1189
+ if (impactCache.size >= IMPACT_CACHE_LIMIT) {
1190
+ const firstKey = impactCache.keys().next().value;
1191
+ if (firstKey !== void 0) impactCache.delete(firstKey);
1192
+ }
1193
+ impactCache.set(key, result);
1194
+ return result;
1326
1195
  }
1327
1196
  function casksToListItems(casks) {
1328
1197
  return casks.map((c) => ({
@@ -1568,7 +1437,7 @@ async function queryOneByOne(packages) {
1568
1437
  try {
1569
1438
  const partial = await queryBatch(
1570
1439
  [pkg],
1571
- [{ package: { name: pkg.name, ecosystem: "Homebrew" }, version: pkg.version }]
1440
+ [{ package: { name: pkg.name, ecosystem: "Bitnami" }, version: pkg.version }]
1572
1441
  );
1573
1442
  for (const [k, v] of partial) result.set(k, v);
1574
1443
  } catch (err) {
@@ -1583,7 +1452,7 @@ async function queryOneByOne(packages) {
1583
1452
  try {
1584
1453
  const retryResult = await queryBatch(
1585
1454
  [pkg],
1586
- [{ package: { name: pkg.name, ecosystem: "Homebrew" }, version: pkg.version }]
1455
+ [{ package: { name: pkg.name, ecosystem: "Bitnami" }, version: pkg.version }]
1587
1456
  );
1588
1457
  for (const [k, v] of retryResult) result.set(k, v);
1589
1458
  } catch {
@@ -1609,7 +1478,7 @@ async function queryVulnerabilities(packages) {
1609
1478
  for (let i = 0; i < validPackages.length; i += BATCH_SIZE) {
1610
1479
  const batch = validPackages.slice(i, i + BATCH_SIZE);
1611
1480
  const queries = batch.map((p) => ({
1612
- package: { name: p.name, ecosystem: "Homebrew" },
1481
+ package: { name: p.name, ecosystem: "Bitnami" },
1613
1482
  version: p.version
1614
1483
  }));
1615
1484
  const partial = await queryBatch(batch, queries);
@@ -1885,7 +1754,9 @@ var useSyncStore = create7((set, get) => ({
1885
1754
  if (!config) throw new Error("Sync not initialized");
1886
1755
  const envelope = await readSyncEnvelope();
1887
1756
  if (!envelope) throw new Error("No sync data found");
1888
- const payload = decryptPayload(envelope.encrypted, envelope.iv, envelope.tag);
1757
+ const license = await loadLicense();
1758
+ if (!license) throw new Error("License missing");
1759
+ const payload = decryptPayload(envelope.encrypted, envelope.iv, envelope.tag, license.key);
1889
1760
  await applyConflictResolutions(payload, resolutions, config.machineId);
1890
1761
  const updatedConfig = await loadSyncConfig();
1891
1762
  set({ config: updatedConfig, conflicts: [], loading: false });
@@ -1933,53 +1804,53 @@ var useComplianceStore = create8((set, get) => ({
1933
1804
  }));
1934
1805
 
1935
1806
  // src/components/common/stat-card.tsx
1936
- import { Box as Box5, Text as Text5, useStdout as useStdout2 } from "ink";
1937
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1938
- function StatCard({ label, value, color = "white" }) {
1807
+ import { Box as Box6, Text as Text6, useStdout as useStdout2 } from "ink";
1808
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1809
+ function StatCard({ label, value, color = COLORS.white }) {
1939
1810
  const { stdout } = useStdout2();
1940
1811
  const cols = stdout?.columns ?? 80;
1941
1812
  const minW = cols < 60 ? 12 : cols < 100 ? 14 : 16;
1942
- return /* @__PURE__ */ jsxs5(
1943
- Box5,
1813
+ return /* @__PURE__ */ jsxs6(
1814
+ Box6,
1944
1815
  {
1945
1816
  borderStyle: "round",
1946
1817
  borderColor: color,
1947
- paddingX: 2,
1948
- paddingY: 0,
1818
+ paddingX: SPACING.sm,
1819
+ paddingY: SPACING.none,
1949
1820
  flexDirection: "column",
1950
1821
  alignItems: "center",
1951
1822
  minWidth: minW,
1952
1823
  children: [
1953
- /* @__PURE__ */ jsx6(Text5, { bold: true, color, children: value }),
1954
- /* @__PURE__ */ jsx6(Text5, { color: COLORS.muted, children: label })
1824
+ /* @__PURE__ */ jsx7(Text6, { bold: true, color, children: value }),
1825
+ /* @__PURE__ */ jsx7(Text6, { color: COLORS.muted, children: label })
1955
1826
  ]
1956
1827
  }
1957
1828
  );
1958
1829
  }
1959
1830
 
1960
1831
  // src/components/common/loading.tsx
1961
- import { Box as Box6, Text as Text6 } from "ink";
1832
+ import { Box as Box7, Text as Text7 } from "ink";
1962
1833
  import { Spinner } from "@inkjs/ui";
1963
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1834
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1964
1835
  function Loading({ message }) {
1965
1836
  useLocaleStore((s) => s.locale);
1966
- return /* @__PURE__ */ jsx7(Box6, { paddingY: 1, children: /* @__PURE__ */ jsx7(Spinner, { label: message ?? t("loading_default") }) });
1837
+ return /* @__PURE__ */ jsx8(Box7, { paddingY: SPACING.xs, children: /* @__PURE__ */ jsx8(Spinner, { label: message ?? t("loading_default") }) });
1967
1838
  }
1968
1839
  function ErrorMessage({ message }) {
1969
1840
  useLocaleStore((s) => s.locale);
1970
- return /* @__PURE__ */ jsxs6(Box6, { paddingY: 1, children: [
1971
- /* @__PURE__ */ jsxs6(Text6, { color: COLORS.error, bold: true, children: [
1841
+ return /* @__PURE__ */ jsxs7(Box7, { paddingY: SPACING.xs, children: [
1842
+ /* @__PURE__ */ jsxs7(Text7, { color: COLORS.error, bold: true, children: [
1972
1843
  "\u2718",
1973
1844
  " ",
1974
1845
  t("error_prefix")
1975
1846
  ] }),
1976
- /* @__PURE__ */ jsx7(Text6, { color: COLORS.error, children: message })
1847
+ /* @__PURE__ */ jsx8(Text7, { color: COLORS.error, children: message })
1977
1848
  ] });
1978
1849
  }
1979
1850
 
1980
1851
  // src/components/common/status-badge.tsx
1981
- import { Text as Text7 } from "ink";
1982
- import { jsxs as jsxs7 } from "react/jsx-runtime";
1852
+ import { Text as Text8 } from "ink";
1853
+ import { jsxs as jsxs8 } from "react/jsx-runtime";
1983
1854
  var BADGE_STYLES = {
1984
1855
  success: { icon: "\u2714", color: COLORS.success },
1985
1856
  warning: { icon: "\u25B2", color: COLORS.warning },
@@ -1989,7 +1860,7 @@ var BADGE_STYLES = {
1989
1860
  };
1990
1861
  function StatusBadge({ label, variant }) {
1991
1862
  const { icon, color } = BADGE_STYLES[variant];
1992
- return /* @__PURE__ */ jsxs7(Text7, { color, children: [
1863
+ return /* @__PURE__ */ jsxs8(Text8, { color, children: [
1993
1864
  icon,
1994
1865
  " ",
1995
1866
  label
@@ -1997,16 +1868,16 @@ function StatusBadge({ label, variant }) {
1997
1868
  }
1998
1869
 
1999
1870
  // src/components/common/section-header.tsx
2000
- import { Box as Box7, Text as Text8 } from "ink";
2001
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1871
+ import { Box as Box8, Text as Text9 } from "ink";
1872
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2002
1873
  function SectionHeader({ emoji, title, color = COLORS.gold, gradient, count }) {
2003
- return /* @__PURE__ */ jsxs8(Box7, { gap: 1, children: [
2004
- /* @__PURE__ */ jsxs8(Text8, { children: [
1874
+ return /* @__PURE__ */ jsxs9(Box8, { gap: SPACING.xs, children: [
1875
+ /* @__PURE__ */ jsxs9(Text9, { children: [
2005
1876
  emoji,
2006
1877
  " "
2007
1878
  ] }),
2008
- gradient ? /* @__PURE__ */ jsx8(GradientText, { colors: gradient, bold: true, children: title }) : /* @__PURE__ */ jsx8(Text8, { bold: true, color, children: title }),
2009
- count !== void 0 && /* @__PURE__ */ jsxs8(Text8, { color: COLORS.textSecondary, children: [
1879
+ gradient ? /* @__PURE__ */ jsx9(GradientText, { colors: gradient, bold: true, children: title }) : /* @__PURE__ */ jsx9(Text9, { bold: true, color, children: title }),
1880
+ count !== void 0 && /* @__PURE__ */ jsxs9(Text9, { color: COLORS.textSecondary, children: [
2010
1881
  "(",
2011
1882
  count,
2012
1883
  ")"
@@ -2015,23 +1886,23 @@ function SectionHeader({ emoji, title, color = COLORS.gold, gradient, count }) {
2015
1886
  }
2016
1887
 
2017
1888
  // src/components/common/version-arrow.tsx
2018
- import { Text as Text9 } from "ink";
2019
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1889
+ import { Text as Text10 } from "ink";
1890
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2020
1891
  function VersionArrow({ current, latest }) {
2021
- return /* @__PURE__ */ jsxs9(Fragment3, { children: [
2022
- /* @__PURE__ */ jsxs9(Text9, { color: COLORS.muted, children: [
1892
+ return /* @__PURE__ */ jsxs10(Fragment3, { children: [
1893
+ /* @__PURE__ */ jsxs10(Text10, { color: COLORS.muted, children: [
2023
1894
  t("version_installed"),
2024
1895
  " "
2025
1896
  ] }),
2026
- /* @__PURE__ */ jsx9(Text9, { color: COLORS.error, children: current }),
2027
- /* @__PURE__ */ jsx9(Text9, { color: COLORS.warning, children: " \u2500\u2500 " }),
2028
- /* @__PURE__ */ jsx9(Text9, { color: COLORS.gold, children: "\u25B6" }),
2029
- /* @__PURE__ */ jsxs9(Text9, { color: COLORS.muted, children: [
1897
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.error, children: current }),
1898
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.warning, children: " \u2500\u2500 " }),
1899
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.gold, children: "\u25B6" }),
1900
+ /* @__PURE__ */ jsxs10(Text10, { color: COLORS.muted, children: [
2030
1901
  " ",
2031
1902
  t("version_available"),
2032
1903
  " "
2033
1904
  ] }),
2034
- /* @__PURE__ */ jsx9(Text9, { color: COLORS.teal, children: latest })
1905
+ /* @__PURE__ */ jsx10(Text10, { color: COLORS.teal, children: latest })
2035
1906
  ] });
2036
1907
  }
2037
1908
 
@@ -2063,7 +1934,7 @@ function truncate(str, maxLen) {
2063
1934
  }
2064
1935
 
2065
1936
  // src/views/dashboard.tsx
2066
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1937
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2067
1938
  function ProStatusPanel() {
2068
1939
  const security = useSecurityStore((s) => s.summary);
2069
1940
  const drift = useBrewfileStore((s) => s.drift);
@@ -2075,40 +1946,53 @@ function ProStatusPanel() {
2075
1946
  const lastSync = syncConfig?.lastSync ?? null;
2076
1947
  const syncAgo = lastSync ? formatRelativeTime(new Date(lastSync).getTime() / 1e3) : null;
2077
1948
  const violationCount = complianceReport ? complianceReport.violations.length : null;
2078
- return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.purple, paddingX: 2, paddingY: 0, marginTop: 1, children: [
2079
- /* @__PURE__ */ jsx10(Text10, { bold: true, color: COLORS.purple, children: t("dashboard_pro_status") }),
2080
- /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
2081
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_security") }),
2082
- cveCount === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "\u2014" }) : cveCount === 0 ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.success, children: t("dashboard_no_cves") }) : /* @__PURE__ */ jsxs10(Text10, { color: COLORS.error, children: [
1949
+ return /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.purple, paddingX: SPACING.sm, paddingY: SPACING.none, marginTop: SPACING.xs, children: [
1950
+ /* @__PURE__ */ jsx11(Text11, { bold: true, color: COLORS.purple, children: t("dashboard_pro_status") }),
1951
+ /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
1952
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_security") }),
1953
+ cveCount === null ? /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: "\u2014" }) : cveCount === 0 ? /* @__PURE__ */ jsx11(Text11, { color: COLORS.success, children: t("dashboard_no_cves") }) : /* @__PURE__ */ jsxs11(Text11, { color: COLORS.error, children: [
2083
1954
  t("dashboard_cves", { count: String(cveCount) }),
2084
1955
  criticalCount && criticalCount > 0 ? ` (${criticalCount} critical)` : ""
2085
1956
  ] })
2086
1957
  ] }),
2087
- /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
2088
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_brewfile") }),
2089
- driftScore === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "\u2014" }) : /* @__PURE__ */ jsxs10(Text10, { color: driftScore >= 80 ? COLORS.success : COLORS.warning, children: [
1958
+ /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
1959
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_brewfile") }),
1960
+ driftScore === null ? /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: "\u2014" }) : /* @__PURE__ */ jsxs11(Text11, { color: driftScore >= 80 ? COLORS.success : COLORS.warning, children: [
2090
1961
  driftScore,
2091
1962
  "%"
2092
1963
  ] })
2093
1964
  ] }),
2094
- /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
2095
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_sync") }),
2096
- syncAgo === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_sync_never") }) : /* @__PURE__ */ jsx10(Text10, { color: COLORS.info, children: t("dashboard_sync_ago", { time: syncAgo }) })
1965
+ /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
1966
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_sync") }),
1967
+ syncAgo === null ? /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_sync_never") }) : /* @__PURE__ */ jsx11(Text11, { color: COLORS.info, children: t("dashboard_sync_ago", { time: syncAgo }) })
2097
1968
  ] }),
2098
- /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
2099
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_compliance") }),
2100
- violationCount === null ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: "\u2014" }) : violationCount === 0 ? /* @__PURE__ */ jsx10(Text10, { color: COLORS.success, children: t("dashboard_compliance_ok") }) : /* @__PURE__ */ jsx10(Text10, { color: COLORS.warning, children: t("dashboard_compliance_violations", { count: String(violationCount) }) })
1969
+ /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
1970
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_compliance") }),
1971
+ violationCount === null ? /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: "\u2014" }) : violationCount === 0 ? /* @__PURE__ */ jsx11(Text11, { color: COLORS.success, children: t("dashboard_compliance_ok") }) : /* @__PURE__ */ jsx11(Text11, { color: COLORS.warning, children: t("dashboard_compliance_violations", { count: String(violationCount) }) })
2101
1972
  ] })
2102
1973
  ] });
2103
1974
  }
2104
1975
  function DashboardView() {
2105
- const { formulae, casks, outdated, services, config, loading, errors, lastFetchedAt, fetchAll } = useBrewStore();
1976
+ const formulae = useBrewStore((s) => s.formulae);
1977
+ const casks = useBrewStore((s) => s.casks);
1978
+ const outdated = useBrewStore((s) => s.outdated);
1979
+ const services = useBrewStore((s) => s.services);
1980
+ const config = useBrewStore((s) => s.config);
1981
+ const loading = useBrewStore((s) => s.loading);
1982
+ const errors = useBrewStore((s) => s.errors);
1983
+ const lastFetchedAt = useBrewStore((s) => s.lastFetchedAt);
1984
+ const fetchAll = useBrewStore((s) => s.fetchAll);
2106
1985
  const isPro = useLicenseStore((s) => s.isPro);
2107
1986
  const { stdout } = useStdout3();
2108
1987
  const columns = stdout?.columns ?? 80;
2109
- useEffect(() => {
1988
+ useEffect2(() => {
2110
1989
  fetchAll();
2111
1990
  }, []);
1991
+ useInput3((input) => {
1992
+ if (errors.installed && (input === "r" || input === "R")) {
1993
+ void fetchAll();
1994
+ }
1995
+ });
2112
1996
  const errorServiceList = useMemo2(
2113
1997
  () => services.filter((s) => s.status === "error"),
2114
1998
  [services]
@@ -2126,15 +2010,23 @@ function DashboardView() {
2126
2010
  const outdatedValue = loading.outdated ? "..." : errors.outdated ? t("dashboard_statError") : outdated.formulae.length + outdated.casks.length;
2127
2011
  const servicesValue = loading.services ? "..." : errors.services ? t("dashboard_statError") : `${runningServices}/${services.length}`;
2128
2012
  const lastUpdated = lastFetchedAt.installed ? formatRelativeTime(lastFetchedAt.installed / 1e3) : null;
2129
- if (loading.installed) return /* @__PURE__ */ jsx10(Loading, { message: t("loading_fetchingBrew") });
2130
- if (errors.installed) return /* @__PURE__ */ jsx10(ErrorMessage, { message: errors.installed });
2013
+ if (loading.installed) return /* @__PURE__ */ jsx11(Loading, { message: t("loading_fetchingBrew") });
2014
+ if (errors.installed) {
2015
+ return /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
2016
+ /* @__PURE__ */ jsx11(ErrorMessage, { message: errors.installed }),
2017
+ /* @__PURE__ */ jsx11(Box9, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs11(Text11, { color: COLORS.textSecondary, children: [
2018
+ "r:",
2019
+ t("hint_refresh")
2020
+ ] }) })
2021
+ ] });
2022
+ }
2131
2023
  const isNarrow = columns < 60;
2132
- return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", gap: 2, children: [
2133
- /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4CA}", title: t("dashboard_overview"), gradient: GRADIENTS.gold }),
2134
- /* @__PURE__ */ jsxs10(Box8, { gap: 1, flexWrap: "wrap", flexDirection: isNarrow ? "column" : "row", children: [
2135
- /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_formulae"), value: formulae.length, color: COLORS.info }),
2136
- /* @__PURE__ */ jsx10(StatCard, { label: t("dashboard_casks"), value: casks.length, color: COLORS.purple }),
2137
- /* @__PURE__ */ jsx10(
2024
+ return /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", gap: SPACING.sm, children: [
2025
+ /* @__PURE__ */ jsx11(SectionHeader, { emoji: "\u{1F4CA}", title: t("dashboard_overview"), gradient: GRADIENTS.gold }),
2026
+ /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, flexWrap: "wrap", flexDirection: isNarrow ? "column" : "row", children: [
2027
+ /* @__PURE__ */ jsx11(StatCard, { label: t("dashboard_formulae"), value: formulae.length, color: COLORS.info }),
2028
+ /* @__PURE__ */ jsx11(StatCard, { label: t("dashboard_casks"), value: casks.length, color: COLORS.purple }),
2029
+ /* @__PURE__ */ jsx11(
2138
2030
  StatCard,
2139
2031
  {
2140
2032
  label: t("dashboard_outdated"),
@@ -2142,7 +2034,7 @@ function DashboardView() {
2142
2034
  color: typeof outdatedValue === "number" && outdatedValue > 0 ? COLORS.warning : errors.outdated ? COLORS.error : COLORS.success
2143
2035
  }
2144
2036
  ),
2145
- /* @__PURE__ */ jsx10(
2037
+ /* @__PURE__ */ jsx11(
2146
2038
  StatCard,
2147
2039
  {
2148
2040
  label: t("dashboard_services"),
@@ -2151,66 +2043,66 @@ function DashboardView() {
2151
2043
  }
2152
2044
  )
2153
2045
  ] }),
2154
- lastUpdated && /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_lastUpdated", { time: lastUpdated }) }),
2155
- partialErrors.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, paddingY: 0, children: [
2156
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.warning, bold: true, children: t("dashboard_partialData") }),
2157
- partialErrors.map((item) => /* @__PURE__ */ jsxs10(Text10, { color: COLORS.muted, children: [
2046
+ lastUpdated && /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_lastUpdated", { time: lastUpdated }) }),
2047
+ partialErrors.length > 0 && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, paddingY: SPACING.none, children: [
2048
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.warning, bold: true, children: t("dashboard_partialData") }),
2049
+ partialErrors.map((item) => /* @__PURE__ */ jsxs11(Text11, { color: COLORS.muted, children: [
2158
2050
  item.label,
2159
2051
  ": ",
2160
2052
  item.message
2161
2053
  ] }, item.label))
2162
2054
  ] }),
2163
- config && !errors.config && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
2164
- /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u2139\uFE0F", title: t("dashboard_systemInfo"), gradient: [COLORS.text, COLORS.muted] }),
2165
- /* @__PURE__ */ jsxs10(Box8, { borderStyle: "round", borderColor: COLORS.blue, paddingX: 2, paddingY: 0, flexDirection: "column", marginTop: 1, children: [
2166
- /* @__PURE__ */ jsxs10(Text10, { children: [
2167
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_homebrew") }),
2055
+ config && !errors.config && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
2056
+ /* @__PURE__ */ jsx11(SectionHeader, { emoji: "\u2139\uFE0F", title: t("dashboard_systemInfo"), gradient: [COLORS.text, COLORS.muted] }),
2057
+ /* @__PURE__ */ jsxs11(Box9, { borderStyle: "round", borderColor: COLORS.blue, paddingX: SPACING.sm, paddingY: SPACING.none, flexDirection: "column", marginTop: SPACING.xs, children: [
2058
+ /* @__PURE__ */ jsxs11(Text11, { children: [
2059
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_homebrew") }),
2168
2060
  " ",
2169
2061
  config.HOMEBREW_VERSION
2170
2062
  ] }),
2171
- /* @__PURE__ */ jsxs10(Text10, { children: [
2172
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_prefix") }),
2063
+ /* @__PURE__ */ jsxs11(Text11, { children: [
2064
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_prefix") }),
2173
2065
  " ",
2174
2066
  config.HOMEBREW_PREFIX
2175
2067
  ] }),
2176
- /* @__PURE__ */ jsxs10(Text10, { children: [
2177
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("dashboard_updated") }),
2068
+ /* @__PURE__ */ jsxs11(Text11, { children: [
2069
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("dashboard_updated") }),
2178
2070
  " ",
2179
2071
  config.coreUpdated
2180
2072
  ] })
2181
2073
  ] })
2182
2074
  ] }),
2183
- !errors.outdated && outdated.formulae.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
2184
- /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u{1F4E6}", title: t("dashboard_outdatedPackages"), gradient: GRADIENTS.fire }),
2185
- /* @__PURE__ */ jsxs10(Box8, { paddingLeft: 2, flexDirection: "column", children: [
2186
- outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
2187
- /* @__PURE__ */ jsx10(Text10, { color: COLORS.text, children: pkg.name }),
2188
- /* @__PURE__ */ jsx10(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version })
2075
+ !errors.outdated && outdated.formulae.length > 0 && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: SPACING.xs, children: [
2076
+ /* @__PURE__ */ jsx11(SectionHeader, { emoji: "\u{1F4E6}", title: t("dashboard_outdatedPackages"), gradient: GRADIENTS.fire }),
2077
+ /* @__PURE__ */ jsxs11(Box9, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
2078
+ outdated.formulae.slice(0, 10).map((pkg) => /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
2079
+ /* @__PURE__ */ jsx11(Text11, { color: COLORS.text, children: pkg.name }),
2080
+ /* @__PURE__ */ jsx11(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version })
2189
2081
  ] }, pkg.name)),
2190
- outdated.formulae.length > 10 && /* @__PURE__ */ jsx10(Text10, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: outdated.formulae.length - 10 }) })
2082
+ outdated.formulae.length > 10 && /* @__PURE__ */ jsx11(Text11, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: outdated.formulae.length - 10 }) })
2191
2083
  ] })
2192
2084
  ] }),
2193
- !errors.services && errorServices > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
2194
- /* @__PURE__ */ jsx10(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("dashboard_serviceErrors"), color: COLORS.error }),
2195
- /* @__PURE__ */ jsx10(Box8, { paddingLeft: 2, flexDirection: "column", children: errorServiceList.map((s) => /* @__PURE__ */ jsxs10(Box8, { gap: 1, children: [
2196
- /* @__PURE__ */ jsx10(StatusBadge, { label: t("badge_error"), variant: "error" }),
2197
- /* @__PURE__ */ jsx10(Text10, { children: s.name }),
2198
- s.exit_code != null && /* @__PURE__ */ jsx10(Text10, { color: COLORS.muted, children: t("common_exit", { code: s.exit_code }) })
2085
+ !errors.services && errorServices > 0 && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: SPACING.xs, children: [
2086
+ /* @__PURE__ */ jsx11(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("dashboard_serviceErrors"), color: COLORS.error }),
2087
+ /* @__PURE__ */ jsx11(Box9, { paddingLeft: SPACING.sm, flexDirection: "column", children: errorServiceList.map((s) => /* @__PURE__ */ jsxs11(Box9, { gap: SPACING.xs, children: [
2088
+ /* @__PURE__ */ jsx11(StatusBadge, { label: t("badge_error"), variant: "error" }),
2089
+ /* @__PURE__ */ jsx11(Text11, { children: s.name }),
2090
+ s.exit_code != null && /* @__PURE__ */ jsx11(Text11, { color: COLORS.muted, children: t("common_exit", { code: s.exit_code }) })
2199
2091
  ] }, s.name)) })
2200
2092
  ] }),
2201
- isPro() && /* @__PURE__ */ jsx10(ProStatusPanel, {})
2093
+ isPro() && /* @__PURE__ */ jsx11(ProStatusPanel, {})
2202
2094
  ] });
2203
2095
  }
2204
2096
 
2205
2097
  // src/views/installed.tsx
2206
- import { useState as useState3, useMemo as useMemo3, useEffect as useEffect5 } from "react";
2207
- import { Box as Box14, Text as Text16, useInput as useInput3, useStdout as useStdout4 } from "ink";
2098
+ import { useState as useState3, useMemo as useMemo3, useEffect as useEffect6 } from "react";
2099
+ import { Box as Box15, Text as Text17, useInput as useInput5, useStdout as useStdout4 } from "ink";
2208
2100
 
2209
2101
  // src/hooks/use-debounce.ts
2210
- import { useState, useEffect as useEffect2 } from "react";
2102
+ import { useState, useEffect as useEffect3 } from "react";
2211
2103
  function useDebounce(value, delayMs) {
2212
2104
  const [debounced, setDebounced] = useState(value);
2213
- useEffect2(() => {
2105
+ useEffect3(() => {
2214
2106
  const timer = setTimeout(() => setDebounced(value), delayMs);
2215
2107
  return () => clearTimeout(timer);
2216
2108
  }, [value, delayMs]);
@@ -2218,14 +2110,14 @@ function useDebounce(value, delayMs) {
2218
2110
  }
2219
2111
 
2220
2112
  // src/hooks/use-brew-stream.ts
2221
- import { useState as useState2, useCallback, useRef, useEffect as useEffect3 } from "react";
2113
+ import { useState as useState2, useCallback, useRef, useEffect as useEffect4 } from "react";
2222
2114
  var MAX_LINES = 100;
2223
2115
  async function logToHistory(args, success, error) {
2224
2116
  const detected = detectAction(args);
2225
2117
  if (!detected) return;
2226
2118
  try {
2227
2119
  const isPro = useLicenseStore.getState().isPro();
2228
- const { appendEntry: appendEntry2 } = await import("./history-logger-PBDOLKNJ.js");
2120
+ const { appendEntry: appendEntry2 } = await import("./history-logger-FJ3HZSFU.js");
2229
2121
  await appendEntry2(isPro, detected.action, detected.packageName, success, error);
2230
2122
  } catch {
2231
2123
  }
@@ -2237,7 +2129,7 @@ function useBrewStream() {
2237
2129
  const cancelRef = useRef(false);
2238
2130
  const generatorRef = useRef(null);
2239
2131
  const mountedRef = useRef(true);
2240
- useEffect3(() => {
2132
+ useEffect4(() => {
2241
2133
  mountedRef.current = true;
2242
2134
  return () => {
2243
2135
  mountedRef.current = false;
@@ -2277,7 +2169,7 @@ function useBrewStream() {
2277
2169
  }
2278
2170
  const MUTATING_COMMANDS = /* @__PURE__ */ new Set(["install", "uninstall", "upgrade", "pin", "unpin", "tap", "untap"]);
2279
2171
  if (!cancelRef.current && MUTATING_COMMANDS.has(args[0] ?? "")) {
2280
- void import("./snapshot-RAPGMAJF.js").then(({ captureSnapshot: captureSnapshot2, saveSnapshot: saveSnapshot2 }) => {
2172
+ void import("./snapshot-JDRSBMG6.js").then(({ captureSnapshot: captureSnapshot2, saveSnapshot: saveSnapshot2 }) => {
2281
2173
  captureSnapshot2().then((s) => saveSnapshot2(s)).catch((err) => logger.warn("snapshot: capture/save failed", { error: String(err) }));
2282
2174
  });
2283
2175
  }
@@ -2295,78 +2187,84 @@ function useBrewStream() {
2295
2187
  }
2296
2188
 
2297
2189
  // src/components/common/search-input.tsx
2298
- import { Box as Box9, Text as Text11 } from "ink";
2190
+ import { Box as Box10, Text as Text12 } from "ink";
2299
2191
  import { TextInput } from "@inkjs/ui";
2300
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2192
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2301
2193
  function SearchInput({ defaultValue, onChange, placeholder, isActive = true }) {
2302
2194
  const resolvedPlaceholder = placeholder ?? t("searchInput_placeholder");
2303
- return /* @__PURE__ */ jsxs11(Box9, { children: [
2304
- /* @__PURE__ */ jsxs11(Text11, { color: COLORS.gold, children: [
2195
+ return /* @__PURE__ */ jsxs12(Box10, { children: [
2196
+ /* @__PURE__ */ jsxs12(Text12, { color: COLORS.gold, children: [
2305
2197
  "\u{1F50D}",
2306
2198
  " "
2307
2199
  ] }),
2308
- isActive ? /* @__PURE__ */ jsx11(
2200
+ isActive ? /* @__PURE__ */ jsx12(
2309
2201
  TextInput,
2310
2202
  {
2311
2203
  placeholder: resolvedPlaceholder,
2312
2204
  defaultValue,
2313
2205
  onChange
2314
2206
  }
2315
- ) : /* @__PURE__ */ jsx11(Text11, { color: COLORS.textSecondary, children: defaultValue || placeholder })
2207
+ ) : /* @__PURE__ */ jsx12(Text12, { color: COLORS.textSecondary, children: defaultValue || placeholder })
2316
2208
  ] });
2317
2209
  }
2318
2210
 
2319
2211
  // src/components/common/confirm-dialog.tsx
2320
- import { useEffect as useEffect4 } from "react";
2321
- import { Box as Box10, Text as Text12, useInput as useInput2 } from "ink";
2322
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2212
+ import { useEffect as useEffect5 } from "react";
2213
+ import { Box as Box11, Text as Text13, useInput as useInput4 } from "ink";
2214
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2323
2215
  function ConfirmDialog({ message, onConfirm, onCancel }) {
2324
2216
  const locale = useLocaleStore((s) => s.locale);
2325
2217
  const { openModal, closeModal } = useModalStore();
2326
- useEffect4(() => {
2218
+ useEffect5(() => {
2327
2219
  openModal();
2328
2220
  return () => {
2329
2221
  closeModal();
2330
2222
  };
2331
2223
  }, []);
2332
- useInput2((input, key) => {
2224
+ useInput4((input, key) => {
2333
2225
  if (input === "y" || input === "Y") onConfirm();
2334
2226
  else if (locale === "es" && (input === "s" || input === "S")) onConfirm();
2335
2227
  else if (input === "n" || input === "N") onCancel();
2336
2228
  else if (key.escape) onCancel();
2337
2229
  });
2338
- return /* @__PURE__ */ jsxs12(Box10, { borderStyle: "double", borderColor: COLORS.purple, paddingX: 2, paddingY: 1, flexDirection: "column", children: [
2339
- /* @__PURE__ */ jsx12(Text12, { bold: true, color: COLORS.text, children: message }),
2340
- /* @__PURE__ */ jsxs12(Box10, { marginTop: 1, children: [
2341
- /* @__PURE__ */ jsx12(Text12, { color: COLORS.success, children: t("confirm_yes") }),
2342
- /* @__PURE__ */ jsx12(Text12, { children: " / " }),
2343
- /* @__PURE__ */ jsx12(Text12, { color: COLORS.error, children: t("confirm_no") })
2230
+ return /* @__PURE__ */ jsxs13(Box11, { borderStyle: "double", borderColor: COLORS.purple, paddingX: SPACING.sm, paddingY: SPACING.xs, flexDirection: "column", children: [
2231
+ /* @__PURE__ */ jsx13(Text13, { bold: true, color: COLORS.text, children: message }),
2232
+ /* @__PURE__ */ jsxs13(Box11, { marginTop: SPACING.xs, children: [
2233
+ /* @__PURE__ */ jsx13(Text13, { color: COLORS.success, children: t("confirm_yes") }),
2234
+ /* @__PURE__ */ jsx13(Text13, { children: " / " }),
2235
+ /* @__PURE__ */ jsx13(Text13, { color: COLORS.error, children: t("confirm_no") })
2344
2236
  ] })
2345
2237
  ] });
2346
2238
  }
2347
2239
 
2348
2240
  // src/components/common/progress-log.tsx
2349
- import { Box as Box11, Text as Text13 } from "ink";
2241
+ import { Box as Box12, Text as Text14 } from "ink";
2350
2242
  import { Spinner as Spinner2 } from "@inkjs/ui";
2351
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2243
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2352
2244
  function ProgressLog({ lines, isRunning, title, maxVisible = 15 }) {
2353
- const visible = lines.slice(-maxVisible);
2354
- return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.sky, paddingX: 1, children: [
2355
- title && /* @__PURE__ */ jsxs13(Box11, { marginBottom: 1, children: [
2356
- isRunning && /* @__PURE__ */ jsx13(Spinner2, { label: "" }),
2357
- /* @__PURE__ */ jsxs13(Text13, { bold: true, color: COLORS.sky, children: [
2245
+ const start = Math.max(0, lines.length - maxVisible);
2246
+ const visible = lines.slice(start);
2247
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", borderStyle: "round", borderColor: COLORS.sky, paddingX: SPACING.xs, children: [
2248
+ title && /* @__PURE__ */ jsxs14(Box12, { marginBottom: SPACING.xs, children: [
2249
+ isRunning && /* @__PURE__ */ jsx14(Spinner2, { label: "" }),
2250
+ /* @__PURE__ */ jsxs14(Text14, { bold: true, color: COLORS.sky, children: [
2358
2251
  " ",
2359
2252
  title
2360
2253
  ] })
2361
2254
  ] }),
2362
- visible.map((line, i) => /* @__PURE__ */ jsx13(Text13, { color: COLORS.muted, wrap: "wrap", children: line }, lines.length - visible.length + i)),
2363
- lines.length === 0 && !isRunning && /* @__PURE__ */ jsx13(Text13, { color: COLORS.textSecondary, italic: true, children: t("progress_noOutput") })
2255
+ visible.map((line, i) => (
2256
+ // UI-006: keys are absolute indices, so a line that scrolls off-screen
2257
+ // does not change the key of remaining lines. Stable identity prevents
2258
+ // React from treating the whole list as new on every append.
2259
+ /* @__PURE__ */ jsx14(Text14, { color: COLORS.muted, wrap: "wrap", children: line }, `log-${start + i}`)
2260
+ )),
2261
+ lines.length === 0 && !isRunning && /* @__PURE__ */ jsx14(Text14, { color: COLORS.textSecondary, italic: true, children: t("progress_noOutput") })
2364
2262
  ] });
2365
2263
  }
2366
2264
 
2367
2265
  // src/components/common/result-banner.tsx
2368
- import { Box as Box12, Text as Text14 } from "ink";
2369
- import { jsx as jsx14 } from "react/jsx-runtime";
2266
+ import { Box as Box13, Text as Text15 } from "ink";
2267
+ import { jsx as jsx15 } from "react/jsx-runtime";
2370
2268
  var STATUS_COLORS = {
2371
2269
  success: COLORS.success,
2372
2270
  error: COLORS.error,
@@ -2374,21 +2272,21 @@ var STATUS_COLORS = {
2374
2272
  info: COLORS.info
2375
2273
  };
2376
2274
  function ResultBanner({ status, message }) {
2377
- return /* @__PURE__ */ jsx14(Box12, { borderStyle: "round", borderColor: STATUS_COLORS[status], paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx14(Text14, { color: STATUS_COLORS[status], bold: true, children: message }) });
2275
+ return /* @__PURE__ */ jsx15(Box13, { borderStyle: "round", borderColor: STATUS_COLORS[status], paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx15(Text15, { color: STATUS_COLORS[status], bold: true, children: message }) });
2378
2276
  }
2379
2277
 
2380
2278
  // src/components/common/selectable-row.tsx
2381
- import { Box as Box13, Text as Text15 } from "ink";
2382
- import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
2279
+ import { Box as Box14, Text as Text16 } from "ink";
2280
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
2383
2281
  function SelectableRow({ isCurrent, children, gap = 1 }) {
2384
- return /* @__PURE__ */ jsxs14(Box13, { gap, children: [
2385
- /* @__PURE__ */ jsx15(Text15, { color: isCurrent ? COLORS.success : COLORS.muted, children: isCurrent ? "\u25B6" : " " }),
2282
+ return /* @__PURE__ */ jsxs15(Box14, { gap, children: [
2283
+ /* @__PURE__ */ jsx16(Text16, { color: isCurrent ? COLORS.success : COLORS.muted, children: isCurrent ? "\u25B6" : " " }),
2386
2284
  children
2387
2285
  ] });
2388
2286
  }
2389
2287
 
2390
2288
  // src/views/installed.tsx
2391
- import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
2289
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
2392
2290
  function InstalledView() {
2393
2291
  const formulae = useBrewStore((s) => s.formulae);
2394
2292
  const casks = useBrewStore((s) => s.casks);
@@ -2409,10 +2307,10 @@ function InstalledView() {
2409
2307
  const cols = stdout?.columns ?? 80;
2410
2308
  const nameWidth = Math.floor(cols * 0.35);
2411
2309
  const versionWidth = Math.floor(cols * 0.15);
2412
- useEffect5(() => {
2310
+ useEffect6(() => {
2413
2311
  fetchInstalled();
2414
2312
  }, []);
2415
- useEffect5(() => {
2313
+ useEffect6(() => {
2416
2314
  if (isSearching) {
2417
2315
  openModal();
2418
2316
  return () => {
@@ -2429,9 +2327,13 @@ function InstalledView() {
2429
2327
  (p) => p.name.toLowerCase().includes(lower) || p.desc.toLowerCase().includes(lower)
2430
2328
  );
2431
2329
  }, [formulae, casks, tab, debouncedFilter]);
2432
- useInput3((input, key) => {
2433
- if (confirmUninstall || stream.isRunning) return;
2434
- if (!stream.isRunning && stream.lines.length > 0) {
2330
+ useInput5((input, key) => {
2331
+ if (confirmUninstall) return;
2332
+ if (stream.isRunning) {
2333
+ if (key.escape) stream.cancel();
2334
+ return;
2335
+ }
2336
+ if (stream.lines.length > 0) {
2435
2337
  if (key.escape) {
2436
2338
  stream.clear();
2437
2339
  void fetchInstalled();
@@ -2469,11 +2371,11 @@ function InstalledView() {
2469
2371
  setCursor(0);
2470
2372
  }
2471
2373
  }, { isActive: true });
2472
- if (loading.installed) return /* @__PURE__ */ jsx16(Loading, { message: t("loading_installed") });
2473
- if (errors.installed) return /* @__PURE__ */ jsx16(ErrorMessage, { message: errors.installed });
2374
+ if (loading.installed) return /* @__PURE__ */ jsx17(Loading, { message: t("loading_installed") });
2375
+ if (errors.installed) return /* @__PURE__ */ jsx17(ErrorMessage, { message: errors.installed });
2474
2376
  if (stream.isRunning || stream.lines.length > 0) {
2475
- return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
2476
- /* @__PURE__ */ jsx16(
2377
+ return /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2378
+ /* @__PURE__ */ jsx17(
2477
2379
  ProgressLog,
2478
2380
  {
2479
2381
  lines: stream.lines,
@@ -2481,19 +2383,19 @@ function InstalledView() {
2481
2383
  title: t("pkgInfo_uninstalling", { name: "..." })
2482
2384
  }
2483
2385
  ),
2484
- stream.isRunning && /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, children: [
2386
+ stream.isRunning && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, children: [
2485
2387
  "esc:",
2486
2388
  t("hint_cancel")
2487
2389
  ] }),
2488
- !stream.isRunning && /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", marginTop: 1, children: [
2489
- /* @__PURE__ */ jsx16(
2390
+ !stream.isRunning && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginTop: SPACING.xs, children: [
2391
+ /* @__PURE__ */ jsx17(
2490
2392
  ResultBanner,
2491
2393
  {
2492
2394
  status: stream.error ? "error" : "success",
2493
2395
  message: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}`
2494
2396
  }
2495
2397
  ),
2496
- /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, children: [
2398
+ /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, children: [
2497
2399
  "esc:",
2498
2400
  t("hint_back")
2499
2401
  ] })
@@ -2503,28 +2405,28 @@ function InstalledView() {
2503
2405
  const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
2504
2406
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
2505
2407
  const visible = allItems.slice(start, start + MAX_VISIBLE_ROWS);
2506
- return /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
2507
- /* @__PURE__ */ jsxs15(Box14, { marginBottom: 1, gap: 1, children: [
2508
- /* @__PURE__ */ jsx16(
2509
- Box14,
2408
+ return /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2409
+ /* @__PURE__ */ jsxs16(Box15, { marginBottom: SPACING.xs, gap: SPACING.xs, children: [
2410
+ /* @__PURE__ */ jsx17(
2411
+ Box15,
2510
2412
  {
2511
2413
  borderStyle: "round",
2512
2414
  borderColor: tab === "formulae" ? COLORS.info : COLORS.textSecondary,
2513
- paddingX: 1,
2514
- children: /* @__PURE__ */ jsxs15(Text16, { bold: tab === "formulae", color: tab === "formulae" ? COLORS.info : COLORS.textSecondary, children: [
2415
+ paddingX: SPACING.xs,
2416
+ children: /* @__PURE__ */ jsxs16(Text17, { bold: tab === "formulae", color: tab === "formulae" ? COLORS.info : COLORS.textSecondary, children: [
2515
2417
  "\u{1F4E6}",
2516
2418
  " ",
2517
2419
  t("installed_formulaeCount", { count: formulae.length })
2518
2420
  ] })
2519
2421
  }
2520
2422
  ),
2521
- /* @__PURE__ */ jsx16(
2522
- Box14,
2423
+ /* @__PURE__ */ jsx17(
2424
+ Box15,
2523
2425
  {
2524
2426
  borderStyle: "round",
2525
2427
  borderColor: tab === "casks" ? COLORS.purple : COLORS.textSecondary,
2526
- paddingX: 1,
2527
- children: /* @__PURE__ */ jsxs15(Text16, { bold: tab === "casks", color: tab === "casks" ? COLORS.purple : COLORS.textSecondary, children: [
2428
+ paddingX: SPACING.xs,
2429
+ children: /* @__PURE__ */ jsxs16(Text17, { bold: tab === "casks", color: tab === "casks" ? COLORS.purple : COLORS.textSecondary, children: [
2528
2430
  "\u{1F37A}",
2529
2431
  " ",
2530
2432
  t("installed_casksCount", { count: casks.length })
@@ -2532,7 +2434,7 @@ function InstalledView() {
2532
2434
  }
2533
2435
  )
2534
2436
  ] }),
2535
- confirmUninstall && /* @__PURE__ */ jsx16(
2437
+ confirmUninstall && /* @__PURE__ */ jsx17(
2536
2438
  ConfirmDialog,
2537
2439
  {
2538
2440
  message: t("installed_confirmUninstall", { name: confirmUninstall }),
@@ -2546,48 +2448,48 @@ function InstalledView() {
2546
2448
  onCancel: () => setConfirmUninstall(null)
2547
2449
  }
2548
2450
  ),
2549
- isSearching && /* @__PURE__ */ jsx16(Box14, { marginBottom: 1, borderStyle: "round", borderColor: COLORS.gold, paddingX: 1, children: /* @__PURE__ */ jsx16(SearchInput, { defaultValue: filter, onChange: setFilter, isActive: isSearching }) }),
2550
- /* @__PURE__ */ jsxs15(Box14, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, children: [
2551
- /* @__PURE__ */ jsxs15(Text16, { color: COLORS.text, bold: true, children: [
2451
+ isSearching && /* @__PURE__ */ jsx17(Box15, { marginBottom: SPACING.xs, borderStyle: "round", borderColor: COLORS.gold, paddingX: SPACING.xs, children: /* @__PURE__ */ jsx17(SearchInput, { defaultValue: filter, onChange: setFilter, isActive: isSearching }) }),
2452
+ /* @__PURE__ */ jsxs16(Box15, { gap: SPACING.xs, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, children: [
2453
+ /* @__PURE__ */ jsxs16(Text17, { color: COLORS.text, bold: true, children: [
2552
2454
  " ",
2553
2455
  t("installed_col_package").padEnd(nameWidth)
2554
2456
  ] }),
2555
- /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: t("installed_col_version").padEnd(versionWidth) }),
2556
- /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: t("installed_col_status") })
2457
+ /* @__PURE__ */ jsx17(Text17, { color: COLORS.text, bold: true, children: t("installed_col_version").padEnd(versionWidth) }),
2458
+ /* @__PURE__ */ jsx17(Text17, { color: COLORS.text, bold: true, children: t("installed_col_status") })
2557
2459
  ] }),
2558
- /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", children: [
2559
- visible.length === 0 && /* @__PURE__ */ jsx16(Box14, { paddingY: 1, justifyContent: "center", children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.textSecondary, italic: true, children: t("installed_noPackages") }) }),
2560
- start > 0 && /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, dimColor: true, children: [
2460
+ /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2461
+ visible.length === 0 && /* @__PURE__ */ jsx17(Box15, { paddingY: SPACING.xs, justifyContent: "center", children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.textSecondary, italic: true, children: t("installed_noPackages") }) }),
2462
+ start > 0 && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, dimColor: true, children: [
2561
2463
  " ",
2562
2464
  t("scroll_moreAbove", { count: start })
2563
2465
  ] }),
2564
2466
  visible.map((item, i) => {
2565
2467
  const idx = start + i;
2566
2468
  const isCurrent = idx === cursor;
2567
- return /* @__PURE__ */ jsxs15(SelectableRow, { isCurrent, children: [
2568
- /* @__PURE__ */ jsx16(Text16, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: truncate(item.name, nameWidth).padEnd(nameWidth) }),
2569
- /* @__PURE__ */ jsx16(Text16, { color: COLORS.teal, children: item.version.padEnd(versionWidth) }),
2570
- item.outdated && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
2571
- item.pinned && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
2572
- item.kegOnly && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
2573
- item.installedAsDependency && /* @__PURE__ */ jsx16(StatusBadge, { label: t("badge_dep"), variant: "muted" }),
2574
- !item.outdated && !item.pinned && !item.kegOnly && !item.installedAsDependency && /* @__PURE__ */ jsx16(Text16, { color: COLORS.textSecondary, dimColor: true, children: truncate(item.desc, 30) })
2469
+ return /* @__PURE__ */ jsxs16(SelectableRow, { isCurrent, children: [
2470
+ /* @__PURE__ */ jsx17(Text17, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: truncate(item.name, nameWidth).padEnd(nameWidth) }),
2471
+ /* @__PURE__ */ jsx17(Text17, { color: COLORS.teal, children: item.version.padEnd(versionWidth) }),
2472
+ item.outdated && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
2473
+ item.pinned && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
2474
+ item.kegOnly && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
2475
+ item.installedAsDependency && /* @__PURE__ */ jsx17(StatusBadge, { label: t("badge_dep"), variant: "muted" }),
2476
+ !item.outdated && !item.pinned && !item.kegOnly && !item.installedAsDependency && /* @__PURE__ */ jsx17(Text17, { color: COLORS.textSecondary, dimColor: true, children: truncate(item.desc, 30) })
2575
2477
  ] }, item.name);
2576
2478
  }),
2577
- start + MAX_VISIBLE_ROWS < allItems.length && /* @__PURE__ */ jsxs15(Text16, { color: COLORS.textSecondary, dimColor: true, children: [
2479
+ start + MAX_VISIBLE_ROWS < allItems.length && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, dimColor: true, children: [
2578
2480
  " ",
2579
2481
  t("scroll_moreBelow", { count: allItems.length - start - MAX_VISIBLE_ROWS })
2580
2482
  ] })
2581
2483
  ] }),
2582
- /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { color: COLORS.text, bold: true, children: allItems.length > 0 ? `${cursor + 1}/${allItems.length}` : "0/0" }) })
2484
+ /* @__PURE__ */ jsx17(Box15, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.text, bold: true, children: allItems.length > 0 ? `${cursor + 1}/${allItems.length}` : "0/0" }) })
2583
2485
  ] });
2584
2486
  }
2585
2487
 
2586
2488
  // src/views/search.tsx
2587
- import { useState as useState4, useCallback as useCallback2, useEffect as useEffect6, useRef as useRef2 } from "react";
2588
- import { Box as Box15, Text as Text17, useInput as useInput4 } from "ink";
2489
+ import { useState as useState4, useCallback as useCallback2, useEffect as useEffect7, useRef as useRef2 } from "react";
2490
+ import { Box as Box16, Text as Text18, useInput as useInput6 } from "ink";
2589
2491
  import { TextInput as TextInput2 } from "@inkjs/ui";
2590
- import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
2492
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
2591
2493
  function SearchView() {
2592
2494
  const [query, setQuery] = useState4("");
2593
2495
  const [results, setResults] = useState4(null);
@@ -2601,7 +2503,7 @@ function SearchView() {
2601
2503
  const selectPackage = useNavigationStore((s) => s.selectPackage);
2602
2504
  const fetchInstalled = useBrewStore((s) => s.fetchInstalled);
2603
2505
  const hasRefreshed = useRef2(false);
2604
- useEffect6(() => {
2506
+ useEffect7(() => {
2605
2507
  if (results !== null) {
2606
2508
  openModal();
2607
2509
  return () => {
@@ -2629,7 +2531,7 @@ function SearchView() {
2629
2531
  setSearching(false);
2630
2532
  }
2631
2533
  }, []);
2632
- useEffect6(() => {
2534
+ useEffect7(() => {
2633
2535
  if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current) {
2634
2536
  hasRefreshed.current = true;
2635
2537
  void fetchInstalled();
@@ -2639,7 +2541,7 @@ function SearchView() {
2639
2541
  const visibleFormulae = results ? results.formulae.slice(0, MAX_VISIBLE) : [];
2640
2542
  const visibleCasks = results ? results.casks.slice(0, MAX_VISIBLE) : [];
2641
2543
  const allVisible = [...visibleFormulae, ...visibleCasks];
2642
- useInput4((input, key) => {
2544
+ useInput6((input, key) => {
2643
2545
  if (stream.isRunning) {
2644
2546
  if (key.escape) stream.cancel();
2645
2547
  return;
@@ -2674,8 +2576,8 @@ function SearchView() {
2674
2576
  }
2675
2577
  });
2676
2578
  if (stream.isRunning || stream.lines.length > 0) {
2677
- return /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2678
- /* @__PURE__ */ jsx17(
2579
+ return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2580
+ /* @__PURE__ */ jsx18(
2679
2581
  ProgressLog,
2680
2582
  {
2681
2583
  lines: stream.lines,
@@ -2683,32 +2585,32 @@ function SearchView() {
2683
2585
  title: t("search_installing")
2684
2586
  }
2685
2587
  ),
2686
- stream.isRunning && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, children: [
2588
+ stream.isRunning && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, children: [
2687
2589
  "esc:",
2688
2590
  t("hint_cancel")
2689
2591
  ] }),
2690
- !stream.isRunning && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginTop: 1, children: [
2691
- /* @__PURE__ */ jsx17(
2592
+ !stream.isRunning && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: SPACING.xs, children: [
2593
+ /* @__PURE__ */ jsx18(
2692
2594
  ResultBanner,
2693
2595
  {
2694
2596
  status: stream.error ? "error" : "success",
2695
2597
  message: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("search_installComplete")}`
2696
2598
  }
2697
2599
  ),
2698
- /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, children: [
2600
+ /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, children: [
2699
2601
  "esc:",
2700
2602
  t("hint_clear")
2701
2603
  ] })
2702
2604
  ] })
2703
2605
  ] });
2704
2606
  }
2705
- return /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2706
- /* @__PURE__ */ jsxs16(Box15, { marginBottom: 1, children: [
2707
- /* @__PURE__ */ jsxs16(Text17, { color: COLORS.gold, children: [
2607
+ return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2608
+ /* @__PURE__ */ jsxs17(Box16, { marginBottom: SPACING.xs, children: [
2609
+ /* @__PURE__ */ jsxs17(Text18, { color: COLORS.gold, children: [
2708
2610
  "\u{1F50D}",
2709
2611
  " "
2710
2612
  ] }),
2711
- !results ? /* @__PURE__ */ jsx17(
2613
+ !results ? /* @__PURE__ */ jsx18(
2712
2614
  TextInput2,
2713
2615
  {
2714
2616
  placeholder: t("search_placeholder"),
@@ -2716,17 +2618,17 @@ function SearchView() {
2716
2618
  onChange: setQuery,
2717
2619
  onSubmit: () => void doSearch(query)
2718
2620
  }
2719
- ) : /* @__PURE__ */ jsxs16(Text17, { children: [
2621
+ ) : /* @__PURE__ */ jsxs17(Text18, { children: [
2720
2622
  t("search_resultsFor"),
2721
2623
  ' "',
2722
- /* @__PURE__ */ jsx17(Text17, { bold: true, color: COLORS.text, children: query }),
2624
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: COLORS.text, children: query }),
2723
2625
  '" ',
2724
- /* @__PURE__ */ jsx17(Text17, { color: COLORS.textSecondary, children: t("search_escToClear") })
2626
+ /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: t("search_escToClear") })
2725
2627
  ] })
2726
2628
  ] }),
2727
- searching && /* @__PURE__ */ jsx17(Loading, { message: t("loading_searching") }),
2728
- searchError && /* @__PURE__ */ jsx17(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.error, children: searchError }) }),
2729
- confirmInstall && /* @__PURE__ */ jsx17(
2629
+ searching && /* @__PURE__ */ jsx18(Loading, { message: t("loading_searching") }),
2630
+ searchError && /* @__PURE__ */ jsx18(Box16, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.error, children: searchError }) }),
2631
+ confirmInstall && /* @__PURE__ */ jsx18(
2730
2632
  ConfirmDialog,
2731
2633
  {
2732
2634
  message: t("search_confirmInstall", { name: confirmInstall }),
@@ -2739,59 +2641,59 @@ function SearchView() {
2739
2641
  onCancel: () => setConfirmInstall(null)
2740
2642
  }
2741
2643
  ),
2742
- results && !searching && !confirmInstall && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2743
- visibleFormulae.length > 0 && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", marginBottom: 1, children: [
2744
- /* @__PURE__ */ jsx17(Text17, { bold: true, color: COLORS.info, children: t("search_formulaeHeader", { count: results.formulae.length }) }),
2644
+ results && !searching && !confirmInstall && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2645
+ visibleFormulae.length > 0 && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginBottom: SPACING.xs, children: [
2646
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: COLORS.info, children: t("search_formulaeHeader", { count: results.formulae.length }) }),
2745
2647
  visibleFormulae.map((name, i) => {
2746
2648
  const isCurrent = i === cursor;
2747
- return /* @__PURE__ */ jsx17(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx17(Text17, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
2649
+ return /* @__PURE__ */ jsx18(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx18(Text18, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
2748
2650
  }),
2749
- results.formulae.length > MAX_VISIBLE && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, dimColor: true, children: [
2651
+ results.formulae.length > MAX_VISIBLE && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
2750
2652
  " ",
2751
2653
  t("scroll_moreBelow", { count: results.formulae.length - MAX_VISIBLE })
2752
2654
  ] })
2753
2655
  ] }),
2754
- visibleCasks.length > 0 && /* @__PURE__ */ jsxs16(Box15, { flexDirection: "column", children: [
2755
- /* @__PURE__ */ jsx17(Text17, { bold: true, color: COLORS.purple, children: t("search_casksHeader", { count: results.casks.length }) }),
2656
+ visibleCasks.length > 0 && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2657
+ /* @__PURE__ */ jsx18(Text18, { bold: true, color: COLORS.purple, children: t("search_casksHeader", { count: results.casks.length }) }),
2756
2658
  visibleCasks.map((name, i) => {
2757
2659
  const idx = visibleFormulae.length + i;
2758
2660
  const isCurrent = idx === cursor;
2759
- return /* @__PURE__ */ jsx17(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx17(Text17, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
2661
+ return /* @__PURE__ */ jsx18(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx18(Text18, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
2760
2662
  }),
2761
- results.casks.length > MAX_VISIBLE && /* @__PURE__ */ jsxs16(Text17, { color: COLORS.textSecondary, dimColor: true, children: [
2663
+ results.casks.length > MAX_VISIBLE && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
2762
2664
  " ",
2763
2665
  t("scroll_moreBelow", { count: results.casks.length - MAX_VISIBLE })
2764
2666
  ] })
2765
2667
  ] }),
2766
- allVisible.length === 0 && /* @__PURE__ */ jsx17(Box15, { borderStyle: "round", borderColor: COLORS.textSecondary, paddingX: 2, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.textSecondary, italic: true, children: t("search_noResults") }) }),
2767
- /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text17, { color: COLORS.text, bold: true, children: allVisible.length > 0 ? `${cursor + 1}/${allVisible.length}` : "" }) })
2668
+ allVisible.length === 0 && /* @__PURE__ */ jsx18(Box16, { borderStyle: "round", borderColor: COLORS.textSecondary, paddingX: SPACING.sm, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, italic: true, children: t("search_noResults") }) }),
2669
+ /* @__PURE__ */ jsx18(Box16, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.text, bold: true, children: allVisible.length > 0 ? `${cursor + 1}/${allVisible.length}` : "" }) })
2768
2670
  ] })
2769
2671
  ] });
2770
2672
  }
2771
2673
 
2772
2674
  // src/views/outdated.tsx
2773
- import { useEffect as useEffect7, useRef as useRef3, useState as useState5 } from "react";
2774
- import { Box as Box16, Text as Text18, useInput as useInput5, useStdout as useStdout5 } from "ink";
2775
- import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
2675
+ import { useEffect as useEffect8, useMemo as useMemo4, useRef as useRef3, useState as useState5 } from "react";
2676
+ import { Box as Box17, Text as Text19, useInput as useInput7, useStdout as useStdout5 } from "ink";
2677
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
2776
2678
  function ImpactPanel({ impact }) {
2777
2679
  const riskColor = impact.risk === "high" ? COLORS.error : impact.risk === "medium" ? COLORS.warning : COLORS.success;
2778
2680
  const riskLabel = impact.risk === "high" ? t("impact_high") : impact.risk === "medium" ? t("impact_medium") : t("impact_low");
2779
2681
  const riskIcon = impact.risk === "high" ? "\u26A0" : impact.risk === "medium" ? "~" : "\u2713";
2780
- return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: riskColor, paddingX: 2, paddingY: 0, children: [
2781
- /* @__PURE__ */ jsxs17(Box16, { children: [
2782
- /* @__PURE__ */ jsxs17(Text18, { bold: true, color: riskColor, children: [
2682
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", marginTop: SPACING.xs, borderStyle: "round", borderColor: riskColor, paddingX: SPACING.sm, paddingY: SPACING.none, children: [
2683
+ /* @__PURE__ */ jsxs18(Box17, { children: [
2684
+ /* @__PURE__ */ jsxs18(Text19, { bold: true, color: riskColor, children: [
2783
2685
  riskIcon,
2784
2686
  " ",
2785
2687
  riskLabel
2786
2688
  ] }),
2787
- impact.reverseDeps.length > 0 && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, children: [
2689
+ impact.reverseDeps.length > 0 && /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
2788
2690
  " \\u2014 ",
2789
2691
  t("impact_affects", { count: impact.reverseDeps.length })
2790
2692
  ] })
2791
2693
  ] }),
2792
- impact.riskReasons.length > 0 && /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: impact.riskReasons.join(" \xB7 ") }),
2793
- impact.reverseDeps.length > 0 && impact.reverseDeps.length <= 5 && /* @__PURE__ */ jsx18(Text18, { color: COLORS.muted, dimColor: true, children: t("impact_usedBy", { packages: impact.reverseDeps.join(", ") }) }),
2794
- impact.risk === "high" && /* @__PURE__ */ jsx18(Text18, { color: COLORS.info, children: t("impact_brewfile_hint") })
2694
+ impact.riskReasons.length > 0 && /* @__PURE__ */ jsx19(Text19, { color: COLORS.textSecondary, children: impact.riskReasons.join(" \xB7 ") }),
2695
+ impact.reverseDeps.length > 0 && impact.reverseDeps.length <= 5 && /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, dimColor: true, children: t("impact_usedBy", { packages: impact.reverseDeps.join(", ") }) }),
2696
+ impact.risk === "high" && /* @__PURE__ */ jsx19(Text19, { color: COLORS.info, children: t("impact_brewfile_hint") })
2795
2697
  ] });
2796
2698
  }
2797
2699
  function OutdatedView() {
@@ -2802,35 +2704,48 @@ function OutdatedView() {
2802
2704
  const hasRefreshed = useRef3(false);
2803
2705
  const [impact, setImpact] = useState5(null);
2804
2706
  const [impactLoading, setImpactLoading] = useState5(false);
2805
- useEffect7(() => {
2707
+ useEffect8(() => {
2806
2708
  fetchOutdated();
2807
2709
  }, []);
2808
- useEffect7(() => {
2710
+ useEffect8(() => {
2809
2711
  if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current) {
2810
2712
  hasRefreshed.current = true;
2811
2713
  void fetchOutdated();
2812
2714
  }
2813
2715
  }, [stream.isRunning, stream.error]);
2814
- const allOutdated = [
2815
- ...outdated.formulae.map((p) => ({ ...p, type: "formula" })),
2816
- ...outdated.casks.map((p) => ({ ...p, type: "cask" }))
2817
- ];
2716
+ const allOutdated = useMemo4(
2717
+ () => [
2718
+ ...outdated.formulae.map((p) => ({ ...p, type: "formula" })),
2719
+ ...outdated.casks.map((p) => ({ ...p, type: "cask" }))
2720
+ ],
2721
+ [outdated.formulae, outdated.casks]
2722
+ );
2818
2723
  const debouncedCursor = useDebounce(cursor, 150);
2819
- useEffect7(() => {
2724
+ useEffect8(() => {
2820
2725
  const pkg = allOutdated[debouncedCursor];
2821
2726
  if (!pkg || stream.isRunning) {
2822
2727
  setImpact(null);
2823
2728
  return;
2824
2729
  }
2730
+ let cancelled = false;
2825
2731
  setImpactLoading(true);
2826
2732
  void getUpgradeImpact(
2827
2733
  pkg.name,
2828
2734
  pkg.installed_versions[0] ?? "",
2829
2735
  pkg.current_version,
2830
2736
  pkg.type
2831
- ).then(setImpact).catch(() => setImpact(null)).finally(() => setImpactLoading(false));
2737
+ ).then((result) => {
2738
+ if (!cancelled) setImpact(result);
2739
+ }).catch(() => {
2740
+ if (!cancelled) setImpact(null);
2741
+ }).finally(() => {
2742
+ if (!cancelled) setImpactLoading(false);
2743
+ });
2744
+ return () => {
2745
+ cancelled = true;
2746
+ };
2832
2747
  }, [debouncedCursor, stream.isRunning]);
2833
- useInput5((input, key) => {
2748
+ useInput7((input, key) => {
2834
2749
  if (stream.isRunning) {
2835
2750
  if (key.escape) stream.cancel();
2836
2751
  return;
@@ -2867,11 +2782,11 @@ function OutdatedView() {
2867
2782
  const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
2868
2783
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
2869
2784
  const visible = allOutdated.slice(start, start + MAX_VISIBLE_ROWS);
2870
- if (loading.outdated) return /* @__PURE__ */ jsx18(Loading, { message: t("loading_outdated") });
2871
- if (errors.outdated) return /* @__PURE__ */ jsx18(ErrorMessage, { message: errors.outdated });
2785
+ if (loading.outdated) return /* @__PURE__ */ jsx19(Loading, { message: t("loading_outdated") });
2786
+ if (errors.outdated) return /* @__PURE__ */ jsx19(ErrorMessage, { message: errors.outdated });
2872
2787
  if (stream.isRunning || stream.lines.length > 0) {
2873
- return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2874
- /* @__PURE__ */ jsx18(
2788
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
2789
+ /* @__PURE__ */ jsx19(
2875
2790
  ProgressLog,
2876
2791
  {
2877
2792
  lines: stream.lines,
@@ -2879,19 +2794,19 @@ function OutdatedView() {
2879
2794
  title: t("outdated_upgrading")
2880
2795
  }
2881
2796
  ),
2882
- stream.isRunning && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, children: [
2797
+ stream.isRunning && /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
2883
2798
  "esc:",
2884
2799
  t("hint_cancel")
2885
2800
  ] }),
2886
- !stream.isRunning && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: 1, children: [
2887
- /* @__PURE__ */ jsxs17(Box16, { borderStyle: "round", borderColor: stream.error ? COLORS.error : COLORS.success, paddingX: 2, paddingY: 0, children: [
2888
- /* @__PURE__ */ jsx18(Text18, { color: stream.error ? COLORS.error : COLORS.success, bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("outdated_upgradeComplete")}` }),
2889
- /* @__PURE__ */ jsxs17(Text18, { color: COLORS.muted, children: [
2801
+ !stream.isRunning && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", marginTop: SPACING.xs, children: [
2802
+ /* @__PURE__ */ jsxs18(Box17, { borderStyle: "round", borderColor: stream.error ? COLORS.error : COLORS.success, paddingX: SPACING.sm, paddingY: SPACING.none, children: [
2803
+ /* @__PURE__ */ jsx19(Text19, { color: stream.error ? COLORS.error : COLORS.success, bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("outdated_upgradeComplete")}` }),
2804
+ /* @__PURE__ */ jsxs18(Text19, { color: COLORS.muted, children: [
2890
2805
  " ",
2891
2806
  t("outdated_pressRefresh")
2892
2807
  ] })
2893
2808
  ] }),
2894
- /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, children: [
2809
+ /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
2895
2810
  "r:",
2896
2811
  t("hint_refresh"),
2897
2812
  " esc:",
@@ -2902,9 +2817,9 @@ function OutdatedView() {
2902
2817
  }
2903
2818
  const upgradeAllMessage = confirmAction?.type === "all" ? `${t("outdated_confirmAll", { count: allOutdated.length })}
2904
2819
  ${t("outdated_upgradeAllList", { list: allOutdated.map((p) => p.name).join(", ") })}` : "";
2905
- return /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", children: [
2906
- /* @__PURE__ */ jsx18(SectionHeader, { emoji: "\u{1F4E6}", title: t("outdated_title", { count: allOutdated.length }), gradient: GRADIENTS.fire }),
2907
- confirmAction && /* @__PURE__ */ jsx18(Box16, { marginY: 1, children: /* @__PURE__ */ jsx18(
2820
+ return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
2821
+ /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1F4E6}", title: t("outdated_title", { count: allOutdated.length }), gradient: GRADIENTS.fire }),
2822
+ confirmAction && /* @__PURE__ */ jsx19(Box17, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx19(
2908
2823
  ConfirmDialog,
2909
2824
  {
2910
2825
  message: confirmAction.type === "all" ? upgradeAllMessage : t("outdated_confirmSingle", { name: confirmAction.type === "single" ? confirmAction.name : "" }),
@@ -2920,41 +2835,41 @@ ${t("outdated_upgradeAllList", { list: allOutdated.map((p) => p.name).join(", ")
2920
2835
  onCancel: () => setConfirmAction(null)
2921
2836
  }
2922
2837
  ) }),
2923
- allOutdated.length === 0 && !confirmAction && /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(ResultBanner, { status: "success", message: `\u2714 ${t("outdated_upToDate")}` }) }),
2924
- allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs17(Box16, { flexDirection: "column", marginTop: 1, children: [
2925
- start > 0 && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
2838
+ allOutdated.length === 0 && !confirmAction && /* @__PURE__ */ jsx19(Box17, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx19(ResultBanner, { status: "success", message: `\u2714 ${t("outdated_upToDate")}` }) }),
2839
+ allOutdated.length > 0 && !confirmAction && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", marginTop: SPACING.xs, children: [
2840
+ start > 0 && /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, dimColor: true, children: [
2926
2841
  " ",
2927
2842
  t("scroll_moreAbove", { count: start })
2928
2843
  ] }),
2929
2844
  visible.map((pkg, i) => {
2930
2845
  const idx = start + i;
2931
2846
  const isCurrent = idx === cursor;
2932
- return /* @__PURE__ */ jsxs17(SelectableRow, { isCurrent, children: [
2933
- /* @__PURE__ */ jsx18(Text18, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: pkg.name }),
2934
- /* @__PURE__ */ jsx18(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version }),
2935
- pkg.pinned && /* @__PURE__ */ jsx18(StatusBadge, { label: t("outdated_pinned"), variant: "info" })
2847
+ return /* @__PURE__ */ jsxs18(SelectableRow, { isCurrent, children: [
2848
+ /* @__PURE__ */ jsx19(Text19, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: pkg.name }),
2849
+ /* @__PURE__ */ jsx19(VersionArrow, { current: pkg.installed_versions[0] ?? "", latest: pkg.current_version }),
2850
+ pkg.pinned && /* @__PURE__ */ jsx19(StatusBadge, { label: t("outdated_pinned"), variant: "info" })
2936
2851
  ] }, pkg.name);
2937
2852
  }),
2938
- start + MAX_VISIBLE_ROWS < allOutdated.length && /* @__PURE__ */ jsxs17(Text18, { color: COLORS.textSecondary, dimColor: true, children: [
2853
+ start + MAX_VISIBLE_ROWS < allOutdated.length && /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, dimColor: true, children: [
2939
2854
  " ",
2940
2855
  t("scroll_moreBelow", { count: allOutdated.length - start - MAX_VISIBLE_ROWS })
2941
2856
  ] }),
2942
- /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text18, { color: COLORS.text, bold: true, children: [
2857
+ /* @__PURE__ */ jsx19(Box17, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs18(Text19, { color: COLORS.text, bold: true, children: [
2943
2858
  cursor + 1,
2944
2859
  "/",
2945
2860
  allOutdated.length
2946
2861
  ] }) }),
2947
- impact && !stream.isRunning && !confirmAction && /* @__PURE__ */ jsx18(ImpactPanel, { impact }),
2948
- impactLoading && !stream.isRunning && !confirmAction && /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: t("impact_analyzing") }) }),
2949
- /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { color: COLORS.textSecondary, children: t("impact_hint") }) })
2862
+ impact && !stream.isRunning && !confirmAction && /* @__PURE__ */ jsx19(ImpactPanel, { impact }),
2863
+ impactLoading && !stream.isRunning && !confirmAction && /* @__PURE__ */ jsx19(Box17, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx19(Text19, { color: COLORS.textSecondary, children: t("impact_analyzing") }) }),
2864
+ /* @__PURE__ */ jsx19(Box17, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx19(Text19, { color: COLORS.textSecondary, children: t("impact_hint") }) })
2950
2865
  ] })
2951
2866
  ] });
2952
2867
  }
2953
2868
 
2954
2869
  // src/views/package-info.tsx
2955
- import { useEffect as useEffect8, useRef as useRef4, useState as useState6 } from "react";
2956
- import { Box as Box17, Text as Text19, useInput as useInput6 } from "ink";
2957
- import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
2870
+ import { useEffect as useEffect9, useRef as useRef4, useState as useState6 } from "react";
2871
+ import { Box as Box18, Text as Text20, useInput as useInput8 } from "ink";
2872
+ import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
2958
2873
  var ACTION_PROGRESS_KEYS = {
2959
2874
  install: "pkgInfo_installing",
2960
2875
  uninstall: "pkgInfo_uninstalling",
@@ -2976,47 +2891,20 @@ function PackageInfoView() {
2976
2891
  const mountedRef = useRef4(true);
2977
2892
  const hasRefreshed = useRef4(false);
2978
2893
  const stream = useBrewStream();
2979
- useEffect8(() => {
2894
+ useEffect9(() => {
2980
2895
  mountedRef.current = true;
2981
2896
  return () => {
2982
2897
  mountedRef.current = false;
2983
2898
  };
2984
2899
  }, []);
2985
- useEffect8(() => {
2900
+ useEffect9(() => {
2986
2901
  if (!packageName) return;
2987
2902
  setLoading2(true);
2988
2903
  const fetchInfo = async () => {
2989
2904
  if (packageType === "cask") {
2990
2905
  const caskInfo = await getCaskInfo(packageName);
2991
2906
  if (caskInfo && mountedRef.current) {
2992
- const formulaLike = {
2993
- name: caskInfo.token,
2994
- full_name: caskInfo.full_token,
2995
- tap: "",
2996
- desc: caskInfo.desc,
2997
- license: "",
2998
- homepage: caskInfo.homepage,
2999
- versions: { stable: caskInfo.version, head: null, bottle: false },
3000
- dependencies: [],
3001
- build_dependencies: [],
3002
- installed: caskInfo.installed ? [{
3003
- version: caskInfo.installed,
3004
- used_options: [],
3005
- built_as_bottle: false,
3006
- poured_from_bottle: false,
3007
- time: caskInfo.installed_time ?? 0,
3008
- runtime_dependencies: [],
3009
- installed_as_dependency: false,
3010
- installed_on_request: true
3011
- }] : [],
3012
- linked_keg: null,
3013
- pinned: false,
3014
- outdated: caskInfo.outdated,
3015
- deprecated: false,
3016
- keg_only: false,
3017
- caveats: null
3018
- };
3019
- setFormula(formulaLike);
2907
+ setFormula(formulaeFromCask(caskInfo));
3020
2908
  setLoading2(false);
3021
2909
  return;
3022
2910
  }
@@ -3034,7 +2922,7 @@ function PackageInfoView() {
3034
2922
  }
3035
2923
  });
3036
2924
  }, [packageName, packageType]);
3037
- useEffect8(() => {
2925
+ useEffect9(() => {
3038
2926
  if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current && packageName) {
3039
2927
  hasRefreshed.current = true;
3040
2928
  const refreshFn = packageType === "cask" ? getCaskInfo(packageName).then((c) => c ? { ...c, installed: c.installed ? [{ version: c.installed }] : [] } : null) : getFormulaInfo(packageName);
@@ -3046,7 +2934,7 @@ function PackageInfoView() {
3046
2934
  });
3047
2935
  }
3048
2936
  }, [stream.isRunning, stream.error]);
3049
- useInput6((input, key) => {
2937
+ useInput8((input, key) => {
3050
2938
  if (stream.isRunning) {
3051
2939
  if (key.escape) stream.cancel();
3052
2940
  return;
@@ -3063,21 +2951,21 @@ function PackageInfoView() {
3063
2951
  }
3064
2952
  });
3065
2953
  if (!packageName) {
3066
- return /* @__PURE__ */ jsx19(Text19, { color: COLORS.textSecondary, italic: true, children: t("pkgInfo_noPackage") });
2954
+ return /* @__PURE__ */ jsx20(Text20, { color: COLORS.textSecondary, italic: true, children: t("pkgInfo_noPackage") });
3067
2955
  }
3068
- if (loading) return /* @__PURE__ */ jsx19(Loading, { message: t("loading_package", { name: packageName }) });
3069
- if (error) return /* @__PURE__ */ jsx19(ErrorMessage, { message: error });
3070
- if (!formula) return /* @__PURE__ */ jsx19(ErrorMessage, { message: t("pkgInfo_notFound") });
2956
+ if (loading) return /* @__PURE__ */ jsx20(Loading, { message: t("loading_package", { name: packageName }) });
2957
+ if (error) return /* @__PURE__ */ jsx20(ErrorMessage, { message: error });
2958
+ if (!formula) return /* @__PURE__ */ jsx20(ErrorMessage, { message: t("pkgInfo_notFound") });
3071
2959
  if (stream.isRunning || stream.lines.length > 0) {
3072
- return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
3073
- /* @__PURE__ */ jsx19(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t(ACTION_PROGRESS_KEYS[activeActionRef.current] ?? ACTION_PROGRESS_KEYS["install"], { name: formula.name }) }),
3074
- stream.isRunning && /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
2960
+ return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
2961
+ /* @__PURE__ */ jsx20(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t(ACTION_PROGRESS_KEYS[activeActionRef.current] ?? ACTION_PROGRESS_KEYS["install"], { name: formula.name }) }),
2962
+ stream.isRunning && /* @__PURE__ */ jsxs19(Text20, { color: COLORS.textSecondary, children: [
3075
2963
  "esc:",
3076
2964
  t("hint_cancel")
3077
2965
  ] }),
3078
- !stream.isRunning && /* @__PURE__ */ jsxs18(Fragment4, { children: [
3079
- /* @__PURE__ */ jsx19(Text19, { color: stream.error ? COLORS.error : COLORS.success, bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}` }),
3080
- /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
2966
+ !stream.isRunning && /* @__PURE__ */ jsxs19(Fragment4, { children: [
2967
+ /* @__PURE__ */ jsx20(Text20, { color: stream.error ? COLORS.error : COLORS.success, bold: true, children: stream.error ? `\u2718 ${stream.error}` : `\u2714 ${t("pkgInfo_done")}` }),
2968
+ /* @__PURE__ */ jsxs19(Text20, { color: COLORS.textSecondary, children: [
3081
2969
  "esc:",
3082
2970
  t("hint_back")
3083
2971
  ] })
@@ -3086,8 +2974,8 @@ function PackageInfoView() {
3086
2974
  }
3087
2975
  const installed = formula.installed[0];
3088
2976
  const isInstalled = formula.installed.length > 0;
3089
- return /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
3090
- confirmAction && /* @__PURE__ */ jsx19(
2977
+ return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
2978
+ confirmAction && /* @__PURE__ */ jsx20(
3091
2979
  ConfirmDialog,
3092
2980
  {
3093
2981
  message: t(ACTION_CONFIRM_KEYS[confirmAction], { name: formula.name }),
@@ -3103,72 +2991,72 @@ function PackageInfoView() {
3103
2991
  onCancel: () => setConfirmAction(null)
3104
2992
  }
3105
2993
  ),
3106
- /* @__PURE__ */ jsxs18(Box17, { gap: 2, marginBottom: 1, children: [
3107
- /* @__PURE__ */ jsx19(GradientText, { colors: GRADIENTS.gold, bold: true, children: formula.name }),
3108
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.teal, children: installed?.version ?? formula.versions.stable }),
3109
- isInstalled && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_installed"), variant: "success" }),
3110
- formula.outdated && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
3111
- formula.pinned && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
3112
- formula.keg_only && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
3113
- formula.deprecated && /* @__PURE__ */ jsx19(StatusBadge, { label: t("badge_deprecated"), variant: "error" })
2994
+ /* @__PURE__ */ jsxs19(Box18, { gap: SPACING.sm, marginBottom: SPACING.xs, children: [
2995
+ /* @__PURE__ */ jsx20(GradientText, { colors: GRADIENTS.gold, bold: true, children: formula.name }),
2996
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.teal, children: installed?.version ?? formula.versions.stable }),
2997
+ isInstalled && /* @__PURE__ */ jsx20(StatusBadge, { label: t("badge_installed"), variant: "success" }),
2998
+ formula.outdated && /* @__PURE__ */ jsx20(StatusBadge, { label: t("badge_outdated"), variant: "warning" }),
2999
+ formula.pinned && /* @__PURE__ */ jsx20(StatusBadge, { label: t("badge_pinned"), variant: "info" }),
3000
+ formula.keg_only && /* @__PURE__ */ jsx20(StatusBadge, { label: t("badge_kegOnly"), variant: "muted" }),
3001
+ formula.deprecated && /* @__PURE__ */ jsx20(StatusBadge, { label: t("badge_deprecated"), variant: "error" })
3114
3002
  ] }),
3115
- /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", gap: 1, children: [
3116
- /* @__PURE__ */ jsx19(Text19, { children: formula.desc }),
3117
- /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
3118
- /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1F4CB}", title: t("pkgInfo_details"), gradient: [COLORS.text, COLORS.muted] }),
3119
- /* @__PURE__ */ jsxs18(Box17, { borderStyle: "round", borderColor: COLORS.border, paddingX: 2, flexDirection: "column", children: [
3120
- /* @__PURE__ */ jsxs18(Text19, { children: [
3121
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_homepage") }),
3003
+ /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", gap: SPACING.xs, children: [
3004
+ /* @__PURE__ */ jsx20(Text20, { children: formula.desc }),
3005
+ /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
3006
+ /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u{1F4CB}", title: t("pkgInfo_details"), gradient: [COLORS.text, COLORS.muted] }),
3007
+ /* @__PURE__ */ jsxs19(Box18, { borderStyle: "round", borderColor: COLORS.border, paddingX: SPACING.sm, flexDirection: "column", children: [
3008
+ /* @__PURE__ */ jsxs19(Text20, { children: [
3009
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: t("pkgInfo_homepage") }),
3122
3010
  " ",
3123
3011
  formula.homepage
3124
3012
  ] }),
3125
- /* @__PURE__ */ jsxs18(Text19, { children: [
3126
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_license") }),
3013
+ /* @__PURE__ */ jsxs19(Text20, { children: [
3014
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: t("pkgInfo_license") }),
3127
3015
  " ",
3128
3016
  formula.license
3129
3017
  ] }),
3130
- /* @__PURE__ */ jsxs18(Text19, { children: [
3131
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_tap") }),
3018
+ /* @__PURE__ */ jsxs19(Text20, { children: [
3019
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: t("pkgInfo_tap") }),
3132
3020
  " ",
3133
3021
  formula.tap
3134
3022
  ] }),
3135
- /* @__PURE__ */ jsxs18(Text19, { children: [
3136
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_stable") }),
3023
+ /* @__PURE__ */ jsxs19(Text20, { children: [
3024
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: t("pkgInfo_stable") }),
3137
3025
  " ",
3138
3026
  formula.versions.stable
3139
3027
  ] }),
3140
- installed && /* @__PURE__ */ jsxs18(Fragment4, { children: [
3141
- /* @__PURE__ */ jsxs18(Text19, { children: [
3142
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_installed") }),
3028
+ installed && /* @__PURE__ */ jsxs19(Fragment4, { children: [
3029
+ /* @__PURE__ */ jsxs19(Text20, { children: [
3030
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: t("pkgInfo_installed") }),
3143
3031
  " ",
3144
3032
  installed.version,
3145
3033
  " (",
3146
3034
  formatRelativeTime(installed.time),
3147
3035
  ")"
3148
3036
  ] }),
3149
- /* @__PURE__ */ jsxs18(Text19, { children: [
3150
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_bottle") }),
3037
+ /* @__PURE__ */ jsxs19(Text20, { children: [
3038
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: t("pkgInfo_bottle") }),
3151
3039
  " ",
3152
3040
  installed.poured_from_bottle ? t("common_yes") : t("common_no")
3153
3041
  ] }),
3154
- /* @__PURE__ */ jsxs18(Text19, { children: [
3155
- /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: t("pkgInfo_onRequest") }),
3042
+ /* @__PURE__ */ jsxs19(Text20, { children: [
3043
+ /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: t("pkgInfo_onRequest") }),
3156
3044
  " ",
3157
3045
  installed.installed_on_request ? t("common_yes") : t("pkgInfo_noDependency")
3158
3046
  ] })
3159
3047
  ] })
3160
3048
  ] })
3161
3049
  ] }),
3162
- formula.dependencies.length > 0 && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
3163
- /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u{1F517}", title: t("pkgInfo_dependencies", { count: formula.dependencies.length }), gradient: GRADIENTS.ocean }),
3164
- /* @__PURE__ */ jsx19(Box17, { paddingLeft: 2, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx19(Text19, { color: COLORS.muted, children: dep }, dep)) })
3050
+ formula.dependencies.length > 0 && /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
3051
+ /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u{1F517}", title: t("pkgInfo_dependencies", { count: formula.dependencies.length }), gradient: GRADIENTS.ocean }),
3052
+ /* @__PURE__ */ jsx20(Box18, { paddingLeft: SPACING.sm, flexWrap: "wrap", columnGap: 2, children: formula.dependencies.map((dep) => /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: dep }, dep)) })
3165
3053
  ] }),
3166
- formula.caveats && /* @__PURE__ */ jsxs18(Box17, { flexDirection: "column", children: [
3167
- /* @__PURE__ */ jsx19(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("pkgInfo_caveats"), color: COLORS.warning }),
3168
- /* @__PURE__ */ jsx19(Box17, { borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, children: /* @__PURE__ */ jsx19(Text19, { color: COLORS.warning, children: formula.caveats }) })
3054
+ formula.caveats && /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
3055
+ /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u26A0\uFE0F", title: t("pkgInfo_caveats"), color: COLORS.warning }),
3056
+ /* @__PURE__ */ jsx20(Box18, { borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, children: /* @__PURE__ */ jsx20(Text20, { color: COLORS.warning, children: formula.caveats }) })
3169
3057
  ] })
3170
3058
  ] }),
3171
- /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text19, { color: COLORS.textSecondary, children: [
3059
+ /* @__PURE__ */ jsx20(Box18, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs19(Text20, { color: COLORS.textSecondary, children: [
3172
3060
  isInstalled ? `u:${t("hint_uninstall")}` : `i:${t("hint_install")}`,
3173
3061
  isInstalled && formula.outdated ? ` U:${t("hint_upgrade")}` : "",
3174
3062
  ` esc:${t("hint_back")}`
@@ -3177,15 +3065,21 @@ function PackageInfoView() {
3177
3065
  }
3178
3066
 
3179
3067
  // src/views/services.tsx
3180
- import { useEffect as useEffect9, useState as useState7 } from "react";
3181
- import { Box as Box18, Text as Text20, useInput as useInput7, useStdout as useStdout6 } from "ink";
3182
- import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
3068
+ import { useEffect as useEffect10, useState as useState7 } from "react";
3069
+ import { Box as Box19, Text as Text21, useInput as useInput9, useStdout as useStdout6 } from "ink";
3070
+ import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
3183
3071
  var STATUS_VARIANTS = {
3184
3072
  started: "success",
3185
3073
  stopped: "muted",
3186
3074
  error: "error",
3187
3075
  none: "muted"
3188
3076
  };
3077
+ function humaniseServiceError(message) {
3078
+ if (/EACCES|operation not permitted|permission denied|sudo/i.test(message)) {
3079
+ return t("services_errorPermission");
3080
+ }
3081
+ return message;
3082
+ }
3189
3083
  function ServicesView() {
3190
3084
  const { services, loading, errors, fetchServices, serviceAction: serviceAction2 } = useBrewStore();
3191
3085
  const [cursor, setCursor] = useState7(0);
@@ -3197,10 +3091,10 @@ function ServicesView() {
3197
3091
  const svcNameWidth = Math.floor(cols * 0.35);
3198
3092
  const svcStatusWidth = Math.floor(cols * 0.15);
3199
3093
  const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 10);
3200
- useEffect9(() => {
3094
+ useEffect10(() => {
3201
3095
  fetchServices();
3202
3096
  }, []);
3203
- useInput7((input, key) => {
3097
+ useInput9((input, key) => {
3204
3098
  if (actionInProgress) return;
3205
3099
  if (confirmAction) return;
3206
3100
  if (lastError) {
@@ -3218,30 +3112,30 @@ function ServicesView() {
3218
3112
  const doAction = (action) => {
3219
3113
  setActionInProgress(true);
3220
3114
  void serviceAction2(svc.name, action).catch((err) => {
3221
- setLastError(err instanceof Error ? err.message : String(err));
3115
+ setLastError(humaniseServiceError(err instanceof Error ? err.message : String(err)));
3222
3116
  }).finally(() => {
3223
3117
  setActionInProgress(false);
3224
3118
  const storeError = useBrewStore.getState().errors["service-action"];
3225
- if (storeError) setLastError(storeError);
3119
+ if (storeError) setLastError(humaniseServiceError(storeError));
3226
3120
  });
3227
3121
  };
3228
3122
  if (input === "s") doAction("start");
3229
3123
  else if (input === "x") setConfirmAction({ type: "stop", name: svc.name });
3230
3124
  else if (input === "R") setConfirmAction({ type: "restart", name: svc.name });
3231
3125
  });
3232
- if (loading.services) return /* @__PURE__ */ jsx20(Loading, { message: t("loading_services") });
3233
- if (errors.services) return /* @__PURE__ */ jsx20(ErrorMessage, { message: errors.services });
3126
+ if (loading.services) return /* @__PURE__ */ jsx21(Loading, { message: t("loading_services") });
3127
+ if (errors.services) return /* @__PURE__ */ jsx21(ErrorMessage, { message: errors.services });
3234
3128
  if (services.length === 0) {
3235
- return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
3236
- /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_title"), gradient: GRADIENTS.ocean }),
3237
- /* @__PURE__ */ jsx20(Text20, { color: COLORS.textSecondary, italic: true, children: t("services_noServices") })
3129
+ return /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", children: [
3130
+ /* @__PURE__ */ jsx21(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_title"), gradient: GRADIENTS.ocean }),
3131
+ /* @__PURE__ */ jsx21(Text21, { color: COLORS.textSecondary, italic: true, children: t("services_noServices") })
3238
3132
  ] });
3239
3133
  }
3240
3134
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
3241
3135
  const visible = services.slice(start, start + MAX_VISIBLE_ROWS);
3242
- return /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", children: [
3243
- /* @__PURE__ */ jsx20(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_titleCount", { count: services.length }), gradient: GRADIENTS.ocean }),
3244
- confirmAction && /* @__PURE__ */ jsx20(Box18, { marginY: 1, children: /* @__PURE__ */ jsx20(
3136
+ return /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", children: [
3137
+ /* @__PURE__ */ jsx21(SectionHeader, { emoji: "\u2699\uFE0F", title: t("services_titleCount", { count: services.length }), gradient: GRADIENTS.ocean }),
3138
+ confirmAction && /* @__PURE__ */ jsx21(Box19, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx21(
3245
3139
  ConfirmDialog,
3246
3140
  {
3247
3141
  message: confirmAction.type === "stop" ? t("services_confirmStop", { name: confirmAction.name }) : t("services_confirmRestart", { name: confirmAction.name }),
@@ -3260,37 +3154,37 @@ function ServicesView() {
3260
3154
  onCancel: () => setConfirmAction(null)
3261
3155
  }
3262
3156
  ) }),
3263
- /* @__PURE__ */ jsxs19(Box18, { flexDirection: "column", marginTop: 1, children: [
3264
- /* @__PURE__ */ jsxs19(Box18, { gap: 1, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, paddingBottom: 0, children: [
3265
- /* @__PURE__ */ jsxs19(Text20, { bold: true, color: COLORS.text, children: [
3157
+ /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", marginTop: SPACING.xs, children: [
3158
+ /* @__PURE__ */ jsxs20(Box19, { gap: SPACING.xs, borderStyle: "single", borderBottom: true, borderTop: false, borderLeft: false, borderRight: false, borderColor: COLORS.border, paddingBottom: SPACING.none, children: [
3159
+ /* @__PURE__ */ jsxs20(Text21, { bold: true, color: COLORS.text, children: [
3266
3160
  " ",
3267
3161
  t("services_name").padEnd(svcNameWidth)
3268
3162
  ] }),
3269
- /* @__PURE__ */ jsx20(Text20, { bold: true, color: COLORS.text, children: t("services_status").padEnd(svcStatusWidth) }),
3270
- /* @__PURE__ */ jsx20(Text20, { bold: true, color: COLORS.text, children: t("services_user") })
3163
+ /* @__PURE__ */ jsx21(Text21, { bold: true, color: COLORS.text, children: t("services_status").padEnd(svcStatusWidth) }),
3164
+ /* @__PURE__ */ jsx21(Text21, { bold: true, color: COLORS.text, children: t("services_user") })
3271
3165
  ] }),
3272
- start > 0 && /* @__PURE__ */ jsxs19(Text20, { color: COLORS.textSecondary, dimColor: true, children: [
3166
+ start > 0 && /* @__PURE__ */ jsxs20(Text21, { color: COLORS.textSecondary, dimColor: true, children: [
3273
3167
  " ",
3274
3168
  t("scroll_moreAbove", { count: start })
3275
3169
  ] }),
3276
3170
  visible.map((svc, i) => {
3277
3171
  const idx = start + i;
3278
3172
  const isCurrent = idx === cursor;
3279
- return /* @__PURE__ */ jsxs19(SelectableRow, { isCurrent, children: [
3280
- /* @__PURE__ */ jsx20(Text20, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: svc.name.padEnd(svcNameWidth - 2) }),
3281
- /* @__PURE__ */ jsx20(StatusBadge, { label: svc.status, variant: STATUS_VARIANTS[svc.status] }),
3282
- /* @__PURE__ */ jsx20(Text20, { color: COLORS.muted, children: svc.user ?? "-" }),
3283
- svc.exit_code != null && svc.exit_code !== 0 && /* @__PURE__ */ jsx20(Text20, { color: COLORS.error, children: t("common_exit", { code: svc.exit_code }) })
3173
+ return /* @__PURE__ */ jsxs20(SelectableRow, { isCurrent, children: [
3174
+ /* @__PURE__ */ jsx21(Text21, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: svc.name.padEnd(svcNameWidth - 2) }),
3175
+ /* @__PURE__ */ jsx21(StatusBadge, { label: svc.status, variant: STATUS_VARIANTS[svc.status] }),
3176
+ /* @__PURE__ */ jsx21(Text21, { color: COLORS.muted, children: svc.user ?? "-" }),
3177
+ svc.exit_code != null && svc.exit_code !== 0 && /* @__PURE__ */ jsx21(Text21, { color: COLORS.error, children: t("common_exit", { code: svc.exit_code }) })
3284
3178
  ] }, svc.name);
3285
3179
  }),
3286
- start + MAX_VISIBLE_ROWS < services.length && /* @__PURE__ */ jsxs19(Text20, { color: COLORS.textSecondary, dimColor: true, children: [
3180
+ start + MAX_VISIBLE_ROWS < services.length && /* @__PURE__ */ jsxs20(Text21, { color: COLORS.textSecondary, dimColor: true, children: [
3287
3181
  " ",
3288
3182
  t("scroll_moreBelow", { count: services.length - start - MAX_VISIBLE_ROWS })
3289
3183
  ] })
3290
3184
  ] }),
3291
- actionInProgress && /* @__PURE__ */ jsx20(Text20, { color: COLORS.sky, children: t("services_processing") }),
3292
- lastError && /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text20, { color: COLORS.error, children: lastError }) }),
3293
- /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs19(Text20, { color: COLORS.text, bold: true, children: [
3185
+ actionInProgress && /* @__PURE__ */ jsx21(Text21, { color: COLORS.sky, children: t("services_processing") }),
3186
+ lastError && /* @__PURE__ */ jsx21(Box19, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx21(Text21, { color: COLORS.error, children: lastError }) }),
3187
+ /* @__PURE__ */ jsx21(Box19, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs20(Text21, { color: COLORS.text, bold: true, children: [
3294
3188
  cursor + 1,
3295
3189
  "/",
3296
3190
  services.length
@@ -3299,53 +3193,53 @@ function ServicesView() {
3299
3193
  }
3300
3194
 
3301
3195
  // src/views/doctor.tsx
3302
- import { useEffect as useEffect10, useRef as useRef5 } from "react";
3303
- import { Box as Box19, Text as Text21, useInput as useInput8 } from "ink";
3304
- import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
3196
+ import { useEffect as useEffect11, useRef as useRef5 } from "react";
3197
+ import { Box as Box20, Text as Text22, useInput as useInput10 } from "ink";
3198
+ import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
3305
3199
  function DoctorView() {
3306
3200
  const { doctorWarnings, doctorClean, loading, errors, fetchDoctor } = useBrewStore();
3307
3201
  const mountedRef = useRef5(true);
3308
- useEffect10(() => {
3202
+ useEffect11(() => {
3309
3203
  mountedRef.current = true;
3310
3204
  return () => {
3311
3205
  mountedRef.current = false;
3312
3206
  };
3313
3207
  }, []);
3314
- useEffect10(() => {
3208
+ useEffect11(() => {
3315
3209
  fetchDoctor();
3316
3210
  }, []);
3317
- useInput8((input) => {
3211
+ useInput10((input) => {
3318
3212
  if (input === "r") void fetchDoctor();
3319
3213
  });
3320
- if (loading.doctor) return /* @__PURE__ */ jsx21(Loading, { message: t("loading_doctor") });
3321
- if (errors.doctor) return /* @__PURE__ */ jsx21(ErrorMessage, { message: errors.doctor });
3322
- return /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", children: [
3323
- /* @__PURE__ */ jsx21(SectionHeader, { emoji: "\u{1FA7A}", title: t("doctor_title"), gradient: GRADIENTS.emerald }),
3324
- /* @__PURE__ */ jsxs20(Box19, { flexDirection: "column", marginTop: 1, children: [
3325
- doctorClean && /* @__PURE__ */ jsx21(ResultBanner, { status: "success", message: `\u2714 ${t("doctor_clean")}` }),
3326
- doctorClean === false && doctorWarnings.length === 0 && /* @__PURE__ */ jsx21(Text21, { color: COLORS.warning, children: t("doctor_warningsNotCaptured") }),
3214
+ if (loading.doctor) return /* @__PURE__ */ jsx22(Loading, { message: t("loading_doctor") });
3215
+ if (errors.doctor) return /* @__PURE__ */ jsx22(ErrorMessage, { message: errors.doctor });
3216
+ return /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
3217
+ /* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u{1FA7A}", title: t("doctor_title"), gradient: GRADIENTS.emerald }),
3218
+ /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", marginTop: SPACING.xs, children: [
3219
+ doctorClean && /* @__PURE__ */ jsx22(ResultBanner, { status: "success", message: `\u2714 ${t("doctor_clean")}` }),
3220
+ doctorClean === false && doctorWarnings.length === 0 && /* @__PURE__ */ jsx22(Text22, { color: COLORS.warning, children: t("doctor_warningsNotCaptured") }),
3327
3221
  doctorWarnings.map((warning, i) => (
3328
3222
  // FE-004: Improved React key
3329
- /* @__PURE__ */ jsx21(Box19, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: COLORS.warning, paddingX: 1, children: warning.split("\n").map((line, j) => /* @__PURE__ */ jsx21(Text21, { color: j === 0 ? COLORS.warning : COLORS.muted, children: line }, `warning-${i}-${j}-${line.slice(0, 20)}`)) }, `warning-${i}-${warning.slice(0, 20)}`)
3223
+ /* @__PURE__ */ jsx22(Box20, { flexDirection: "column", marginBottom: SPACING.xs, borderStyle: "single", borderColor: COLORS.warning, paddingX: SPACING.xs, children: warning.split("\n").map((line, j) => /* @__PURE__ */ jsx22(Text22, { color: j === 0 ? COLORS.warning : COLORS.muted, children: line }, `warning-${i}-${j}-${line.slice(0, 20)}`)) }, `warning-${i}-${warning.slice(0, 20)}`)
3330
3224
  ))
3331
3225
  ] }),
3332
- /* @__PURE__ */ jsx21(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { color: COLORS.text, bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
3226
+ /* @__PURE__ */ jsx22(Box20, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx22(Text22, { color: COLORS.text, bold: true, children: doctorWarnings.length > 0 ? tp("plural_warnings", doctorWarnings.length) : "" }) })
3333
3227
  ] });
3334
3228
  }
3335
3229
 
3336
3230
  // src/views/profiles.tsx
3337
- import { useEffect as useEffect11, useRef as useRef6, useState as useState8 } from "react";
3338
- import { Box as Box24, useInput as useInput9 } from "ink";
3231
+ import { useEffect as useEffect12, useRef as useRef6, useState as useState8 } from "react";
3232
+ import { Box as Box25, useInput as useInput11 } from "ink";
3339
3233
 
3340
3234
  // src/stores/profile-store.ts
3341
3235
  import { create as create9 } from "zustand";
3342
3236
 
3343
3237
  // src/lib/profiles/profile-manager.ts
3344
- import { readFile as readFile3, writeFile as writeFile3, readdir, rm as rm2, rename as rename2 } from "fs/promises";
3345
- import { join as join2, basename } from "path";
3238
+ import { readFile, writeFile as writeFile2, readdir, rm, rename } from "fs/promises";
3239
+ import { join, basename } from "path";
3346
3240
 
3347
3241
  // src/lib/license/watermark.ts
3348
- function getWatermark(license, consent = true) {
3242
+ function getWatermark(license, consent = false) {
3349
3243
  if (!consent) return "";
3350
3244
  if (!license?.customerEmail) return "";
3351
3245
  return `Licensed to: ${license.customerEmail}`;
@@ -3369,7 +3263,7 @@ function validateProfileName(name) {
3369
3263
  }
3370
3264
  function profilePath(name) {
3371
3265
  validateProfileName(name);
3372
- return join2(PROFILES_DIR, `${basename(name)}.json`);
3266
+ return join(PROFILES_DIR, `${basename(name)}.json`);
3373
3267
  }
3374
3268
  async function listProfiles(isPro) {
3375
3269
  proCheck(isPro);
@@ -3383,7 +3277,7 @@ async function listProfiles(isPro) {
3383
3277
  }
3384
3278
  async function loadProfile(isPro, name) {
3385
3279
  proCheck(isPro);
3386
- const raw = await readFile3(profilePath(name), "utf-8");
3280
+ const raw = await readFile(profilePath(name), "utf-8");
3387
3281
  let file;
3388
3282
  try {
3389
3283
  file = JSON.parse(raw);
@@ -3404,13 +3298,13 @@ async function saveProfile(isPro, profile) {
3404
3298
  const filePath = profilePath(profile.name);
3405
3299
  const tmpPath = filePath + ".tmp";
3406
3300
  const file = { version: 1, profile };
3407
- await writeFile3(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
3408
- await rename2(tmpPath, filePath);
3301
+ await writeFile2(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
3302
+ await rename(tmpPath, filePath);
3409
3303
  }
3410
3304
  async function deleteProfile(isPro, name) {
3411
3305
  proCheck(isPro);
3412
3306
  try {
3413
- await rm2(profilePath(name));
3307
+ await rm(profilePath(name));
3414
3308
  } catch {
3415
3309
  }
3416
3310
  }
@@ -3436,8 +3330,8 @@ async function exportCurrentSetup(isPro, name, description, license = null, cons
3436
3330
  formulae: leaves,
3437
3331
  casks,
3438
3332
  taps,
3439
- exportedBy: consent ? getWatermark(license) : ""
3440
- // SEG-003: Only embed watermark with consent
3333
+ exportedBy: getWatermark(license, consent)
3334
+ // BK-014: explicit consent required (default false)
3441
3335
  };
3442
3336
  await saveProfile(isPro, profile);
3443
3337
  return profile;
@@ -3555,13 +3449,13 @@ var useProfileStore = create9((set) => ({
3555
3449
  }));
3556
3450
 
3557
3451
  // src/views/profiles/profile-list-mode.tsx
3558
- import { Box as Box20, Text as Text22 } from "ink";
3559
- import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
3452
+ import { Box as Box21, Text as Text23 } from "ink";
3453
+ import { jsx as jsx23, jsxs as jsxs22 } from "react/jsx-runtime";
3560
3454
  function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onConfirmDelete, onCancelDelete }) {
3561
- return /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
3562
- /* @__PURE__ */ jsx22(SectionHeader, { emoji: "\u{1F4C1}", title: t("profiles_title", { count: profileNames.length }), gradient: GRADIENTS.gold }),
3563
- loadError && /* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(Text22, { color: COLORS.error, children: loadError }) }),
3564
- confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(
3455
+ return /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", children: [
3456
+ /* @__PURE__ */ jsx23(SectionHeader, { emoji: "\u{1F4C1}", title: t("profiles_title", { count: profileNames.length }), gradient: GRADIENTS.gold }),
3457
+ loadError && /* @__PURE__ */ jsx23(Box21, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx23(Text23, { color: COLORS.error, children: loadError }) }),
3458
+ confirmDelete && profileNames[cursor] && /* @__PURE__ */ jsx23(Box21, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx23(
3565
3459
  ConfirmDialog,
3566
3460
  {
3567
3461
  message: t("profiles_confirmDelete", { name: profileNames[cursor] }),
@@ -3569,22 +3463,22 @@ function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onCon
3569
3463
  onCancel: onCancelDelete
3570
3464
  }
3571
3465
  ) }),
3572
- profileNames.length === 0 && !confirmDelete && /* @__PURE__ */ jsx22(Box20, { marginTop: 1, borderStyle: "round", borderColor: COLORS.textSecondary, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", children: [
3573
- /* @__PURE__ */ jsx22(Text22, { color: COLORS.textSecondary, italic: true, children: t("profiles_noProfiles") }),
3574
- /* @__PURE__ */ jsxs21(Text22, { color: COLORS.muted, children: [
3466
+ profileNames.length === 0 && !confirmDelete && /* @__PURE__ */ jsx23(Box21, { marginTop: SPACING.xs, borderStyle: "round", borderColor: COLORS.textSecondary, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", children: [
3467
+ /* @__PURE__ */ jsx23(Text23, { color: COLORS.textSecondary, italic: true, children: t("profiles_noProfiles") }),
3468
+ /* @__PURE__ */ jsxs22(Text23, { color: COLORS.muted, children: [
3575
3469
  t("profiles_press"),
3576
3470
  " ",
3577
- /* @__PURE__ */ jsx22(Text22, { color: COLORS.gold, bold: true, children: "n" }),
3471
+ /* @__PURE__ */ jsx23(Text23, { color: COLORS.gold, bold: true, children: "n" }),
3578
3472
  " ",
3579
3473
  t("profiles_exportHint")
3580
3474
  ] })
3581
3475
  ] }) }),
3582
- profileNames.length > 0 && !confirmDelete && /* @__PURE__ */ jsxs21(Box20, { flexDirection: "column", marginTop: 1, children: [
3476
+ profileNames.length > 0 && !confirmDelete && /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", marginTop: SPACING.xs, children: [
3583
3477
  profileNames.map((name, i) => {
3584
3478
  const isCurrent = i === cursor;
3585
- return /* @__PURE__ */ jsx22(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx22(Text22, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
3479
+ return /* @__PURE__ */ jsx23(SelectableRow, { isCurrent, children: /* @__PURE__ */ jsx23(Text23, { bold: isCurrent, inverse: isCurrent, children: name }) }, name);
3586
3480
  }),
3587
- /* @__PURE__ */ jsx22(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text22, { color: COLORS.text, bold: true, children: [
3481
+ /* @__PURE__ */ jsx23(Box21, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs22(Text23, { color: COLORS.text, bold: true, children: [
3588
3482
  cursor + 1,
3589
3483
  "/",
3590
3484
  profileNames.length
@@ -3594,23 +3488,23 @@ function ProfileListMode({ profileNames, cursor, confirmDelete, loadError, onCon
3594
3488
  }
3595
3489
 
3596
3490
  // src/views/profiles/profile-detail-mode.tsx
3597
- import { Box as Box21, Text as Text23 } from "ink";
3598
- import { jsx as jsx23, jsxs as jsxs22 } from "react/jsx-runtime";
3491
+ import { Box as Box22, Text as Text24 } from "ink";
3492
+ import { jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
3599
3493
  function ProfileDetailMode({ profile }) {
3600
- return /* @__PURE__ */ jsxs22(Box21, { flexDirection: "column", children: [
3601
- /* @__PURE__ */ jsx23(Text23, { bold: true, color: COLORS.gold, children: profile.name }),
3602
- /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: profile.description }),
3603
- /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: t("profiles_created", { date: formatDate(profile.createdAt) }) }),
3604
- /* @__PURE__ */ jsxs22(Box21, { marginTop: 1, flexDirection: "column", children: [
3605
- /* @__PURE__ */ jsx23(Text23, { bold: true, children: t("profiles_formulaeCount", { count: profile.formulae.length }) }),
3606
- /* @__PURE__ */ jsxs22(Box21, { paddingLeft: 2, flexDirection: "column", children: [
3607
- profile.formulae.slice(0, 30).map((f) => /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: f }, f)),
3608
- profile.formulae.length > 30 && /* @__PURE__ */ jsx23(Text23, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: profile.formulae.length - 30 }) })
3494
+ return /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", children: [
3495
+ /* @__PURE__ */ jsx24(Text24, { bold: true, color: COLORS.gold, children: profile.name }),
3496
+ /* @__PURE__ */ jsx24(Text24, { color: COLORS.muted, children: profile.description }),
3497
+ /* @__PURE__ */ jsx24(Text24, { color: COLORS.muted, children: t("profiles_created", { date: formatDate(profile.createdAt) }) }),
3498
+ /* @__PURE__ */ jsxs23(Box22, { marginTop: SPACING.xs, flexDirection: "column", children: [
3499
+ /* @__PURE__ */ jsx24(Text24, { bold: true, children: t("profiles_formulaeCount", { count: profile.formulae.length }) }),
3500
+ /* @__PURE__ */ jsxs23(Box22, { paddingLeft: SPACING.sm, flexDirection: "column", children: [
3501
+ profile.formulae.slice(0, 30).map((f) => /* @__PURE__ */ jsx24(Text24, { color: COLORS.muted, children: f }, f)),
3502
+ profile.formulae.length > 30 && /* @__PURE__ */ jsx24(Text24, { color: COLORS.textSecondary, italic: true, children: t("common_andMore", { count: profile.formulae.length - 30 }) })
3609
3503
  ] }),
3610
- /* @__PURE__ */ jsx23(Text23, { bold: true, children: t("profiles_casksCount", { count: profile.casks.length }) }),
3611
- /* @__PURE__ */ jsx23(Box21, { paddingLeft: 2, flexDirection: "column", children: profile.casks.map((c) => /* @__PURE__ */ jsx23(Text23, { color: COLORS.muted, children: c }, c)) })
3504
+ /* @__PURE__ */ jsx24(Text24, { bold: true, children: t("profiles_casksCount", { count: profile.casks.length }) }),
3505
+ /* @__PURE__ */ jsx24(Box22, { paddingLeft: SPACING.sm, flexDirection: "column", children: profile.casks.map((c) => /* @__PURE__ */ jsx24(Text24, { color: COLORS.muted, children: c }, c)) })
3612
3506
  ] }),
3613
- /* @__PURE__ */ jsx23(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs22(Text23, { color: COLORS.textSecondary, children: [
3507
+ /* @__PURE__ */ jsx24(Box22, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs23(Text24, { color: COLORS.textSecondary, children: [
3614
3508
  "esc:",
3615
3509
  t("hint_back"),
3616
3510
  " e:",
@@ -3622,13 +3516,13 @@ function ProfileDetailMode({ profile }) {
3622
3516
  }
3623
3517
 
3624
3518
  // src/views/profiles/profile-create-flow.tsx
3625
- import { Box as Box22, Text as Text24 } from "ink";
3519
+ import { Box as Box23, Text as Text25 } from "ink";
3626
3520
  import { TextInput as TextInput3 } from "@inkjs/ui";
3627
- import { jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
3521
+ import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
3628
3522
  function ProfileCreateName({ onSubmit }) {
3629
- return /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", children: [
3630
- /* @__PURE__ */ jsx24(Text24, { bold: true, children: t("profiles_createName") }),
3631
- /* @__PURE__ */ jsx24(
3523
+ return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", children: [
3524
+ /* @__PURE__ */ jsx25(Text25, { bold: true, children: t("profiles_createName") }),
3525
+ /* @__PURE__ */ jsx25(
3632
3526
  TextInput3,
3633
3527
  {
3634
3528
  placeholder: t("profiles_namePlaceholder"),
@@ -3638,13 +3532,13 @@ function ProfileCreateName({ onSubmit }) {
3638
3532
  ] });
3639
3533
  }
3640
3534
  function ProfileCreateDesc({ name, loadError, onSubmit }) {
3641
- return /* @__PURE__ */ jsxs23(Box22, { flexDirection: "column", children: [
3642
- /* @__PURE__ */ jsx24(Text24, { bold: true, children: t("profiles_createDesc", { name }) }),
3643
- loadError && /* @__PURE__ */ jsxs23(Text24, { color: COLORS.error, children: [
3535
+ return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", children: [
3536
+ /* @__PURE__ */ jsx25(Text25, { bold: true, children: t("profiles_createDesc", { name }) }),
3537
+ loadError && /* @__PURE__ */ jsxs24(Text25, { color: COLORS.error, children: [
3644
3538
  t("error_prefix"),
3645
3539
  loadError
3646
3540
  ] }),
3647
- /* @__PURE__ */ jsx24(
3541
+ /* @__PURE__ */ jsx25(
3648
3542
  TextInput3,
3649
3543
  {
3650
3544
  placeholder: t("profiles_descPlaceholder"),
@@ -3655,13 +3549,13 @@ function ProfileCreateDesc({ name, loadError, onSubmit }) {
3655
3549
  }
3656
3550
 
3657
3551
  // src/views/profiles/profile-edit-flow.tsx
3658
- import { Box as Box23, Text as Text25 } from "ink";
3552
+ import { Box as Box24, Text as Text26 } from "ink";
3659
3553
  import { TextInput as TextInput4 } from "@inkjs/ui";
3660
- import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
3554
+ import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
3661
3555
  function ProfileEditName({ defaultName, onSubmit }) {
3662
- return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", children: [
3663
- /* @__PURE__ */ jsx25(Text25, { bold: true, children: t("profiles_editName") }),
3664
- /* @__PURE__ */ jsx25(
3556
+ return /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", children: [
3557
+ /* @__PURE__ */ jsx26(Text26, { bold: true, children: t("profiles_editName") }),
3558
+ /* @__PURE__ */ jsx26(
3665
3559
  TextInput4,
3666
3560
  {
3667
3561
  defaultValue: defaultName,
@@ -3671,13 +3565,13 @@ function ProfileEditName({ defaultName, onSubmit }) {
3671
3565
  ] });
3672
3566
  }
3673
3567
  function ProfileEditDesc({ name, defaultDesc, loadError, onSubmit }) {
3674
- return /* @__PURE__ */ jsxs24(Box23, { flexDirection: "column", children: [
3675
- /* @__PURE__ */ jsx25(Text25, { bold: true, children: t("profiles_editDesc", { name }) }),
3676
- loadError && /* @__PURE__ */ jsxs24(Text25, { color: COLORS.error, children: [
3568
+ return /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", children: [
3569
+ /* @__PURE__ */ jsx26(Text26, { bold: true, children: t("profiles_editDesc", { name }) }),
3570
+ loadError && /* @__PURE__ */ jsxs25(Text26, { color: COLORS.error, children: [
3677
3571
  t("error_prefix"),
3678
3572
  loadError
3679
3573
  ] }),
3680
- /* @__PURE__ */ jsx25(
3574
+ /* @__PURE__ */ jsx26(
3681
3575
  TextInput4,
3682
3576
  {
3683
3577
  defaultValue: defaultDesc,
@@ -3688,7 +3582,7 @@ function ProfileEditDesc({ name, defaultDesc, loadError, onSubmit }) {
3688
3582
  }
3689
3583
 
3690
3584
  // src/views/profiles.tsx
3691
- import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
3585
+ import { jsx as jsx27, jsxs as jsxs26 } from "react/jsx-runtime";
3692
3586
  function ProfilesView() {
3693
3587
  const { profileNames, selectedProfile, loading, loadError, fetchProfiles, loadProfile: loadProfile2, exportCurrent, deleteProfile: deleteProfile2, updateProfile: updateProfile2 } = useProfileStore();
3694
3588
  const [cursor, setCursor] = useState8(0);
@@ -3704,10 +3598,10 @@ function ProfilesView() {
3704
3598
  const { openModal, closeModal } = useModalStore();
3705
3599
  const importGenRef = useRef6(null);
3706
3600
  const mountedRef = useRef6(true);
3707
- useEffect11(() => {
3601
+ useEffect12(() => {
3708
3602
  fetchProfiles();
3709
3603
  }, []);
3710
- useEffect11(() => {
3604
+ useEffect12(() => {
3711
3605
  mountedRef.current = true;
3712
3606
  return () => {
3713
3607
  mountedRef.current = false;
@@ -3715,7 +3609,7 @@ function ProfilesView() {
3715
3609
  importGenRef.current = null;
3716
3610
  };
3717
3611
  }, []);
3718
- useEffect11(() => {
3612
+ useEffect12(() => {
3719
3613
  if (mode !== "list") {
3720
3614
  openModal();
3721
3615
  return () => {
@@ -3724,7 +3618,7 @@ function ProfilesView() {
3724
3618
  }
3725
3619
  return void 0;
3726
3620
  }, [mode]);
3727
- useInput9((input, key) => {
3621
+ useInput11((input, key) => {
3728
3622
  if (mode !== "list" || confirmDelete) return;
3729
3623
  if (input === "n") {
3730
3624
  setMode("create-name");
@@ -3746,7 +3640,7 @@ function ProfilesView() {
3746
3640
  if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, profileNames.length - 1)));
3747
3641
  else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
3748
3642
  });
3749
- useInput9((input, key) => {
3643
+ useInput11((input, key) => {
3750
3644
  if (key.escape || input === "q") {
3751
3645
  setMode("list");
3752
3646
  return;
@@ -3757,7 +3651,7 @@ function ProfilesView() {
3757
3651
  setMode("edit-name");
3758
3652
  }
3759
3653
  }, { isActive: mode === "detail" });
3760
- useInput9(() => {
3654
+ useInput11(() => {
3761
3655
  setMode("list");
3762
3656
  }, { isActive: mode === "importing" && !importRunning });
3763
3657
  const prepareImport = async (name) => {
@@ -3797,9 +3691,9 @@ function ProfilesView() {
3797
3691
  }
3798
3692
  }
3799
3693
  };
3800
- if (loading) return /* @__PURE__ */ jsx26(Loading, { message: t("loading_profiles") });
3694
+ if (loading) return /* @__PURE__ */ jsx27(Loading, { message: t("loading_profiles") });
3801
3695
  if (mode === "confirm-import" && importProfile2) {
3802
- return /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", children: /* @__PURE__ */ jsx26(
3696
+ return /* @__PURE__ */ jsx27(Box25, { flexDirection: "column", children: /* @__PURE__ */ jsx27(
3803
3697
  ConfirmDialog,
3804
3698
  {
3805
3699
  message: t("profiles_importSummary", {
@@ -3819,19 +3713,19 @@ function ProfilesView() {
3819
3713
  ) });
3820
3714
  }
3821
3715
  if (mode === "importing") {
3822
- return /* @__PURE__ */ jsxs25(Box24, { flexDirection: "column", children: [
3823
- /* @__PURE__ */ jsx26(ProgressLog, { lines: importLines, isRunning: importRunning, title: t("profiles_importTitle") }),
3824
- !importRunning && /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsx26(ResultBanner, { status: importHadError ? "error" : "success", message: importHadError ? t("profiles_importPartial") : `\u2714 ${t("profiles_importComplete")}` }) })
3716
+ return /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
3717
+ /* @__PURE__ */ jsx27(ProgressLog, { lines: importLines, isRunning: importRunning, title: t("profiles_importTitle") }),
3718
+ !importRunning && /* @__PURE__ */ jsx27(Box25, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx27(ResultBanner, { status: importHadError ? "error" : "success", message: importHadError ? t("profiles_importPartial") : `\u2714 ${t("profiles_importComplete")}` }) })
3825
3719
  ] });
3826
3720
  }
3827
3721
  if (mode === "create-name") {
3828
- return /* @__PURE__ */ jsx26(ProfileCreateName, { onSubmit: (val) => {
3722
+ return /* @__PURE__ */ jsx27(ProfileCreateName, { onSubmit: (val) => {
3829
3723
  setNewName(val);
3830
3724
  setMode("create-desc");
3831
3725
  } });
3832
3726
  }
3833
3727
  if (mode === "create-desc") {
3834
- return /* @__PURE__ */ jsx26(
3728
+ return /* @__PURE__ */ jsx27(
3835
3729
  ProfileCreateDesc,
3836
3730
  {
3837
3731
  name: newName,
@@ -3848,13 +3742,13 @@ function ProfilesView() {
3848
3742
  );
3849
3743
  }
3850
3744
  if (mode === "edit-name") {
3851
- return /* @__PURE__ */ jsx26(ProfileEditName, { defaultName: editName, onSubmit: (val) => {
3745
+ return /* @__PURE__ */ jsx27(ProfileEditName, { defaultName: editName, onSubmit: (val) => {
3852
3746
  setEditName(val);
3853
3747
  setMode("edit-desc");
3854
3748
  } });
3855
3749
  }
3856
3750
  if (mode === "edit-desc") {
3857
- return /* @__PURE__ */ jsx26(
3751
+ return /* @__PURE__ */ jsx27(
3858
3752
  ProfileEditDesc,
3859
3753
  {
3860
3754
  name: editName,
@@ -3872,9 +3766,9 @@ function ProfilesView() {
3872
3766
  );
3873
3767
  }
3874
3768
  if (mode === "detail" && selectedProfile) {
3875
- return /* @__PURE__ */ jsx26(ProfileDetailMode, { profile: selectedProfile });
3769
+ return /* @__PURE__ */ jsx27(ProfileDetailMode, { profile: selectedProfile });
3876
3770
  }
3877
- return /* @__PURE__ */ jsx26(
3771
+ return /* @__PURE__ */ jsx27(
3878
3772
  ProfileListMode,
3879
3773
  {
3880
3774
  profileNames,
@@ -3891,8 +3785,8 @@ function ProfilesView() {
3891
3785
  }
3892
3786
 
3893
3787
  // src/views/smart-cleanup.tsx
3894
- import { useEffect as useEffect12, useRef as useRef7, useState as useState9 } from "react";
3895
- import { Box as Box25, Text as Text26, useInput as useInput10 } from "ink";
3788
+ import { useEffect as useEffect13, useRef as useRef7, useState as useState9 } from "react";
3789
+ import { Box as Box26, Text as Text27, useInput as useInput12 } from "ink";
3896
3790
 
3897
3791
  // src/stores/cleanup-store.ts
3898
3792
  import { create as create10 } from "zustand";
@@ -4012,7 +3906,7 @@ var useCleanupStore = create10((set, get) => ({
4012
3906
  }));
4013
3907
 
4014
3908
  // src/views/smart-cleanup.tsx
4015
- import { jsx as jsx27, jsxs as jsxs26 } from "react/jsx-runtime";
3909
+ import { jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
4016
3910
  function SmartCleanupView() {
4017
3911
  const { summary, selected, loading, error, analyze, toggleSelect, selectAll } = useCleanupStore();
4018
3912
  const [cursor, setCursor] = useState9(0);
@@ -4021,10 +3915,10 @@ function SmartCleanupView() {
4021
3915
  const [failedNames, setFailedNames] = useState9([]);
4022
3916
  const stream = useBrewStream();
4023
3917
  const hasRefreshed = useRef7(false);
4024
- useEffect12(() => {
3918
+ useEffect13(() => {
4025
3919
  analyze();
4026
3920
  }, []);
4027
- useEffect12(() => {
3921
+ useEffect13(() => {
4028
3922
  if (!stream.isRunning && !stream.error && stream.lines.length > 0 && !hasRefreshed.current) {
4029
3923
  hasRefreshed.current = true;
4030
3924
  void analyze();
@@ -4032,7 +3926,7 @@ function SmartCleanupView() {
4032
3926
  }, [stream.isRunning, stream.error]);
4033
3927
  const candidates = summary?.candidates ?? [];
4034
3928
  const isDependencyError = stream.error != null && stream.lines.some((l) => l.includes("Refusing to uninstall") || l.includes("required by"));
4035
- useInput10((input, key) => {
3929
+ useInput12((input, key) => {
4036
3930
  if (stream.isRunning) {
4037
3931
  if (key.escape) stream.cancel();
4038
3932
  return;
@@ -4063,26 +3957,26 @@ function SmartCleanupView() {
4063
3957
  if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, candidates.length - 1)));
4064
3958
  else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
4065
3959
  });
4066
- if (loading) return /* @__PURE__ */ jsx27(Loading, { message: t("loading_cleanup") });
4067
- if (error) return /* @__PURE__ */ jsx27(ErrorMessage, { message: error });
3960
+ if (loading) return /* @__PURE__ */ jsx28(Loading, { message: t("loading_cleanup") });
3961
+ if (error) return /* @__PURE__ */ jsx28(ErrorMessage, { message: error });
4068
3962
  if (stream.isRunning || stream.lines.length > 0) {
4069
- return /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
4070
- /* @__PURE__ */ jsx27(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("cleanup_cleaning") }),
4071
- stream.isRunning && /* @__PURE__ */ jsxs26(Text26, { color: COLORS.muted, children: [
3963
+ return /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
3964
+ /* @__PURE__ */ jsx28(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("cleanup_cleaning") }),
3965
+ stream.isRunning && /* @__PURE__ */ jsxs27(Text27, { color: COLORS.muted, children: [
4072
3966
  "esc:",
4073
3967
  t("hint_cancel")
4074
3968
  ] }),
4075
- !stream.isRunning && !stream.error && /* @__PURE__ */ jsx27(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_complete")}` }),
4076
- !stream.isRunning && stream.error && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", gap: 1, children: [
4077
- /* @__PURE__ */ jsx27(ResultBanner, { status: "error", message: `\u2718 ${t("cleanup_depError")}` }),
4078
- isDependencyError && failedNames.length > 0 && /* @__PURE__ */ jsxs26(Text26, { color: COLORS.warning, children: [
3969
+ !stream.isRunning && !stream.error && /* @__PURE__ */ jsx28(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_complete")}` }),
3970
+ !stream.isRunning && stream.error && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", gap: SPACING.xs, children: [
3971
+ /* @__PURE__ */ jsx28(ResultBanner, { status: "error", message: `\u2718 ${t("cleanup_depError")}` }),
3972
+ isDependencyError && failedNames.length > 0 && /* @__PURE__ */ jsxs27(Text27, { color: COLORS.warning, children: [
4079
3973
  "F:",
4080
3974
  t("hint_force"),
4081
3975
  " r:",
4082
3976
  t("hint_refresh")
4083
3977
  ] })
4084
3978
  ] }),
4085
- confirmForce && /* @__PURE__ */ jsx27(Box25, { marginY: 1, children: /* @__PURE__ */ jsx27(
3979
+ confirmForce && /* @__PURE__ */ jsx28(Box26, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx28(
4086
3980
  ConfirmDialog,
4087
3981
  {
4088
3982
  message: t("cleanup_confirmForce", { count: failedNames.length }),
@@ -4097,16 +3991,16 @@ function SmartCleanupView() {
4097
3991
  ) })
4098
3992
  ] });
4099
3993
  }
4100
- return /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
4101
- /* @__PURE__ */ jsx27(SectionHeader, { emoji: "\u{1F9F9}", title: t("cleanup_title"), gradient: GRADIENTS.emerald }),
4102
- summary && /* @__PURE__ */ jsxs26(Box25, { gap: 1, marginY: 1, children: [
4103
- /* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_orphans"), value: candidates.length, color: candidates.length > 0 ? COLORS.warning : COLORS.success }),
4104
- /* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_reclaimable"), value: summary.totalReclaimableFormatted, color: COLORS.sky }),
4105
- /* @__PURE__ */ jsx27(StatCard, { label: t("cleanup_selected"), value: selected.size, color: selected.size > 0 ? COLORS.success : COLORS.muted })
3994
+ return /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
3995
+ /* @__PURE__ */ jsx28(SectionHeader, { emoji: "\u{1F9F9}", title: t("cleanup_title"), gradient: GRADIENTS.emerald }),
3996
+ summary && /* @__PURE__ */ jsxs27(Box26, { gap: SPACING.xs, marginY: SPACING.xs, children: [
3997
+ /* @__PURE__ */ jsx28(StatCard, { label: t("cleanup_orphans"), value: candidates.length, color: candidates.length > 0 ? COLORS.warning : COLORS.success }),
3998
+ /* @__PURE__ */ jsx28(StatCard, { label: t("cleanup_reclaimable"), value: summary.totalReclaimableFormatted, color: COLORS.sky }),
3999
+ /* @__PURE__ */ jsx28(StatCard, { label: t("cleanup_selected"), value: selected.size, color: selected.size > 0 ? COLORS.success : COLORS.muted })
4106
4000
  ] }),
4107
- confirmClean && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", marginY: 1, gap: 1, children: [
4108
- /* @__PURE__ */ jsx27(Box25, { borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx27(Text26, { color: COLORS.warning, children: t("cleanup_warning_system_tools") }) }),
4109
- /* @__PURE__ */ jsx27(
4001
+ confirmClean && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", marginY: SPACING.xs, gap: SPACING.xs, children: [
4002
+ /* @__PURE__ */ jsx28(Box26, { borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx28(Text27, { color: COLORS.warning, children: t("cleanup_warning_system_tools") }) }),
4003
+ /* @__PURE__ */ jsx28(
4110
4004
  ConfirmDialog,
4111
4005
  {
4112
4006
  message: t("cleanup_confirmUninstall", { count: selected.size }),
@@ -4121,23 +4015,23 @@ function SmartCleanupView() {
4121
4015
  }
4122
4016
  )
4123
4017
  ] }),
4124
- candidates.length === 0 && !confirmClean && /* @__PURE__ */ jsx27(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_systemClean")}` }),
4125
- candidates.length > 0 && !confirmClean && /* @__PURE__ */ jsxs26(Box25, { flexDirection: "column", children: [
4018
+ candidates.length === 0 && !confirmClean && /* @__PURE__ */ jsx28(ResultBanner, { status: "success", message: `\u2714 ${t("cleanup_systemClean")}` }),
4019
+ candidates.length > 0 && !confirmClean && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
4126
4020
  candidates.map((c, i) => {
4127
4021
  const isCurrent = i === cursor;
4128
4022
  const isSelected = selected.has(c.name);
4129
- return /* @__PURE__ */ jsxs26(SelectableRow, { isCurrent, children: [
4130
- /* @__PURE__ */ jsx27(Text26, { color: isSelected ? COLORS.success : COLORS.muted, children: isSelected ? "\u2611" : "\u2610" }),
4131
- /* @__PURE__ */ jsx27(Text26, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: c.name }),
4132
- /* @__PURE__ */ jsx27(Text26, { color: COLORS.warning, children: c.diskUsageFormatted }),
4133
- /* @__PURE__ */ jsxs26(Text26, { color: COLORS.textSecondary, children: [
4023
+ return /* @__PURE__ */ jsxs27(SelectableRow, { isCurrent, children: [
4024
+ /* @__PURE__ */ jsx28(Text27, { color: isSelected ? COLORS.success : COLORS.muted, children: isSelected ? "\u2611" : "\u2610" }),
4025
+ /* @__PURE__ */ jsx28(Text27, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: c.name }),
4026
+ /* @__PURE__ */ jsx28(Text27, { color: COLORS.warning, children: c.diskUsageFormatted }),
4027
+ /* @__PURE__ */ jsxs27(Text27, { color: COLORS.textSecondary, children: [
4134
4028
  "[",
4135
4029
  c.reason,
4136
4030
  "]"
4137
4031
  ] })
4138
4032
  ] }, c.name);
4139
4033
  }),
4140
- /* @__PURE__ */ jsx27(Box25, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text26, { color: COLORS.text, bold: true, children: [
4034
+ /* @__PURE__ */ jsx28(Box26, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs27(Text27, { color: COLORS.text, bold: true, children: [
4141
4035
  cursor + 1,
4142
4036
  "/",
4143
4037
  candidates.length
@@ -4147,8 +4041,8 @@ function SmartCleanupView() {
4147
4041
  }
4148
4042
 
4149
4043
  // src/views/history.tsx
4150
- import { useEffect as useEffect13, useState as useState10, useMemo as useMemo4 } from "react";
4151
- import { Box as Box26, Text as Text27, useInput as useInput11, useStdout as useStdout7 } from "ink";
4044
+ import { useEffect as useEffect14, useState as useState10, useMemo as useMemo5 } from "react";
4045
+ import { Box as Box27, Text as Text28, useInput as useInput13, useStdout as useStdout7 } from "ink";
4152
4046
 
4153
4047
  // src/stores/history-store.ts
4154
4048
  import { create as create11 } from "zustand";
@@ -4184,7 +4078,7 @@ var useHistoryStore = create11((set) => ({
4184
4078
  }));
4185
4079
 
4186
4080
  // src/views/history.tsx
4187
- import { jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
4081
+ import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
4188
4082
  var ACTION_ICONS = {
4189
4083
  install: { icon: "+", color: COLORS.success },
4190
4084
  uninstall: { icon: "-", color: COLORS.error },
@@ -4209,10 +4103,12 @@ function HistoryView() {
4209
4103
  const stream = useBrewStream();
4210
4104
  const debouncedQuery = useDebounce(searchQuery, 200);
4211
4105
  const { openModal, closeModal } = useModalStore();
4212
- useEffect13(() => {
4106
+ const { stdout } = useStdout7();
4107
+ const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
4108
+ useEffect14(() => {
4213
4109
  fetchHistory();
4214
4110
  }, []);
4215
- useEffect13(() => {
4111
+ useEffect14(() => {
4216
4112
  if (isSearching) {
4217
4113
  openModal();
4218
4114
  return () => {
@@ -4221,7 +4117,7 @@ function HistoryView() {
4221
4117
  }
4222
4118
  return void 0;
4223
4119
  }, [isSearching]);
4224
- const filtered = useMemo4(() => {
4120
+ const filtered = useMemo5(() => {
4225
4121
  let result = entries;
4226
4122
  if (filter !== "all") {
4227
4123
  result = result.filter((e) => e.action === filter);
@@ -4232,7 +4128,7 @@ function HistoryView() {
4232
4128
  }
4233
4129
  return result;
4234
4130
  }, [entries, filter, debouncedQuery]);
4235
- useInput11((input, key) => {
4131
+ useInput13((input, key) => {
4236
4132
  if (confirmClear || confirmReplay || stream.isRunning) return;
4237
4133
  if (isSearching) {
4238
4134
  if (key.escape) {
@@ -4262,19 +4158,17 @@ function HistoryView() {
4262
4158
  if (input === "j" || key.downArrow) setCursor((c) => Math.min(c + 1, Math.max(0, filtered.length - 1)));
4263
4159
  else if (input === "k" || key.upArrow) setCursor((c) => Math.max(c - 1, 0));
4264
4160
  });
4265
- if (loading) return /* @__PURE__ */ jsx28(Loading, { message: t("loading_history") });
4266
- if (error) return /* @__PURE__ */ jsx28(ErrorMessage, { message: error });
4267
- const { stdout } = useStdout7();
4268
- const MAX_VISIBLE_ROWS = Math.max(5, (stdout?.rows ?? 24) - 8);
4161
+ if (loading) return /* @__PURE__ */ jsx29(Loading, { message: t("loading_history") });
4162
+ if (error) return /* @__PURE__ */ jsx29(ErrorMessage, { message: error });
4269
4163
  const start = Math.max(0, cursor - Math.floor(MAX_VISIBLE_ROWS / 2));
4270
4164
  const visible = filtered.slice(start, start + MAX_VISIBLE_ROWS);
4271
- return /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
4272
- /* @__PURE__ */ jsxs27(Box26, { gap: 2, marginBottom: 1, children: [
4273
- /* @__PURE__ */ jsx28(SectionHeader, { emoji: "\u{1F4DC}", title: t("history_title", { count: filtered.length }), gradient: GRADIENTS.gold }),
4274
- /* @__PURE__ */ jsx28(Text27, { color: filter === "all" ? COLORS.text : COLORS.gold, children: t("history_filterLabel", { filter }) })
4165
+ return /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", children: [
4166
+ /* @__PURE__ */ jsxs28(Box27, { gap: SPACING.sm, marginBottom: SPACING.xs, children: [
4167
+ /* @__PURE__ */ jsx29(SectionHeader, { emoji: "\u{1F4DC}", title: t("history_title", { count: filtered.length }), gradient: GRADIENTS.gold }),
4168
+ /* @__PURE__ */ jsx29(Text28, { color: filter === "all" ? COLORS.text : COLORS.gold, children: t("history_filterLabel", { filter }) })
4275
4169
  ] }),
4276
- isSearching && /* @__PURE__ */ jsx28(Box26, { marginBottom: 1, children: /* @__PURE__ */ jsx28(SearchInput, { defaultValue: searchQuery, onChange: setSearchQuery, placeholder: t("history_searchPlaceholder"), isActive: true }) }),
4277
- confirmClear && /* @__PURE__ */ jsx28(
4170
+ isSearching && /* @__PURE__ */ jsx29(Box27, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx29(SearchInput, { defaultValue: searchQuery, onChange: setSearchQuery, placeholder: t("history_searchPlaceholder"), isActive: true }) }),
4171
+ confirmClear && /* @__PURE__ */ jsx29(
4278
4172
  ConfirmDialog,
4279
4173
  {
4280
4174
  message: t("history_confirmClear", { count: entries.length }),
@@ -4285,7 +4179,7 @@ function HistoryView() {
4285
4179
  onCancel: () => setConfirmClear(false)
4286
4180
  }
4287
4181
  ),
4288
- confirmReplay && /* @__PURE__ */ jsx28(
4182
+ confirmReplay && /* @__PURE__ */ jsx29(
4289
4183
  ConfirmDialog,
4290
4184
  {
4291
4185
  message: confirmReplay.action === "upgrade-all" ? t("history_replayAll") + "\n" + t("upgrade_all_warning") : t("history_confirmReplay", { action: t(ACTION_LABEL_KEYS[confirmReplay.action]), name: confirmReplay.packageName ?? "" }),
@@ -4313,10 +4207,10 @@ function HistoryView() {
4313
4207
  onCancel: () => setConfirmReplay(null)
4314
4208
  }
4315
4209
  ),
4316
- (stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx28(Box26, { marginY: 1, children: /* @__PURE__ */ jsx28(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_replay") }) }),
4317
- filtered.length === 0 && !confirmClear && /* @__PURE__ */ jsx28(Text27, { color: COLORS.textSecondary, italic: true, children: filter !== "all" ? t("history_noEntriesFor", { filter }) : t("history_noEntries") }),
4318
- filtered.length > 0 && !confirmClear && /* @__PURE__ */ jsxs27(Box26, { flexDirection: "column", children: [
4319
- start > 0 && /* @__PURE__ */ jsxs27(Text27, { color: COLORS.textSecondary, dimColor: true, children: [
4210
+ (stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx29(Box27, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx29(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_replay") }) }),
4211
+ filtered.length === 0 && !confirmClear && /* @__PURE__ */ jsx29(Text28, { color: COLORS.textSecondary, italic: true, children: filter !== "all" ? t("history_noEntriesFor", { filter }) : t("history_noEntries") }),
4212
+ filtered.length > 0 && !confirmClear && /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", children: [
4213
+ start > 0 && /* @__PURE__ */ jsxs28(Text28, { color: COLORS.textSecondary, dimColor: true, children: [
4320
4214
  " ",
4321
4215
  t("scroll_moreAbove", { count: start })
4322
4216
  ] }),
@@ -4325,19 +4219,19 @@ function HistoryView() {
4325
4219
  const isCurrent = idx === cursor;
4326
4220
  const { icon, color } = ACTION_ICONS[entry.action];
4327
4221
  const ts = new Date(entry.timestamp).getTime() / 1e3;
4328
- return /* @__PURE__ */ jsxs27(SelectableRow, { isCurrent, children: [
4329
- /* @__PURE__ */ jsx28(Text27, { color, bold: true, children: icon }),
4330
- /* @__PURE__ */ jsx28(Text27, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: t(ACTION_LABEL_KEYS[entry.action]).padEnd(12) }),
4331
- /* @__PURE__ */ jsx28(Text27, { color: COLORS.text, children: entry.packageName ?? t("history_all") }),
4332
- entry.success ? /* @__PURE__ */ jsx28(StatusBadge, { label: t("badge_ok"), variant: "success" }) : /* @__PURE__ */ jsx28(StatusBadge, { label: t("badge_fail"), variant: "error" }),
4333
- /* @__PURE__ */ jsx28(Text27, { color: COLORS.muted, children: formatRelativeTime(ts) })
4222
+ return /* @__PURE__ */ jsxs28(SelectableRow, { isCurrent, children: [
4223
+ /* @__PURE__ */ jsx29(Text28, { color, bold: true, children: icon }),
4224
+ /* @__PURE__ */ jsx29(Text28, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: t(ACTION_LABEL_KEYS[entry.action]).padEnd(12) }),
4225
+ /* @__PURE__ */ jsx29(Text28, { color: COLORS.text, children: entry.packageName ?? t("history_all") }),
4226
+ entry.success ? /* @__PURE__ */ jsx29(StatusBadge, { label: t("badge_ok"), variant: "success" }) : /* @__PURE__ */ jsx29(StatusBadge, { label: t("badge_fail"), variant: "error" }),
4227
+ /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: formatRelativeTime(ts) })
4334
4228
  ] }, entry.id);
4335
4229
  }),
4336
- start + MAX_VISIBLE_ROWS < filtered.length && /* @__PURE__ */ jsxs27(Text27, { color: COLORS.textSecondary, dimColor: true, children: [
4230
+ start + MAX_VISIBLE_ROWS < filtered.length && /* @__PURE__ */ jsxs28(Text28, { color: COLORS.textSecondary, dimColor: true, children: [
4337
4231
  " ",
4338
4232
  t("scroll_moreBelow", { count: filtered.length - start - MAX_VISIBLE_ROWS })
4339
4233
  ] }),
4340
- /* @__PURE__ */ jsx28(Box26, { marginTop: 1, children: /* @__PURE__ */ jsxs27(Text27, { color: COLORS.text, bold: true, children: [
4234
+ /* @__PURE__ */ jsx29(Box27, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs28(Text28, { color: COLORS.text, bold: true, children: [
4341
4235
  cursor + 1,
4342
4236
  "/",
4343
4237
  filtered.length
@@ -4347,9 +4241,9 @@ function HistoryView() {
4347
4241
  }
4348
4242
 
4349
4243
  // src/views/security-audit.tsx
4350
- import { useEffect as useEffect14, useState as useState11 } from "react";
4351
- import { Box as Box27, Text as Text28, useInput as useInput12 } from "ink";
4352
- import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
4244
+ import { useEffect as useEffect15, useState as useState11 } from "react";
4245
+ import { Box as Box28, Text as Text29, useInput as useInput14 } from "ink";
4246
+ import { jsx as jsx30, jsxs as jsxs29 } from "react/jsx-runtime";
4353
4247
  var SEVERITY_COLORS = {
4354
4248
  CRITICAL: COLORS.error,
4355
4249
  HIGH: COLORS.error,
@@ -4364,7 +4258,7 @@ var SEVERITY_BADGE = {
4364
4258
  LOW: "muted",
4365
4259
  UNKNOWN: "muted"
4366
4260
  };
4367
- function isNetworkError2(msg) {
4261
+ function isNetworkError(msg) {
4368
4262
  return /fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network|abort/i.test(msg);
4369
4263
  }
4370
4264
  function SecurityAuditView() {
@@ -4374,11 +4268,11 @@ function SecurityAuditView() {
4374
4268
  const [expandedPkg, setExpandedPkg] = useState11(null);
4375
4269
  const [confirmUpgrade, setConfirmUpgrade] = useState11(null);
4376
4270
  const stream = useBrewStream();
4377
- useEffect14(() => {
4271
+ useEffect15(() => {
4378
4272
  scan();
4379
4273
  }, []);
4380
4274
  const results = summary?.results ?? [];
4381
- useInput12((input, key) => {
4275
+ useInput14((input, key) => {
4382
4276
  if (confirmUpgrade || stream.isRunning) return;
4383
4277
  if (input === "r") {
4384
4278
  void scan(true);
@@ -4398,17 +4292,17 @@ function SecurityAuditView() {
4398
4292
  setExpandedPkg(expandedPkg === results[cursor].packageName ? null : results[cursor].packageName);
4399
4293
  }
4400
4294
  });
4401
- if (loading) return /* @__PURE__ */ jsx29(Loading, { message: t("loading_security") });
4295
+ if (loading) return /* @__PURE__ */ jsx30(Loading, { message: t("loading_security") });
4402
4296
  if (error) {
4403
- const displayError = isNetworkError2(error) ? t("security_networkError") : error;
4404
- return /* @__PURE__ */ jsx29(ErrorMessage, { message: displayError });
4297
+ const displayError = isNetworkError(error) ? t("security_networkError") : error;
4298
+ return /* @__PURE__ */ jsx30(ErrorMessage, { message: displayError });
4405
4299
  }
4406
4300
  const cacheAge = cachedAt ? formatRelativeTime(cachedAt / 1e3) : null;
4407
- return /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", children: [
4408
- /* @__PURE__ */ jsx29(SectionHeader, { emoji: "\u{1F6E1}\uFE0F", title: t("security_title"), gradient: GRADIENTS.ocean }),
4409
- summary && /* @__PURE__ */ jsxs28(Box27, { gap: 1, marginY: 1, children: [
4410
- /* @__PURE__ */ jsx29(StatCard, { label: t("security_scanned"), value: summary.totalPackages, color: COLORS.info }),
4411
- /* @__PURE__ */ jsx29(
4301
+ return /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
4302
+ /* @__PURE__ */ jsx30(SectionHeader, { emoji: "\u{1F6E1}\uFE0F", title: t("security_title"), gradient: GRADIENTS.ocean }),
4303
+ summary && /* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, marginY: SPACING.xs, children: [
4304
+ /* @__PURE__ */ jsx30(StatCard, { label: t("security_scanned"), value: summary.totalPackages, color: COLORS.info }),
4305
+ /* @__PURE__ */ jsx30(
4412
4306
  StatCard,
4413
4307
  {
4414
4308
  label: t("security_vulnerable"),
@@ -4416,13 +4310,14 @@ function SecurityAuditView() {
4416
4310
  color: summary.vulnerablePackages > 0 ? COLORS.error : COLORS.success
4417
4311
  }
4418
4312
  ),
4419
- summary.criticalCount > 0 && /* @__PURE__ */ jsx29(StatCard, { label: t("security_critical"), value: summary.criticalCount, color: COLORS.error }),
4420
- summary.highCount > 0 && /* @__PURE__ */ jsx29(StatCard, { label: t("security_high"), value: summary.highCount, color: COLORS.error }),
4421
- summary.mediumCount > 0 && /* @__PURE__ */ jsx29(StatCard, { label: t("security_medium"), value: summary.mediumCount, color: COLORS.warning })
4313
+ summary.criticalCount > 0 && /* @__PURE__ */ jsx30(StatCard, { label: t("security_critical"), value: summary.criticalCount, color: COLORS.error }),
4314
+ summary.highCount > 0 && /* @__PURE__ */ jsx30(StatCard, { label: t("security_high"), value: summary.highCount, color: COLORS.error }),
4315
+ summary.mediumCount > 0 && /* @__PURE__ */ jsx30(StatCard, { label: t("security_medium"), value: summary.mediumCount, color: COLORS.warning })
4422
4316
  ] }),
4423
- cacheAge && /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: t("security_cachedResults", { time: cacheAge }) }),
4424
- results.length === 0 && summary && /* @__PURE__ */ jsx29(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx29(ResultBanner, { status: "success", message: `\u2714 ${t("security_noVulns")}` }) }),
4425
- confirmUpgrade && /* @__PURE__ */ jsx29(Box27, { marginY: 1, children: /* @__PURE__ */ jsx29(
4317
+ cacheAge && /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("security_cachedResults", { time: cacheAge }) }),
4318
+ summary && /* @__PURE__ */ jsx30(Box28, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.textSecondary, italic: true, children: t("security_coverage_warning") }) }),
4319
+ results.length === 0 && summary && /* @__PURE__ */ jsx30(Box28, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx30(ResultBanner, { status: "success", message: `\u2714 ${t("security_noVulns")}` }) }),
4320
+ confirmUpgrade && /* @__PURE__ */ jsx30(Box28, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx30(
4426
4321
  ConfirmDialog,
4427
4322
  {
4428
4323
  message: t("security_confirmUpgrade", { name: confirmUpgrade }),
@@ -4435,82 +4330,83 @@ function SecurityAuditView() {
4435
4330
  onCancel: () => setConfirmUpgrade(null)
4436
4331
  }
4437
4332
  ) }),
4438
- (stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx29(Box27, { marginY: 1, children: /* @__PURE__ */ jsx29(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_upgrade") }) }),
4439
- results.length > 0 && /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginTop: 1, children: [
4333
+ (stream.isRunning || stream.lines.length > 0) && /* @__PURE__ */ jsx30(Box28, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx30(ProgressLog, { lines: stream.lines, isRunning: stream.isRunning, title: t("hint_upgrade") }) }),
4334
+ results.length > 0 && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: SPACING.xs, children: [
4440
4335
  results.map((pkg, i) => {
4441
4336
  const isCurrent = i === cursor;
4442
4337
  const isExpanded = expandedPkg === pkg.packageName;
4443
- return /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", children: [
4444
- /* @__PURE__ */ jsxs28(SelectableRow, { isCurrent, children: [
4445
- /* @__PURE__ */ jsx29(StatusBadge, { label: pkg.maxSeverity, variant: SEVERITY_BADGE[pkg.maxSeverity] }),
4446
- /* @__PURE__ */ jsx29(Text28, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: pkg.packageName }),
4447
- /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: pkg.installedVersion }),
4448
- /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: tp("plural_vulns", pkg.vulnerabilities.length) }),
4449
- pkg.vulnerabilities.some((v) => v.fixedVersion) && /* @__PURE__ */ jsxs28(Text28, { color: COLORS.textSecondary, children: [
4338
+ return /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
4339
+ /* @__PURE__ */ jsxs29(SelectableRow, { isCurrent, children: [
4340
+ /* @__PURE__ */ jsx30(StatusBadge, { label: pkg.maxSeverity, variant: SEVERITY_BADGE[pkg.maxSeverity] }),
4341
+ /* @__PURE__ */ jsx30(Text29, { bold: isCurrent, inverse: isCurrent, color: isCurrent ? COLORS.text : COLORS.muted, children: pkg.packageName }),
4342
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: pkg.installedVersion }),
4343
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: tp("plural_vulns", pkg.vulnerabilities.length) }),
4344
+ pkg.vulnerabilities.some((v) => v.fixedVersion) && /* @__PURE__ */ jsxs29(Text29, { color: COLORS.textSecondary, children: [
4450
4345
  "[R:",
4451
4346
  t("hint_rollback"),
4452
4347
  "]"
4453
4348
  ] }),
4454
- /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, children: isExpanded ? "\u25BC" : "\u25B6" })
4349
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: isExpanded ? "\u25BC" : "\u25B6" })
4455
4350
  ] }),
4456
- isExpanded && /* @__PURE__ */ jsx29(Box27, { flexDirection: "column", paddingLeft: 4, marginBottom: 1, children: pkg.vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs28(Box27, { flexDirection: "column", marginBottom: 1, children: [
4457
- /* @__PURE__ */ jsxs28(Box27, { gap: 1, children: [
4458
- /* @__PURE__ */ jsx29(Text28, { color: SEVERITY_COLORS[vuln.severity], bold: true, children: vuln.id }),
4459
- /* @__PURE__ */ jsxs28(Text28, { color: COLORS.muted, children: [
4351
+ isExpanded && /* @__PURE__ */ jsx30(Box28, { flexDirection: "column", paddingLeft: SPACING.lg, marginBottom: SPACING.xs, children: pkg.vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginBottom: SPACING.xs, children: [
4352
+ /* @__PURE__ */ jsxs29(Box28, { gap: SPACING.xs, children: [
4353
+ /* @__PURE__ */ jsx30(Text29, { color: SEVERITY_COLORS[vuln.severity], bold: true, children: vuln.id }),
4354
+ /* @__PURE__ */ jsxs29(Text29, { color: COLORS.muted, children: [
4460
4355
  "[",
4461
4356
  vuln.severity,
4462
4357
  "]"
4463
4358
  ] })
4464
4359
  ] }),
4465
- /* @__PURE__ */ jsx29(Text28, { color: COLORS.muted, wrap: "wrap", children: vuln.summary }),
4466
- vuln.fixedVersion && /* @__PURE__ */ jsx29(Text28, { color: COLORS.success, children: t("security_fixedIn", { version: vuln.fixedVersion }) })
4360
+ /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, wrap: "wrap", children: vuln.summary }),
4361
+ vuln.fixedVersion && /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, children: t("security_fixedIn", { version: vuln.fixedVersion }) })
4467
4362
  ] }, vuln.id)) })
4468
4363
  ] }, pkg.packageName);
4469
4364
  }),
4470
- /* @__PURE__ */ jsx29(Box27, { marginTop: 1, children: /* @__PURE__ */ jsxs28(Text28, { color: COLORS.text, bold: true, children: [
4365
+ /* @__PURE__ */ jsx30(Box28, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs29(Text29, { color: COLORS.text, bold: true, children: [
4471
4366
  cursor + 1,
4472
4367
  "/",
4473
4368
  results.length
4474
4369
  ] }) }),
4475
- /* @__PURE__ */ jsx29(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text28, { color: COLORS.textSecondary, children: t("security_rollback_hint") }) })
4370
+ /* @__PURE__ */ jsx30(Box28, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.textSecondary, children: t("security_rollback_hint") }) })
4476
4371
  ] })
4477
4372
  ] });
4478
4373
  }
4479
4374
 
4480
4375
  // src/views/account.tsx
4481
4376
  import { useState as useState12 } from "react";
4482
- import { Box as Box28, Text as Text29, useInput as useInput13 } from "ink";
4377
+ import { Box as Box29, Text as Text30, useInput as useInput15 } from "ink";
4483
4378
  import { TextInput as TextInput5 } from "@inkjs/ui";
4484
4379
 
4485
4380
  // src/lib/license/promo.ts
4486
- import { readFile as readFile4, writeFile as writeFile4, rename as rename3, mkdir as mkdir2 } from "fs/promises";
4487
- import { randomBytes as randomBytes2, randomUUID as randomUUID2 } from "crypto";
4488
- import { join as join3 } from "path";
4489
- import { homedir as homedir2 } from "os";
4490
- var MACHINE_ID_PATH2 = join3(homedir2(), ".brew-tui", "machine-id");
4491
- async function getMachineId3() {
4492
- try {
4493
- const id2 = (await readFile4(MACHINE_ID_PATH2, "utf-8")).trim();
4494
- if (id2) return id2;
4495
- } catch {
4381
+ import { readFile as readFile2, writeFile as writeFile3, rename as rename2 } from "fs/promises";
4382
+ import { randomBytes, randomUUID } from "crypto";
4383
+ import { join as join2 } from "path";
4384
+ var PROMO_PATH = join2(DATA_DIR, "promo.json");
4385
+ var PROMO_API_URL = "https://api.molinesdesigns.com/api/promo";
4386
+ function validatePromoApiUrl(url) {
4387
+ const parsed = new URL(url);
4388
+ if (parsed.protocol !== "https:") {
4389
+ throw new Error("HTTPS required for promo API");
4390
+ }
4391
+ if (!parsed.hostname.endsWith("molinesdesigns.com")) {
4392
+ throw new Error("Invalid promo API host");
4496
4393
  }
4497
- const id = randomUUID2();
4498
- await mkdir2(join3(homedir2(), ".brew-tui"), { recursive: true, mode: 448 });
4499
- await writeFile4(MACHINE_ID_PATH2, id, { encoding: "utf-8", mode: 384 });
4500
- return id;
4501
4394
  }
4502
- var PROMO_PATH = join3(DATA_DIR, "promo.json");
4503
- var PROMO_API_URL = "https://api.molinesdesigns.com/api/promo";
4504
4395
  async function redeemPromoCode(code) {
4505
4396
  const normalized = code.trim().toUpperCase();
4506
- const machineId = await getMachineId3();
4397
+ const machineId = await getMachineId();
4398
+ validatePromoApiUrl(`${PROMO_API_URL}/redeem`);
4507
4399
  let serverExpiresAt;
4508
4400
  let serverType;
4401
+ const idempotencyKey = randomUUID();
4509
4402
  try {
4510
4403
  const res = await fetchWithTimeout(`${PROMO_API_URL}/redeem`, {
4511
4404
  method: "POST",
4512
- headers: { "Content-Type": "application/json" },
4513
- body: JSON.stringify({ code: normalized, machineId })
4405
+ headers: {
4406
+ "Content-Type": "application/json",
4407
+ "Idempotency-Key": idempotencyKey
4408
+ },
4409
+ body: JSON.stringify({ code: normalized, machineId, idempotencyKey })
4514
4410
  }, 1e4);
4515
4411
  if (!res.ok) {
4516
4412
  const body = await res.json().catch(() => ({}));
@@ -4541,7 +4437,7 @@ async function redeemPromoCode(code) {
4541
4437
  };
4542
4438
  let file = { version: 1, redemptions: [] };
4543
4439
  try {
4544
- const raw = await readFile4(PROMO_PATH, "utf-8");
4440
+ const raw = await readFile2(PROMO_PATH, "utf-8");
4545
4441
  file = JSON.parse(raw);
4546
4442
  } catch {
4547
4443
  }
@@ -4550,23 +4446,24 @@ async function redeemPromoCode(code) {
4550
4446
  }
4551
4447
  file.redemptions.push(redemption);
4552
4448
  const tmpPath = PROMO_PATH + ".tmp";
4553
- await writeFile4(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
4554
- await rename3(tmpPath, PROMO_PATH);
4449
+ await writeFile3(tmpPath, JSON.stringify(file, null, 2), { encoding: "utf-8", mode: 384 });
4450
+ await rename2(tmpPath, PROMO_PATH);
4555
4451
  return { success: true, expiresAt: redemption.expiresAt };
4556
4452
  }
4557
4453
 
4558
4454
  // src/views/account.tsx
4559
- import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs29 } from "react/jsx-runtime";
4455
+ import { Fragment as Fragment5, jsx as jsx31, jsxs as jsxs30 } from "react/jsx-runtime";
4560
4456
  function AccountView() {
4561
- const { status, license, deactivate: deactivate2, degradation } = useLicenseStore();
4457
+ const { status, license, deactivate: deactivate2, revalidate: revalidate2, degradation } = useLicenseStore();
4562
4458
  const [confirmDeactivate, setConfirmDeactivate] = useState12(false);
4563
4459
  const [deactivating, setDeactivating] = useState12(false);
4564
4460
  const [deactivateError, setDeactivateError] = useState12(null);
4565
4461
  const [promoMode, setPromoMode] = useState12(false);
4566
4462
  const [promoLoading, setPromoLoading] = useState12(false);
4567
4463
  const [promoResult, setPromoResult] = useState12(null);
4568
- useInput13((input, key) => {
4569
- if (confirmDeactivate || deactivating || promoMode) {
4464
+ const [revalidating, setRevalidating] = useState12(false);
4465
+ useInput15((input, key) => {
4466
+ if (confirmDeactivate || deactivating || promoMode || revalidating) {
4570
4467
  if (key.escape && promoMode) {
4571
4468
  setPromoMode(false);
4572
4469
  setPromoResult(null);
@@ -4580,17 +4477,21 @@ function AccountView() {
4580
4477
  setPromoMode(true);
4581
4478
  setPromoResult(null);
4582
4479
  }
4480
+ if ((input === "v" || input === "V") && (status === "pro" || status === "team" || status === "expired")) {
4481
+ setRevalidating(true);
4482
+ void revalidate2().finally(() => setRevalidating(false));
4483
+ }
4583
4484
  });
4584
4485
  const maskKey = (key) => {
4585
4486
  if (key.length <= 8) return key;
4586
4487
  return key.slice(0, 4) + "-****-****-" + key.slice(-4);
4587
4488
  };
4588
4489
  if (status === "validating") {
4589
- return /* @__PURE__ */ jsx30(Loading, { message: t("account_loading") });
4490
+ return /* @__PURE__ */ jsx31(Loading, { message: t("account_loading") });
4590
4491
  }
4591
- return /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", children: [
4592
- /* @__PURE__ */ jsx30(SectionHeader, { emoji: "\u{1F464}", title: t("account_title"), gradient: GRADIENTS.gold }),
4593
- confirmDeactivate && /* @__PURE__ */ jsx30(Box28, { marginY: 1, children: /* @__PURE__ */ jsx30(
4492
+ return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
4493
+ /* @__PURE__ */ jsx31(SectionHeader, { emoji: "\u{1F464}", title: t("account_title"), gradient: GRADIENTS.gold }),
4494
+ confirmDeactivate && /* @__PURE__ */ jsx31(Box29, { marginY: SPACING.xs, children: /* @__PURE__ */ jsx31(
4594
4495
  ConfirmDialog,
4595
4496
  {
4596
4497
  message: t("account_confirmDeactivate"),
@@ -4609,72 +4510,72 @@ function AccountView() {
4609
4510
  onCancel: () => setConfirmDeactivate(false)
4610
4511
  }
4611
4512
  ) }),
4612
- /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [
4613
- /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4614
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_statusLabel") }),
4615
- status === "pro" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: t("account_pro") }),
4616
- status === "free" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_free") }),
4617
- status === "expired" && /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: t("account_expired") })
4513
+ /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: SPACING.xs, paddingLeft: SPACING.sm, children: [
4514
+ /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4515
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_statusLabel") }),
4516
+ status === "pro" && /* @__PURE__ */ jsx31(Text30, { color: COLORS.success, bold: true, children: t("account_pro") }),
4517
+ status === "free" && /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_free") }),
4518
+ status === "expired" && /* @__PURE__ */ jsx31(Text30, { color: COLORS.error, children: t("account_expired") })
4618
4519
  ] }),
4619
- (degradation === "warning" || degradation === "limited") && license && /* @__PURE__ */ jsx30(Box28, { marginTop: 1, borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.warning, children: t("license_offlineWarning", {
4520
+ (degradation === "warning" || degradation === "limited") && license && /* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, borderStyle: "round", borderColor: COLORS.warning, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx31(Text30, { color: COLORS.warning, children: t("license_offlineWarning", {
4620
4521
  days: Math.floor((Date.now() - new Date(license.lastValidatedAt).getTime()) / (24 * 60 * 60 * 1e3))
4621
4522
  }) }) }),
4622
- license && /* @__PURE__ */ jsxs29(Fragment5, { children: [
4623
- /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4624
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_emailLabel") }),
4625
- /* @__PURE__ */ jsx30(Text29, { children: license.customerEmail })
4523
+ license && /* @__PURE__ */ jsxs30(Fragment5, { children: [
4524
+ /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4525
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_emailLabel") }),
4526
+ /* @__PURE__ */ jsx31(Text30, { children: license.customerEmail })
4626
4527
  ] }),
4627
- /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4628
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_nameLabel") }),
4629
- /* @__PURE__ */ jsx30(Text29, { children: license.customerName })
4528
+ /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4529
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_nameLabel") }),
4530
+ /* @__PURE__ */ jsx31(Text30, { children: license.customerName })
4630
4531
  ] }),
4631
- /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4632
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_planLabel") }),
4633
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: "Pro" })
4532
+ /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4533
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_planLabel") }),
4534
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.success, bold: true, children: "Pro" })
4634
4535
  ] }),
4635
- /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4636
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_keyLabel") }),
4637
- /* @__PURE__ */ jsx30(Text29, { children: maskKey(license.key) })
4536
+ /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4537
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_keyLabel") }),
4538
+ /* @__PURE__ */ jsx31(Text30, { children: maskKey(license.key) })
4638
4539
  ] }),
4639
- license.expiresAt && /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4640
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_expiresLabel") }),
4641
- /* @__PURE__ */ jsx30(Text29, { children: formatDate(license.expiresAt) })
4540
+ license.expiresAt && /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4541
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_expiresLabel") }),
4542
+ /* @__PURE__ */ jsx31(Text30, { children: formatDate(license.expiresAt) })
4642
4543
  ] }),
4643
- /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4644
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_activatedLabel") }),
4645
- /* @__PURE__ */ jsx30(Text29, { children: formatDate(license.activatedAt) })
4544
+ /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4545
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_activatedLabel") }),
4546
+ /* @__PURE__ */ jsx31(Text30, { children: formatDate(license.activatedAt) })
4646
4547
  ] })
4647
4548
  ] }),
4648
- status === "free" && /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", marginTop: 2, borderStyle: "round", borderColor: COLORS.brand, paddingX: 2, paddingY: 1, children: [
4649
- /* @__PURE__ */ jsxs29(Text29, { bold: true, color: COLORS.brand, children: [
4549
+ status === "free" && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: SPACING.sm, borderStyle: "round", borderColor: COLORS.brand, paddingX: SPACING.sm, paddingY: SPACING.xs, children: [
4550
+ /* @__PURE__ */ jsxs30(Text30, { bold: true, color: COLORS.brand, children: [
4650
4551
  "\u2B50",
4651
4552
  " ",
4652
4553
  t("account_upgradeTitle")
4653
4554
  ] }),
4654
- /* @__PURE__ */ jsx30(Text29, { children: " " }),
4655
- /* @__PURE__ */ jsx30(Text29, { children: t("account_unlockDesc") }),
4656
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.info, bold: true, children: t("account_pricing") }),
4657
- /* @__PURE__ */ jsx30(Text29, { children: " " }),
4658
- /* @__PURE__ */ jsxs29(Text29, { color: COLORS.muted, children: [
4555
+ /* @__PURE__ */ jsx31(Text30, { children: " " }),
4556
+ /* @__PURE__ */ jsx31(Text30, { children: t("account_unlockDesc") }),
4557
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.info, bold: true, children: t("account_pricing") }),
4558
+ /* @__PURE__ */ jsx31(Text30, { children: " " }),
4559
+ /* @__PURE__ */ jsxs30(Text30, { color: COLORS.muted, children: [
4659
4560
  t("upgrade_buyAt"),
4660
4561
  " ",
4661
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, bold: true, children: t("upgrade_buyUrl") })
4562
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.sky, bold: true, children: t("upgrade_buyUrl") })
4662
4563
  ] }),
4663
- /* @__PURE__ */ jsxs29(Text29, { color: COLORS.muted, children: [
4564
+ /* @__PURE__ */ jsxs30(Text30, { color: COLORS.muted, children: [
4664
4565
  t("account_runActivate"),
4665
4566
  " ",
4666
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.success, bold: true, children: t("account_activateCmd") })
4567
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.success, bold: true, children: t("account_activateCmd") })
4667
4568
  ] })
4668
4569
  ] }),
4669
- status === "expired" && /* @__PURE__ */ jsx30(Box28, { marginTop: 1, children: /* @__PURE__ */ jsx30(Box28, { borderStyle: "round", borderColor: COLORS.error, paddingX: 2, paddingY: 0, children: /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: t("account_licenseExpired") }) }) }),
4670
- deactivating && /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, children: t("account_deactivating") }),
4671
- deactivateError && /* @__PURE__ */ jsx30(Text29, { color: COLORS.error, children: deactivateError })
4570
+ status === "expired" && /* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx31(Box29, { borderStyle: "round", borderColor: COLORS.error, paddingX: SPACING.sm, paddingY: SPACING.none, children: /* @__PURE__ */ jsx31(Text30, { color: COLORS.error, children: t("account_licenseExpired") }) }) }),
4571
+ deactivating && /* @__PURE__ */ jsx31(Text30, { color: COLORS.sky, children: t("account_deactivating") }),
4572
+ deactivateError && /* @__PURE__ */ jsx31(Text30, { color: COLORS.error, children: deactivateError })
4672
4573
  ] }),
4673
- /* @__PURE__ */ jsx30(Box28, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: promoMode ? /* @__PURE__ */ jsxs29(Box28, { flexDirection: "column", gap: 1, children: [
4674
- /* @__PURE__ */ jsx30(Text29, { bold: true, color: COLORS.gold, children: t("account_promoTitle") }),
4675
- promoLoading ? /* @__PURE__ */ jsx30(Text29, { color: COLORS.sky, children: t("account_promoValidating") }) : /* @__PURE__ */ jsxs29(Box28, { gap: 1, children: [
4676
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.muted, children: t("account_promoLabel") }),
4677
- /* @__PURE__ */ jsx30(
4574
+ /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", marginTop: SPACING.xs, paddingLeft: SPACING.sm, children: promoMode ? /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", gap: SPACING.xs, children: [
4575
+ /* @__PURE__ */ jsx31(Text30, { bold: true, color: COLORS.gold, children: t("account_promoTitle") }),
4576
+ promoLoading ? /* @__PURE__ */ jsx31(Text30, { color: COLORS.sky, children: t("account_promoValidating") }) : /* @__PURE__ */ jsxs30(Box29, { gap: SPACING.xs, children: [
4577
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.muted, children: t("account_promoLabel") }),
4578
+ /* @__PURE__ */ jsx31(
4678
4579
  TextInput5,
4679
4580
  {
4680
4581
  defaultValue: "",
@@ -4698,27 +4599,29 @@ function AccountView() {
4698
4599
  }
4699
4600
  )
4700
4601
  ] }),
4701
- promoResult && /* @__PURE__ */ jsx30(ResultBanner, { status: promoResult.success ? "success" : "error", message: promoResult.message }),
4702
- /* @__PURE__ */ jsx30(Text29, { color: COLORS.textSecondary, dimColor: true, children: t("account_promoEsc") })
4703
- ] }) : /* @__PURE__ */ jsx30(Text29, { color: COLORS.textSecondary, children: t("account_promoHint") }) }),
4704
- /* @__PURE__ */ jsx30(Box28, { marginTop: 1, children: /* @__PURE__ */ jsxs29(Text29, { color: COLORS.textSecondary, children: [
4705
- status === "pro" ? `d ${t("hint_deactivate")}` : "",
4602
+ promoResult && /* @__PURE__ */ jsx31(ResultBanner, { status: promoResult.success ? "success" : "error", message: promoResult.message }),
4603
+ /* @__PURE__ */ jsx31(Text30, { color: COLORS.textSecondary, dimColor: true, children: t("account_promoEsc") })
4604
+ ] }) : /* @__PURE__ */ jsx31(Text30, { color: COLORS.textSecondary, children: t("account_promoHint") }) }),
4605
+ /* @__PURE__ */ jsx31(Box29, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
4606
+ status === "pro" || status === "team" ? `d ${t("hint_deactivate")} ` : "",
4607
+ status === "pro" || status === "team" || status === "expired" ? `v ${t("hint_revalidate")} ` : "",
4608
+ revalidating ? t("account_revalidating") : "",
4706
4609
  " ",
4707
- t("app_version", { version: "0.6.1" })
4610
+ t("app_version", { version: "0.7.0" })
4708
4611
  ] }) })
4709
4612
  ] });
4710
4613
  }
4711
4614
 
4712
4615
  // src/views/rollback.tsx
4713
- import { useCallback as useCallback3, useEffect as useEffect15, useRef as useRef8, useState as useState13 } from "react";
4714
- import { Box as Box29, Text as Text30, useInput as useInput14 } from "ink";
4616
+ import { useCallback as useCallback3, useEffect as useEffect16, useRef as useRef8, useState as useState13 } from "react";
4617
+ import { Box as Box30, Text as Text31, useInput as useInput16 } from "ink";
4715
4618
 
4716
4619
  // src/stores/rollback-store.ts
4717
4620
  import { create as create12 } from "zustand";
4718
4621
 
4719
4622
  // src/lib/rollback/rollback-engine.ts
4720
4623
  import { readdir as readdir2 } from "fs/promises";
4721
- import { join as join4 } from "path";
4624
+ import { join as join3 } from "path";
4722
4625
  async function detectStrategy(name, targetVersion, packageType) {
4723
4626
  if (packageType === "cask") {
4724
4627
  return { strategy: "pin-only" };
@@ -4734,7 +4637,7 @@ async function detectStrategy(name, targetVersion, packageType) {
4734
4637
  }
4735
4638
  try {
4736
4639
  const brewCache = (await execBrew(["--cache"])).trim();
4737
- const downloadsDir = join4(brewCache, "downloads");
4640
+ const downloadsDir = join3(brewCache, "downloads");
4738
4641
  const entries = await readdir2(downloadsDir);
4739
4642
  const found = entries.some(
4740
4643
  (entry) => entry.includes(name) && entry.includes(targetVersion)
@@ -4921,7 +4824,7 @@ var useRollbackStore = create12((set) => ({
4921
4824
  }));
4922
4825
 
4923
4826
  // src/views/rollback.tsx
4924
- import { jsx as jsx31, jsxs as jsxs30 } from "react/jsx-runtime";
4827
+ import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
4925
4828
  function strategyLabel(action) {
4926
4829
  switch (action.strategy) {
4927
4830
  case "versioned-formula":
@@ -4949,48 +4852,48 @@ function actionPrefix(action) {
4949
4852
  }
4950
4853
  function PlanView({ plan }) {
4951
4854
  const executableCount = plan.actions.filter((a) => a.strategy !== "unavailable" && a.action !== "remove").length;
4952
- return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: 1, children: [
4953
- /* @__PURE__ */ jsxs30(Box29, { marginBottom: 1, children: [
4954
- /* @__PURE__ */ jsxs30(Text30, { color: COLORS.text, bold: true, children: [
4855
+ return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
4856
+ /* @__PURE__ */ jsxs31(Box30, { marginBottom: SPACING.xs, children: [
4857
+ /* @__PURE__ */ jsxs31(Text31, { color: COLORS.text, bold: true, children: [
4955
4858
  plan.snapshotLabel,
4956
4859
  " "
4957
4860
  ] }),
4958
- /* @__PURE__ */ jsx31(Text30, { color: COLORS.textSecondary, children: plan.snapshotDate })
4861
+ /* @__PURE__ */ jsx32(Text31, { color: COLORS.textSecondary, children: plan.snapshotDate })
4959
4862
  ] }),
4960
- plan.actions.length === 0 && /* @__PURE__ */ jsx31(ResultBanner, { status: "success", message: t("rollback_diff_empty") }),
4961
- plan.actions.map((a) => /* @__PURE__ */ jsxs30(Box29, { children: [
4962
- /* @__PURE__ */ jsxs30(Text30, { color: actionColor(a), children: [
4863
+ plan.actions.length === 0 && /* @__PURE__ */ jsx32(ResultBanner, { status: "success", message: t("rollback_diff_empty") }),
4864
+ plan.actions.map((a) => /* @__PURE__ */ jsxs31(Box30, { children: [
4865
+ /* @__PURE__ */ jsxs31(Text31, { color: actionColor(a), children: [
4963
4866
  actionPrefix(a),
4964
4867
  " "
4965
4868
  ] }),
4966
- /* @__PURE__ */ jsx31(Text30, { color: actionColor(a), bold: true, children: a.packageName }),
4967
- a.fromVersion !== "" && a.toVersion !== "" && /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
4869
+ /* @__PURE__ */ jsx32(Text31, { color: actionColor(a), bold: true, children: a.packageName }),
4870
+ a.fromVersion !== "" && a.toVersion !== "" && /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
4968
4871
  " ",
4969
4872
  a.fromVersion,
4970
4873
  " \u2192 ",
4971
4874
  a.toVersion
4972
4875
  ] }),
4973
- a.fromVersion === "" && a.toVersion !== "" && /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
4876
+ a.fromVersion === "" && a.toVersion !== "" && /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
4974
4877
  " install ",
4975
4878
  a.toVersion
4976
4879
  ] }),
4977
- a.fromVersion !== "" && a.toVersion === "" && /* @__PURE__ */ jsx31(Text30, { color: COLORS.textSecondary, children: " remove" }),
4978
- /* @__PURE__ */ jsxs30(Text30, { color: COLORS.muted, dimColor: true, children: [
4880
+ a.fromVersion !== "" && a.toVersion === "" && /* @__PURE__ */ jsx32(Text31, { color: COLORS.textSecondary, children: " remove" }),
4881
+ /* @__PURE__ */ jsxs31(Text31, { color: COLORS.muted, dimColor: true, children: [
4979
4882
  " [",
4980
4883
  strategyLabel(a),
4981
4884
  "]"
4982
4885
  ] })
4983
4886
  ] }, a.packageName + a.action)),
4984
- plan.warnings.map((w) => /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsxs30(Text30, { color: COLORS.warning, children: [
4887
+ plan.warnings.map((w) => /* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.warning, children: [
4985
4888
  "\u26A0 ",
4986
4889
  w
4987
4890
  ] }) }, w)),
4988
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: plan.canExecute ? /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
4891
+ /* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: plan.canExecute ? /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
4989
4892
  "enter:",
4990
4893
  t("rollback_confirm", { count: String(executableCount) }),
4991
4894
  " esc:",
4992
4895
  t("hint_back")
4993
- ] }) : /* @__PURE__ */ jsxs30(Text30, { color: COLORS.muted, children: [
4896
+ ] }) : /* @__PURE__ */ jsxs31(Text31, { color: COLORS.muted, children: [
4994
4897
  t("rollback_strategy_unavailable"),
4995
4898
  " esc:",
4996
4899
  t("hint_back")
@@ -5007,14 +4910,14 @@ function RollbackView() {
5007
4910
  const [streamError, setStreamError] = useState13(null);
5008
4911
  const generatorRef = useRef8(null);
5009
4912
  const mountedRef = useRef8(true);
5010
- useEffect15(() => {
4913
+ useEffect16(() => {
5011
4914
  mountedRef.current = true;
5012
4915
  return () => {
5013
4916
  mountedRef.current = false;
5014
4917
  void generatorRef.current?.return(void 0);
5015
4918
  };
5016
4919
  }, []);
5017
- useEffect15(() => {
4920
+ useEffect16(() => {
5018
4921
  void fetchSnapshots(isPro());
5019
4922
  }, []);
5020
4923
  const runRollback = useCallback3(async (p) => {
@@ -5041,7 +4944,7 @@ function RollbackView() {
5041
4944
  }
5042
4945
  }
5043
4946
  }, [isPro]);
5044
- useInput14((input, key) => {
4947
+ useInput16((input, key) => {
5045
4948
  if (phase === "executing") return;
5046
4949
  if (phase === "result") {
5047
4950
  if (key.escape || input === "r") {
@@ -5074,21 +4977,21 @@ function RollbackView() {
5074
4977
  void fetchSnapshots(isPro());
5075
4978
  }
5076
4979
  });
5077
- if (loading) return /* @__PURE__ */ jsx31(Loading, { message: t("rollback_select_snapshot") });
5078
- if (error) return /* @__PURE__ */ jsx31(ErrorMessage, { message: error });
4980
+ if (loading) return /* @__PURE__ */ jsx32(Loading, { message: t("rollback_select_snapshot") });
4981
+ if (error) return /* @__PURE__ */ jsx32(ErrorMessage, { message: error });
5079
4982
  if (phase === "executing") {
5080
- return /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", children: /* @__PURE__ */ jsx31(ProgressLog, { lines: streamLines, isRunning: streamRunning, title: t("rollback_executing") }) });
4983
+ return /* @__PURE__ */ jsx32(Box30, { flexDirection: "column", children: /* @__PURE__ */ jsx32(ProgressLog, { lines: streamLines, isRunning: streamRunning, title: t("rollback_executing") }) });
5081
4984
  }
5082
4985
  if (phase === "result") {
5083
- return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: 1, children: [
5084
- /* @__PURE__ */ jsx31(
4986
+ return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
4987
+ /* @__PURE__ */ jsx32(
5085
4988
  ResultBanner,
5086
4989
  {
5087
4990
  status: streamError ? "error" : "success",
5088
4991
  message: streamError ? t("rollback_error", { error: streamError }) : t("rollback_success")
5089
4992
  }
5090
4993
  ),
5091
- /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
4994
+ /* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
5092
4995
  "r:",
5093
4996
  t("hint_refresh"),
5094
4997
  " esc:",
@@ -5096,18 +4999,18 @@ function RollbackView() {
5096
4999
  ] }) })
5097
5000
  ] });
5098
5001
  }
5099
- return /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
5100
- /* @__PURE__ */ jsx31(SectionHeader, { emoji: "\u23EA", title: t("rollback_title"), gradient: GRADIENTS.gold }),
5101
- snapshots.length === 0 && /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(ResultBanner, { status: "info", message: t("rollback_no_snapshots") }) }),
5102
- phase === "list" && snapshots.length > 0 && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", marginTop: 1, children: [
5103
- /* @__PURE__ */ jsx31(Text30, { color: COLORS.textSecondary, dimColor: true, children: t("rollback_select_snapshot") }),
5104
- /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", marginTop: 1, children: snapshots.map((s, i) => /* @__PURE__ */ jsxs30(SelectableRow, { isCurrent: i === cursor, children: [
5105
- /* @__PURE__ */ jsx31(Text30, { bold: i === cursor, color: i === cursor ? COLORS.text : COLORS.muted, children: s.label ?? t("rollback_snapshot_auto") }),
5106
- /* @__PURE__ */ jsxs30(Text30, { color: COLORS.textSecondary, children: [
5002
+ return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
5003
+ /* @__PURE__ */ jsx32(SectionHeader, { emoji: "\u23EA", title: t("rollback_title"), gradient: GRADIENTS.gold }),
5004
+ snapshots.length === 0 && /* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx32(ResultBanner, { status: "info", message: t("rollback_no_snapshots") }) }),
5005
+ phase === "list" && snapshots.length > 0 && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: [
5006
+ /* @__PURE__ */ jsx32(Text31, { color: COLORS.textSecondary, dimColor: true, children: t("rollback_select_snapshot") }),
5007
+ /* @__PURE__ */ jsx32(Box30, { flexDirection: "column", marginTop: SPACING.xs, children: snapshots.map((s, i) => /* @__PURE__ */ jsxs31(SelectableRow, { isCurrent: i === cursor, children: [
5008
+ /* @__PURE__ */ jsx32(Text31, { bold: i === cursor, color: i === cursor ? COLORS.text : COLORS.muted, children: s.label ?? t("rollback_snapshot_auto") }),
5009
+ /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
5107
5010
  " \u2014 ",
5108
5011
  new Date(s.capturedAt).toLocaleString()
5109
5012
  ] }),
5110
- /* @__PURE__ */ jsxs30(Text30, { color: COLORS.muted, dimColor: true, children: [
5013
+ /* @__PURE__ */ jsxs31(Text31, { color: COLORS.muted, dimColor: true, children: [
5111
5014
  " ",
5112
5015
  "(",
5113
5016
  tp("packages", s.formulae.length + s.casks.length),
@@ -5115,12 +5018,12 @@ function RollbackView() {
5115
5018
  ] })
5116
5019
  ] }, s.capturedAt)) })
5117
5020
  ] }),
5118
- phase === "plan" && /* @__PURE__ */ jsxs30(Box29, { flexDirection: "column", children: [
5119
- planLoading && /* @__PURE__ */ jsx31(Loading, { message: t("rollback_capturing") }),
5120
- planError && /* @__PURE__ */ jsx31(ErrorMessage, { message: planError }),
5121
- plan && !planLoading && /* @__PURE__ */ jsx31(PlanView, { plan })
5021
+ phase === "plan" && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
5022
+ planLoading && /* @__PURE__ */ jsx32(Loading, { message: t("rollback_capturing") }),
5023
+ planError && /* @__PURE__ */ jsx32(ErrorMessage, { message: planError }),
5024
+ plan && !planLoading && /* @__PURE__ */ jsx32(PlanView, { plan })
5122
5025
  ] }),
5123
- phase === "confirm" && plan && /* @__PURE__ */ jsx31(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx31(
5026
+ phase === "confirm" && plan && /* @__PURE__ */ jsx32(Box30, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx32(
5124
5027
  ConfirmDialog,
5125
5028
  {
5126
5029
  message: t("rollback_confirm", {
@@ -5134,47 +5037,47 @@ function RollbackView() {
5134
5037
  }
5135
5038
 
5136
5039
  // src/views/brewfile.tsx
5137
- import { useCallback as useCallback4, useEffect as useEffect16, useRef as useRef9, useState as useState14 } from "react";
5138
- import { Box as Box30, Text as Text31, useInput as useInput15 } from "ink";
5040
+ import { useCallback as useCallback4, useEffect as useEffect17, useRef as useRef9, useState as useState14 } from "react";
5041
+ import { Box as Box31, Text as Text32, useInput as useInput17 } from "ink";
5139
5042
  import { TextInput as TextInput6 } from "@inkjs/ui";
5140
- import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
5043
+ import { jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
5141
5044
  function DriftScore({ score }) {
5142
5045
  const color = score >= 80 ? COLORS.success : score >= 50 ? COLORS.warning : COLORS.error;
5143
5046
  const bars = Math.round(score / 10);
5144
5047
  const filled = "\u2593".repeat(bars);
5145
5048
  const empty = "\u2591".repeat(10 - bars);
5146
- return /* @__PURE__ */ jsxs31(Box30, { children: [
5147
- /* @__PURE__ */ jsxs31(Text31, { color, children: [
5049
+ return /* @__PURE__ */ jsxs32(Box31, { children: [
5050
+ /* @__PURE__ */ jsxs32(Text32, { color, children: [
5148
5051
  filled,
5149
5052
  empty
5150
5053
  ] }),
5151
- /* @__PURE__ */ jsxs31(Text31, { color, bold: true, children: [
5054
+ /* @__PURE__ */ jsxs32(Text32, { color, bold: true, children: [
5152
5055
  " ",
5153
5056
  score,
5154
5057
  "% "
5155
5058
  ] }),
5156
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.textSecondary, children: t("brewfile_compliant") })
5059
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("brewfile_compliant") })
5157
5060
  ] });
5158
5061
  }
5159
5062
  function DriftSummary({ drift }) {
5160
- return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 1, children: [
5161
- drift.missingPackages.length > 0 && /* @__PURE__ */ jsxs31(Box30, { children: [
5162
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.error, children: "\u25CF " }),
5163
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.error, children: t("brewfile_drift_missing", { count: drift.missingPackages.length }) }),
5164
- /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
5063
+ return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
5064
+ drift.missingPackages.length > 0 && /* @__PURE__ */ jsxs32(Box31, { children: [
5065
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.error, children: "\u25CF " }),
5066
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.error, children: t("brewfile_drift_missing", { count: drift.missingPackages.length }) }),
5067
+ /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5165
5068
  ": " + drift.missingPackages.slice(0, 3).join(", "),
5166
5069
  drift.missingPackages.length > 3 ? "..." : ""
5167
5070
  ] })
5168
5071
  ] }),
5169
- drift.extraPackages.length > 0 && /* @__PURE__ */ jsxs31(Box30, { children: [
5170
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.warning, children: "\u25CF " }),
5171
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.warning, children: t("brewfile_drift_extra", { count: drift.extraPackages.length }) })
5072
+ drift.extraPackages.length > 0 && /* @__PURE__ */ jsxs32(Box31, { children: [
5073
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.warning, children: "\u25CF " }),
5074
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.warning, children: t("brewfile_drift_extra", { count: drift.extraPackages.length }) })
5172
5075
  ] }),
5173
- drift.wrongVersions.length > 0 && /* @__PURE__ */ jsxs31(Box30, { children: [
5174
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.info, children: "\u25CF " }),
5175
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.info, children: t("brewfile_drift_wrong", { count: drift.wrongVersions.length }) })
5076
+ drift.wrongVersions.length > 0 && /* @__PURE__ */ jsxs32(Box31, { children: [
5077
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.info, children: "\u25CF " }),
5078
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.info, children: t("brewfile_drift_wrong", { count: drift.wrongVersions.length }) })
5176
5079
  ] }),
5177
- drift.missingPackages.length === 0 && drift.extraPackages.length === 0 && drift.wrongVersions.length === 0 && /* @__PURE__ */ jsx32(ResultBanner, { status: "success", message: "\u2713 System is in sync with Brewfile" })
5080
+ drift.missingPackages.length === 0 && drift.extraPackages.length === 0 && drift.wrongVersions.length === 0 && /* @__PURE__ */ jsx33(ResultBanner, { status: "success", message: t("brewfile_in_sync") })
5178
5081
  ] });
5179
5082
  }
5180
5083
  function BrewfileView() {
@@ -5187,7 +5090,7 @@ function BrewfileView() {
5187
5090
  const [resultMessage, setResultMessage] = useState14("");
5188
5091
  const generatorRef = useRef9(null);
5189
5092
  const mountedRef = useRef9(true);
5190
- useEffect16(() => {
5093
+ useEffect17(() => {
5191
5094
  mountedRef.current = true;
5192
5095
  void load();
5193
5096
  return () => {
@@ -5225,8 +5128,9 @@ function BrewfileView() {
5225
5128
  }
5226
5129
  }
5227
5130
  }, [schema, isPro]);
5228
- useInput15((input, key) => {
5131
+ useInput17((input, key) => {
5229
5132
  if (phase === "reconciling") return;
5133
+ if (phase === "confirming-reconcile") return;
5230
5134
  if (phase === "result") {
5231
5135
  if (key.escape || input === "r") {
5232
5136
  setPhase("overview");
@@ -5246,24 +5150,41 @@ function BrewfileView() {
5246
5150
  if (input === "c") {
5247
5151
  const needsReconcile = drift && (drift.missingPackages.length > 0 || drift.wrongVersions.length > 0);
5248
5152
  if (needsReconcile) {
5249
- void startReconcile();
5153
+ setPhase("confirming-reconcile");
5250
5154
  }
5251
5155
  return;
5252
5156
  }
5253
5157
  if (key.escape) {
5254
5158
  }
5255
5159
  });
5256
- if (loading) return /* @__PURE__ */ jsx32(Loading, { message: t("loading_default") });
5257
- if (error) return /* @__PURE__ */ jsx32(ErrorMessage, { message: error });
5160
+ if (loading) return /* @__PURE__ */ jsx33(Loading, { message: t("loading_default") });
5161
+ if (error) return /* @__PURE__ */ jsx33(ErrorMessage, { message: error });
5162
+ if (phase === "confirming-reconcile" && drift) {
5163
+ return /* @__PURE__ */ jsx33(
5164
+ ConfirmDialog,
5165
+ {
5166
+ message: t("confirm_brewfile_reconcile", {
5167
+ missing: String(drift.missingPackages.length),
5168
+ wrongVer: String(drift.wrongVersions.length)
5169
+ }),
5170
+ onConfirm: () => {
5171
+ void startReconcile();
5172
+ },
5173
+ onCancel: () => {
5174
+ setPhase("overview");
5175
+ }
5176
+ }
5177
+ );
5178
+ }
5258
5179
  if (phase === "creating") {
5259
- return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 1, children: [
5260
- /* @__PURE__ */ jsx32(SectionHeader, { emoji: "\u{1F4E6}", title: t("brewfile_title"), gradient: GRADIENTS.ocean }),
5261
- /* @__PURE__ */ jsxs31(Box30, { marginTop: 1, children: [
5262
- /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
5180
+ return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
5181
+ /* @__PURE__ */ jsx33(SectionHeader, { emoji: "\u{1F4E6}", title: t("brewfile_title"), gradient: GRADIENTS.ocean }),
5182
+ /* @__PURE__ */ jsxs32(Box31, { marginTop: SPACING.xs, children: [
5183
+ /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5263
5184
  t("brewfile_create_name"),
5264
5185
  " "
5265
5186
  ] }),
5266
- /* @__PURE__ */ jsx32(
5187
+ /* @__PURE__ */ jsx33(
5267
5188
  TextInput6,
5268
5189
  {
5269
5190
  defaultValue: "My Environment",
@@ -5279,7 +5200,7 @@ function BrewfileView() {
5279
5200
  ] });
5280
5201
  }
5281
5202
  if (phase === "reconciling") {
5282
- return /* @__PURE__ */ jsx32(Box30, { flexDirection: "column", children: /* @__PURE__ */ jsx32(
5203
+ return /* @__PURE__ */ jsx33(Box31, { flexDirection: "column", children: /* @__PURE__ */ jsx33(
5283
5204
  ProgressLog,
5284
5205
  {
5285
5206
  lines: streamLines,
@@ -5289,15 +5210,15 @@ function BrewfileView() {
5289
5210
  ) });
5290
5211
  }
5291
5212
  if (phase === "result") {
5292
- return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 1, children: [
5293
- /* @__PURE__ */ jsx32(
5213
+ return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
5214
+ /* @__PURE__ */ jsx33(
5294
5215
  ResultBanner,
5295
5216
  {
5296
5217
  status: streamError ? "error" : "success",
5297
5218
  message: resultMessage
5298
5219
  }
5299
5220
  ),
5300
- /* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
5221
+ /* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5301
5222
  "r:",
5302
5223
  t("hint_refresh"),
5303
5224
  " esc:",
@@ -5305,34 +5226,34 @@ function BrewfileView() {
5305
5226
  ] }) })
5306
5227
  ] });
5307
5228
  }
5308
- return /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", children: [
5309
- /* @__PURE__ */ jsx32(SectionHeader, { emoji: "\u{1F4E6}", title: t("brewfile_title"), gradient: GRADIENTS.ocean }),
5310
- schema === null ? /* @__PURE__ */ jsxs31(Box30, { marginTop: 1, flexDirection: "column", children: [
5311
- /* @__PURE__ */ jsx32(ResultBanner, { status: "info", message: t("brewfile_no_brewfile") }),
5312
- /* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
5229
+ return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
5230
+ /* @__PURE__ */ jsx33(SectionHeader, { emoji: "\u{1F4E6}", title: t("brewfile_title"), gradient: GRADIENTS.ocean }),
5231
+ schema === null ? /* @__PURE__ */ jsxs32(Box31, { marginTop: SPACING.xs, flexDirection: "column", children: [
5232
+ /* @__PURE__ */ jsx33(ResultBanner, { status: "info", message: t("brewfile_no_brewfile") }),
5233
+ /* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5313
5234
  "n:",
5314
5235
  t("hint_new")
5315
5236
  ] }) })
5316
- ] }) : /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 1, children: [
5317
- /* @__PURE__ */ jsxs31(Box30, { gap: 2, children: [
5318
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.text, bold: true, children: schema.meta.name }),
5319
- schema.meta.description && /* @__PURE__ */ jsx32(Text31, { color: COLORS.textSecondary, children: schema.meta.description }),
5320
- schema.strictMode && /* @__PURE__ */ jsxs31(Text31, { color: COLORS.warning, children: [
5237
+ ] }) : /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
5238
+ /* @__PURE__ */ jsxs32(Box31, { gap: SPACING.sm, children: [
5239
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.text, bold: true, children: schema.meta.name }),
5240
+ schema.meta.description && /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: schema.meta.description }),
5241
+ schema.strictMode && /* @__PURE__ */ jsxs32(Text32, { color: COLORS.warning, children: [
5321
5242
  "[",
5322
5243
  t("brewfile_strict_mode"),
5323
5244
  "]"
5324
5245
  ] })
5325
5246
  ] }),
5326
- /* @__PURE__ */ jsxs31(Box30, { gap: 3, marginTop: 1, children: [
5327
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.sky, children: t("brewfile_formulae_count", { count: schema.formulae.length }) }),
5328
- /* @__PURE__ */ jsx32(Text31, { color: COLORS.teal, children: t("brewfile_casks_count", { count: schema.casks.length }) })
5247
+ /* @__PURE__ */ jsxs32(Box31, { gap: SPACING.md, marginTop: SPACING.xs, children: [
5248
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.sky, children: t("brewfile_formulae_count", { count: schema.formulae.length }) }),
5249
+ /* @__PURE__ */ jsx33(Text32, { color: COLORS.teal, children: t("brewfile_casks_count", { count: schema.casks.length }) })
5329
5250
  ] }),
5330
- driftLoading && /* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text31, { color: COLORS.muted, children: "Computing drift..." }) }),
5331
- drift && !driftLoading && /* @__PURE__ */ jsxs31(Box30, { flexDirection: "column", marginTop: 1, children: [
5332
- /* @__PURE__ */ jsx32(DriftScore, { score: drift.score }),
5333
- /* @__PURE__ */ jsx32(DriftSummary, { drift })
5251
+ driftLoading && /* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.muted, children: t("brewfile_computing_drift") }) }),
5252
+ drift && !driftLoading && /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: SPACING.xs, children: [
5253
+ /* @__PURE__ */ jsx33(DriftScore, { score: drift.score }),
5254
+ /* @__PURE__ */ jsx33(DriftSummary, { drift })
5334
5255
  ] }),
5335
- /* @__PURE__ */ jsx32(Box30, { marginTop: 1, children: /* @__PURE__ */ jsxs31(Text31, { color: COLORS.textSecondary, children: [
5256
+ /* @__PURE__ */ jsx33(Box31, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5336
5257
  "r:",
5337
5258
  t("hint_refresh"),
5338
5259
  drift && (drift.missingPackages.length > 0 || drift.wrongVersions.length > 0) ? ` c:${t("hint_reconcile")}` : "",
@@ -5345,9 +5266,9 @@ function BrewfileView() {
5345
5266
  }
5346
5267
 
5347
5268
  // src/views/sync.tsx
5348
- import { useCallback as useCallback5, useEffect as useEffect17, useState as useState15 } from "react";
5349
- import { Box as Box31, Text as Text32, useInput as useInput16 } from "ink";
5350
- import { Fragment as Fragment6, jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
5269
+ import { useCallback as useCallback5, useEffect as useEffect18, useState as useState15 } from "react";
5270
+ import { Box as Box32, Text as Text33, useInput as useInput18 } from "ink";
5271
+ import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs33 } from "react/jsx-runtime";
5351
5272
  function OverviewSection({
5352
5273
  config,
5353
5274
  lastResult,
@@ -5357,34 +5278,34 @@ function OverviewSection({
5357
5278
  }) {
5358
5279
  const hasConflicts = conflicts.length > 0;
5359
5280
  const showComplianceHint = !hasConflicts && !!lastResult?.success;
5360
- return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: 1, children: [
5361
- config ? /* @__PURE__ */ jsxs32(Fragment6, { children: [
5362
- /* @__PURE__ */ jsx33(Box31, { marginBottom: 1, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_machine", { name: config.machineName }) }) }),
5363
- config.lastSync && /* @__PURE__ */ jsx33(Box31, { marginBottom: 1, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_last_sync", { date: new Date(config.lastSync).toLocaleString() }) }) }),
5364
- hasConflicts ? /* @__PURE__ */ jsx33(
5281
+ return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
5282
+ config ? /* @__PURE__ */ jsxs33(Fragment6, { children: [
5283
+ /* @__PURE__ */ jsx34(Box32, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, children: t("sync_machine", { name: config.machineName }) }) }),
5284
+ config.lastSync && /* @__PURE__ */ jsx34(Box32, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, children: t("sync_last_sync", { date: new Date(config.lastSync).toLocaleString() }) }) }),
5285
+ hasConflicts ? /* @__PURE__ */ jsx34(
5365
5286
  ResultBanner,
5366
5287
  {
5367
5288
  status: "error",
5368
5289
  message: t("sync_status_conflict", { count: String(conflicts.length) })
5369
5290
  }
5370
- ) : lastResult?.success ? /* @__PURE__ */ jsx33(ResultBanner, { status: "success", message: t("sync_status_ok") }) : null
5371
- ] }) : /* @__PURE__ */ jsx33(Box31, { marginBottom: 1, children: /* @__PURE__ */ jsx33(Text32, { color: COLORS.textSecondary, children: t("sync_disabled") }) }),
5372
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5291
+ ) : lastResult?.success ? /* @__PURE__ */ jsx34(ResultBanner, { status: "success", message: t("sync_status_ok") }) : null
5292
+ ] }) : /* @__PURE__ */ jsx34(Box32, { marginBottom: SPACING.xs, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, children: t("sync_disabled") }) }),
5293
+ /* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
5373
5294
  "s",
5374
- /* @__PURE__ */ jsxs32(Text32, { color: COLORS.gold, children: [
5295
+ /* @__PURE__ */ jsxs33(Text33, { color: COLORS.gold, children: [
5375
5296
  ":",
5376
5297
  t("hint_sync")
5377
5298
  ] }),
5378
- hasConflicts && /* @__PURE__ */ jsxs32(Fragment6, { children: [
5299
+ hasConflicts && /* @__PURE__ */ jsxs33(Fragment6, { children: [
5379
5300
  " c",
5380
- /* @__PURE__ */ jsxs32(Text32, { color: COLORS.gold, children: [
5301
+ /* @__PURE__ */ jsxs33(Text33, { color: COLORS.gold, children: [
5381
5302
  ":",
5382
5303
  t("hint_conflict")
5383
5304
  ] })
5384
5305
  ] }),
5385
- showComplianceHint && /* @__PURE__ */ jsxs32(Fragment6, { children: [
5306
+ showComplianceHint && /* @__PURE__ */ jsxs33(Fragment6, { children: [
5386
5307
  " c",
5387
- /* @__PURE__ */ jsxs32(Text32, { color: COLORS.gold, children: [
5308
+ /* @__PURE__ */ jsxs33(Text33, { color: COLORS.gold, children: [
5388
5309
  ":",
5389
5310
  t("hint_check_compliance")
5390
5311
  ] })
@@ -5396,22 +5317,22 @@ function ConflictsList({
5396
5317
  entries,
5397
5318
  cursor
5398
5319
  }) {
5399
- return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: 1, children: [
5320
+ return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
5400
5321
  entries.map((entry, i) => {
5401
5322
  const { conflict, resolution } = entry;
5402
5323
  const isActive = i === cursor;
5403
- return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginBottom: 1, children: [
5404
- /* @__PURE__ */ jsxs32(SelectableRow, { isCurrent: isActive, children: [
5405
- /* @__PURE__ */ jsx33(Text32, { bold: true, color: isActive ? COLORS.text : COLORS.textSecondary, children: t("sync_conflict_title", { package: conflict.packageName }) }),
5406
- /* @__PURE__ */ jsxs32(Text32, { color: COLORS.muted, children: [
5324
+ return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom: SPACING.xs, children: [
5325
+ /* @__PURE__ */ jsxs33(SelectableRow, { isCurrent: isActive, children: [
5326
+ /* @__PURE__ */ jsx34(Text33, { bold: true, color: isActive ? COLORS.text : COLORS.textSecondary, children: t("sync_conflict_title", { package: conflict.packageName }) }),
5327
+ /* @__PURE__ */ jsxs33(Text33, { color: COLORS.muted, children: [
5407
5328
  " (",
5408
5329
  conflict.packageType,
5409
5330
  ")"
5410
5331
  ] })
5411
5332
  ] }),
5412
- /* @__PURE__ */ jsxs32(Box31, { marginLeft: 2, flexDirection: "column", children: [
5413
- /* @__PURE__ */ jsxs32(
5414
- Text32,
5333
+ /* @__PURE__ */ jsxs33(Box32, { marginLeft: SPACING.sm, flexDirection: "column", children: [
5334
+ /* @__PURE__ */ jsxs33(
5335
+ Text33,
5415
5336
  {
5416
5337
  color: resolution === "use-local" ? COLORS.success : COLORS.textSecondary,
5417
5338
  children: [
@@ -5421,8 +5342,8 @@ function ConflictsList({
5421
5342
  ]
5422
5343
  }
5423
5344
  ),
5424
- /* @__PURE__ */ jsxs32(
5425
- Text32,
5345
+ /* @__PURE__ */ jsxs33(
5346
+ Text33,
5426
5347
  {
5427
5348
  color: resolution === "use-remote" ? COLORS.success : COLORS.textSecondary,
5428
5349
  children: [
@@ -5435,12 +5356,17 @@ function ConflictsList({
5435
5356
  ] })
5436
5357
  ] }, `${conflict.packageName}-${conflict.remoteMachine}`);
5437
5358
  }),
5438
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5439
- "j/k:navegar l:",
5359
+ /* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
5360
+ "j/k:",
5361
+ t("hint_navigate"),
5362
+ " l:",
5440
5363
  t("sync_conflict_use_local"),
5441
5364
  " r:",
5442
5365
  t("sync_conflict_use_remote"),
5443
- " enter:aplicar"
5366
+ " enter:",
5367
+ t("hint_apply"),
5368
+ " esc:",
5369
+ t("hint_back")
5444
5370
  ] }) })
5445
5371
  ] });
5446
5372
  }
@@ -5452,10 +5378,10 @@ function SyncView() {
5452
5378
  const [syncError, setSyncError] = useState15(null);
5453
5379
  const [conflictEntries, setConflictEntries] = useState15([]);
5454
5380
  const [cursor, setCursor] = useState15(0);
5455
- useEffect17(() => {
5381
+ useEffect18(() => {
5456
5382
  void initialize(isPro());
5457
5383
  }, []);
5458
- useEffect17(() => {
5384
+ useEffect18(() => {
5459
5385
  if (conflicts.length > 0) {
5460
5386
  setConflictEntries(
5461
5387
  conflicts.map((c) => ({ conflict: c, resolution: "pending" }))
@@ -5486,8 +5412,9 @@ function SyncView() {
5486
5412
  await resolveConflicts(resolutions);
5487
5413
  setPhase("result");
5488
5414
  }, [conflictEntries, resolveConflicts]);
5489
- useInput16((input, key) => {
5415
+ useInput18((input, key) => {
5490
5416
  if (phase === "syncing") return;
5417
+ if (phase === "confirming-sync" || phase === "confirming-apply") return;
5491
5418
  if (phase === "result") {
5492
5419
  if (key.escape || input === "r") {
5493
5420
  setPhase("overview");
@@ -5522,12 +5449,13 @@ function SyncView() {
5522
5449
  return;
5523
5450
  }
5524
5451
  if (key.return) {
5525
- void handleApplyResolutions();
5452
+ const pending = conflictEntries.filter((e) => e.resolution === "pending");
5453
+ if (pending.length === 0) setPhase("confirming-apply");
5526
5454
  return;
5527
5455
  }
5528
5456
  }
5529
5457
  if (input === "s") {
5530
- void handleSyncNow();
5458
+ setPhase("confirming-sync");
5531
5459
  return;
5532
5460
  }
5533
5461
  if (input === "c" && conflicts.length > 0) {
@@ -5544,21 +5472,49 @@ function SyncView() {
5544
5472
  return;
5545
5473
  }
5546
5474
  });
5475
+ if (phase === "confirming-sync") {
5476
+ return /* @__PURE__ */ jsx34(
5477
+ ConfirmDialog,
5478
+ {
5479
+ message: t("confirm_sync_now"),
5480
+ onConfirm: () => {
5481
+ void handleSyncNow();
5482
+ },
5483
+ onCancel: () => {
5484
+ setPhase("overview");
5485
+ }
5486
+ }
5487
+ );
5488
+ }
5489
+ if (phase === "confirming-apply") {
5490
+ return /* @__PURE__ */ jsx34(
5491
+ ConfirmDialog,
5492
+ {
5493
+ message: t("confirm_sync_apply", { count: String(conflictEntries.length) }),
5494
+ onConfirm: () => {
5495
+ void handleApplyResolutions();
5496
+ },
5497
+ onCancel: () => {
5498
+ setPhase("conflicts");
5499
+ }
5500
+ }
5501
+ );
5502
+ }
5547
5503
  if (phase === "syncing" || loading) {
5548
- return /* @__PURE__ */ jsx33(Loading, { message: t("sync_syncing") });
5504
+ return /* @__PURE__ */ jsx34(Loading, { message: t("sync_syncing") });
5549
5505
  }
5550
5506
  if (phase === "result") {
5551
5507
  const isError = !!(syncError ?? error);
5552
- return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", marginTop: 1, children: [
5553
- /* @__PURE__ */ jsx33(SectionHeader, { emoji: "\u{1F504}", title: t("sync_title"), gradient: GRADIENTS.gold }),
5554
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(
5508
+ return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: SPACING.xs, children: [
5509
+ /* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F504}", title: t("sync_title"), gradient: GRADIENTS.gold }),
5510
+ /* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(
5555
5511
  ResultBanner,
5556
5512
  {
5557
5513
  status: isError ? "error" : "success",
5558
5514
  message: isError ? t("sync_error", { error: syncError ?? error ?? "" }) : t("sync_success")
5559
5515
  }
5560
5516
  ) }),
5561
- /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsxs32(Text32, { color: COLORS.textSecondary, children: [
5517
+ /* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
5562
5518
  "r:",
5563
5519
  t("hint_refresh"),
5564
5520
  " esc:",
@@ -5566,10 +5522,10 @@ function SyncView() {
5566
5522
  ] }) })
5567
5523
  ] });
5568
5524
  }
5569
- return /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
5570
- /* @__PURE__ */ jsx33(SectionHeader, { emoji: "\u{1F504}", title: t("sync_title"), gradient: GRADIENTS.gold }),
5571
- error && phase === "overview" && /* @__PURE__ */ jsx33(Box31, { marginTop: 1, children: /* @__PURE__ */ jsx33(ResultBanner, { status: "error", message: t("sync_error", { error }) }) }),
5572
- phase === "overview" && /* @__PURE__ */ jsx33(
5525
+ return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
5526
+ /* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F504}", title: t("sync_title"), gradient: GRADIENTS.gold }),
5527
+ error && phase === "overview" && /* @__PURE__ */ jsx34(Box32, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx34(ResultBanner, { status: "error", message: t("sync_error", { error }) }) }),
5528
+ phase === "overview" && /* @__PURE__ */ jsx34(
5573
5529
  OverviewSection,
5574
5530
  {
5575
5531
  config,
@@ -5582,16 +5538,16 @@ function SyncView() {
5582
5538
  }
5583
5539
  }
5584
5540
  ),
5585
- phase === "conflicts" && /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", children: [
5586
- /* @__PURE__ */ jsx33(SectionHeader, { emoji: "\u26A0", title: t("sync_status_conflict", { count: String(conflictEntries.length) }), gradient: GRADIENTS.gold }),
5587
- /* @__PURE__ */ jsx33(ConflictsList, { entries: conflictEntries, cursor })
5541
+ phase === "conflicts" && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
5542
+ /* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u26A0", title: t("sync_status_conflict", { count: String(conflictEntries.length) }), gradient: GRADIENTS.gold }),
5543
+ /* @__PURE__ */ jsx34(ConflictsList, { entries: conflictEntries, cursor })
5588
5544
  ] })
5589
5545
  ] });
5590
5546
  }
5591
5547
 
5592
5548
  // src/views/compliance.tsx
5593
- import { useCallback as useCallback6, useEffect as useEffect18, useRef as useRef10, useState as useState16 } from "react";
5594
- import { Box as Box32, Text as Text33, useInput as useInput17 } from "ink";
5549
+ import { useCallback as useCallback6, useEffect as useEffect19, useRef as useRef10, useState as useState16 } from "react";
5550
+ import { Box as Box33, Text as Text34, useInput as useInput19 } from "ink";
5595
5551
  import { TextInput as TextInput7 } from "@inkjs/ui";
5596
5552
 
5597
5553
  // src/lib/compliance/compliance-remediator.ts
@@ -5635,28 +5591,28 @@ async function* remediateViolations(violations, isPro) {
5635
5591
  }
5636
5592
 
5637
5593
  // src/views/compliance.tsx
5638
- import { join as join5 } from "path";
5639
- import { jsx as jsx34, jsxs as jsxs33 } from "react/jsx-runtime";
5594
+ import { join as join4 } from "path";
5595
+ import { jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
5640
5596
  function ComplianceScore({ report }) {
5641
5597
  const color = report.score >= 80 ? COLORS.success : report.score >= 50 ? COLORS.warning : COLORS.error;
5642
5598
  const bars = Math.round(report.score / 10);
5643
- return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom: 1, children: [
5644
- /* @__PURE__ */ jsxs33(Box32, { children: [
5645
- /* @__PURE__ */ jsxs33(Text33, { color, children: [
5599
+ return /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginBottom: SPACING.xs, children: [
5600
+ /* @__PURE__ */ jsxs34(Box33, { children: [
5601
+ /* @__PURE__ */ jsxs34(Text34, { color, children: [
5646
5602
  "\u2593".repeat(bars),
5647
5603
  "\u2591".repeat(10 - bars)
5648
5604
  ] }),
5649
- /* @__PURE__ */ jsxs33(Text33, { color, bold: true, children: [
5605
+ /* @__PURE__ */ jsxs34(Text34, { color, bold: true, children: [
5650
5606
  " ",
5651
5607
  report.score,
5652
5608
  "%"
5653
5609
  ] }),
5654
- /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
5610
+ /* @__PURE__ */ jsxs34(Text34, { color: COLORS.textSecondary, children: [
5655
5611
  " ",
5656
5612
  t("compliance_score", { score: String(report.score) })
5657
5613
  ] })
5658
5614
  ] }),
5659
- /* @__PURE__ */ jsxs33(Text33, { color: COLORS.muted, dimColor: true, children: [
5615
+ /* @__PURE__ */ jsxs34(Text34, { color: COLORS.muted, dimColor: true, children: [
5660
5616
  t("compliance_policy_name", { name: report.policyName }),
5661
5617
  " \xB7 ",
5662
5618
  t("compliance_machine", { name: report.machineName })
@@ -5666,31 +5622,31 @@ function ComplianceScore({ report }) {
5666
5622
  function ViolationItem({ violation }) {
5667
5623
  const color = violation.severity === "error" ? COLORS.error : COLORS.warning;
5668
5624
  const prefix = violation.severity === "error" ? "\u2717" : "\u26A0";
5669
- return /* @__PURE__ */ jsxs33(Box32, { marginBottom: 0, children: [
5670
- /* @__PURE__ */ jsxs33(Text33, { color, children: [
5625
+ return /* @__PURE__ */ jsxs34(Box33, { marginBottom: SPACING.none, children: [
5626
+ /* @__PURE__ */ jsxs34(Text34, { color, children: [
5671
5627
  prefix,
5672
5628
  " "
5673
5629
  ] }),
5674
- /* @__PURE__ */ jsx34(Text33, { color, children: violation.detail })
5630
+ /* @__PURE__ */ jsx35(Text34, { color, children: violation.detail })
5675
5631
  ] });
5676
5632
  }
5677
5633
  function ViolationList({ violations }) {
5678
5634
  const errors = violations.filter((v) => v.severity === "error");
5679
5635
  const warnings = violations.filter((v) => v.severity === "warning");
5680
- return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: 1, children: [
5681
- errors.length > 0 && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginBottom: 1, children: [
5682
- /* @__PURE__ */ jsxs33(Text33, { color: COLORS.error, bold: true, children: [
5636
+ return /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginTop: SPACING.xs, children: [
5637
+ errors.length > 0 && /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginBottom: SPACING.xs, children: [
5638
+ /* @__PURE__ */ jsxs34(Text34, { color: COLORS.error, bold: true, children: [
5683
5639
  t("compliance_violations", { count: String(errors.length) }),
5684
5640
  " (errors)"
5685
5641
  ] }),
5686
- errors.map((v) => /* @__PURE__ */ jsx34(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`))
5642
+ errors.map((v) => /* @__PURE__ */ jsx35(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`))
5687
5643
  ] }),
5688
- warnings.length > 0 && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
5689
- /* @__PURE__ */ jsxs33(Text33, { color: COLORS.warning, bold: true, children: [
5644
+ warnings.length > 0 && /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", children: [
5645
+ /* @__PURE__ */ jsxs34(Text34, { color: COLORS.warning, bold: true, children: [
5690
5646
  t("compliance_violations", { count: String(warnings.length) }),
5691
5647
  " (warnings)"
5692
5648
  ] }),
5693
- warnings.map((v) => /* @__PURE__ */ jsx34(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`))
5649
+ warnings.map((v) => /* @__PURE__ */ jsx35(ViolationItem, { violation: v }, `${v.type}-${v.packageName}`))
5694
5650
  ] })
5695
5651
  ] });
5696
5652
  }
@@ -5703,7 +5659,7 @@ function ComplianceView() {
5703
5659
  const [streamRunning, setStreamRunning] = useState16(false);
5704
5660
  const generatorRef = useRef10(null);
5705
5661
  const mountedRef = useRef10(true);
5706
- useEffect18(() => {
5662
+ useEffect19(() => {
5707
5663
  mountedRef.current = true;
5708
5664
  return () => {
5709
5665
  mountedRef.current = false;
@@ -5731,7 +5687,7 @@ function ComplianceView() {
5731
5687
  const handleExport = useCallback6(async () => {
5732
5688
  if (!report) return;
5733
5689
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5734
- const outputPath = join5(DATA_DIR, `compliance-report-${timestamp}.json`);
5690
+ const outputPath = join4(DATA_DIR, `compliance-report-${timestamp}.json`);
5735
5691
  try {
5736
5692
  await exportReport(report, outputPath);
5737
5693
  setResultMessage({ ok: true, text: t("compliance_export_done", { path: outputPath }) });
@@ -5779,8 +5735,9 @@ function ComplianceView() {
5779
5735
  }
5780
5736
  }
5781
5737
  }, [report, isPro, runCheck]);
5782
- useInput17((input, key) => {
5738
+ useInput19((input, key) => {
5783
5739
  if (phase === "remediating" || phase === "importing") return;
5740
+ if (phase === "confirming-remediate") return;
5784
5741
  if (phase === "result") {
5785
5742
  if (key.escape || input === "r") {
5786
5743
  setPhase("overview");
@@ -5805,31 +5762,48 @@ function ComplianceView() {
5805
5762
  (v) => v.type === "missing" || v.type === "wrong-version"
5806
5763
  );
5807
5764
  if (actionable.length > 0) {
5808
- void handleRemediate();
5765
+ setPhase("confirming-remediate");
5809
5766
  }
5810
5767
  return;
5811
5768
  }
5812
5769
  });
5770
+ if (phase === "confirming-remediate" && report) {
5771
+ const actionable = report.violations.filter(
5772
+ (v) => v.type === "missing" || v.type === "wrong-version"
5773
+ );
5774
+ return /* @__PURE__ */ jsx35(
5775
+ ConfirmDialog,
5776
+ {
5777
+ message: t("confirm_compliance_remediate", { count: String(actionable.length) }),
5778
+ onConfirm: () => {
5779
+ void handleRemediate();
5780
+ },
5781
+ onCancel: () => {
5782
+ setPhase("overview");
5783
+ }
5784
+ }
5785
+ );
5786
+ }
5813
5787
  if (phase === "remediating" || loading && phase !== "importing") {
5814
5788
  if (phase === "remediating") {
5815
- return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
5816
- /* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
5817
- /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(ProgressLog, { lines: streamLines, isRunning: streamRunning, title: t("compliance_remediating") }) })
5789
+ return /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", children: [
5790
+ /* @__PURE__ */ jsx35(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
5791
+ /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx35(ProgressLog, { lines: streamLines, isRunning: streamRunning, title: t("compliance_remediating") }) })
5818
5792
  ] });
5819
5793
  }
5820
- return /* @__PURE__ */ jsx34(Loading, { message: t("compliance_title") });
5794
+ return /* @__PURE__ */ jsx35(Loading, { message: t("compliance_title") });
5821
5795
  }
5822
5796
  if (phase === "result" && resultMessage) {
5823
- return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: 1, children: [
5824
- /* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
5825
- /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(
5797
+ return /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginTop: SPACING.xs, children: [
5798
+ /* @__PURE__ */ jsx35(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
5799
+ /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx35(
5826
5800
  ResultBanner,
5827
5801
  {
5828
5802
  status: resultMessage.ok ? "success" : "error",
5829
5803
  message: resultMessage.text
5830
5804
  }
5831
5805
  ) }),
5832
- /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
5806
+ /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs34(Text34, { color: COLORS.textSecondary, children: [
5833
5807
  "r:",
5834
5808
  t("hint_refresh"),
5835
5809
  " esc:",
@@ -5837,12 +5811,12 @@ function ComplianceView() {
5837
5811
  ] }) })
5838
5812
  ] });
5839
5813
  }
5840
- return /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
5841
- /* @__PURE__ */ jsx34(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
5842
- error && /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(ResultBanner, { status: "error", message: t("compliance_import_error", { error }) }) }),
5843
- phase === "importing" && /* @__PURE__ */ jsxs33(Box32, { marginTop: 1, flexDirection: "column", children: [
5844
- /* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, children: t("compliance_import_prompt") }),
5845
- /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(
5814
+ return /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", children: [
5815
+ /* @__PURE__ */ jsx35(SectionHeader, { emoji: "\u{1F50D}", title: t("compliance_title"), gradient: GRADIENTS.gold }),
5816
+ error && /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx35(ResultBanner, { status: "error", message: t("compliance_import_error", { error }) }) }),
5817
+ phase === "importing" && /* @__PURE__ */ jsxs34(Box33, { marginTop: SPACING.xs, flexDirection: "column", children: [
5818
+ /* @__PURE__ */ jsx35(Text34, { color: COLORS.textSecondary, children: t("compliance_import_prompt") }),
5819
+ /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx35(
5846
5820
  TextInput7,
5847
5821
  {
5848
5822
  defaultValue: "",
@@ -5851,31 +5825,31 @@ function ComplianceView() {
5851
5825
  }
5852
5826
  }
5853
5827
  ) }),
5854
- /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.muted, dimColor: true, children: [
5828
+ /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsxs34(Text34, { color: COLORS.muted, dimColor: true, children: [
5855
5829
  "esc:",
5856
5830
  t("hint_back")
5857
5831
  ] }) })
5858
5832
  ] }),
5859
- phase === "overview" && /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: 1, children: [
5860
- !policy ? /* @__PURE__ */ jsx34(Box32, { flexDirection: "column", children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, children: t("compliance_no_policy") }) }) : /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", children: [
5861
- /* @__PURE__ */ jsx34(Text33, { color: COLORS.textSecondary, bold: true, children: t("compliance_policy_by", { maintainer: policy.meta.maintainer }) }),
5862
- report ? /* @__PURE__ */ jsxs33(Box32, { flexDirection: "column", marginTop: 1, children: [
5863
- /* @__PURE__ */ jsx34(ComplianceScore, { report }),
5864
- report.compliant ? /* @__PURE__ */ jsx34(ResultBanner, { status: "success", message: t("compliance_ok") }) : /* @__PURE__ */ jsx34(ViolationList, { violations: report.violations })
5865
- ] }) : /* @__PURE__ */ jsx34(Box32, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text33, { color: COLORS.muted, dimColor: true, children: t("compliance_press_r_hint") }) })
5833
+ phase === "overview" && /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginTop: SPACING.xs, children: [
5834
+ !policy ? /* @__PURE__ */ jsx35(Box33, { flexDirection: "column", children: /* @__PURE__ */ jsx35(Text34, { color: COLORS.textSecondary, children: t("compliance_no_policy") }) }) : /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", children: [
5835
+ /* @__PURE__ */ jsx35(Text34, { color: COLORS.textSecondary, bold: true, children: t("compliance_policy_by", { maintainer: policy.meta.maintainer }) }),
5836
+ report ? /* @__PURE__ */ jsxs34(Box33, { flexDirection: "column", marginTop: SPACING.xs, children: [
5837
+ /* @__PURE__ */ jsx35(ComplianceScore, { report }),
5838
+ report.compliant ? /* @__PURE__ */ jsx35(ResultBanner, { status: "success", message: t("compliance_ok") }) : /* @__PURE__ */ jsx35(ViolationList, { violations: report.violations })
5839
+ ] }) : /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.xs, children: /* @__PURE__ */ jsx35(Text34, { color: COLORS.muted, dimColor: true, children: t("compliance_press_r_hint") }) })
5866
5840
  ] }),
5867
- /* @__PURE__ */ jsx34(Box32, { marginTop: 2, flexWrap: "wrap", children: /* @__PURE__ */ jsxs33(Text33, { color: COLORS.textSecondary, children: [
5841
+ /* @__PURE__ */ jsx35(Box33, { marginTop: SPACING.sm, flexWrap: "wrap", children: /* @__PURE__ */ jsxs34(Text34, { color: COLORS.textSecondary, children: [
5868
5842
  "i:",
5869
5843
  t("hint_import"),
5870
- policy && /* @__PURE__ */ jsxs33(Text33, { children: [
5844
+ policy && /* @__PURE__ */ jsxs34(Text34, { children: [
5871
5845
  " r:",
5872
5846
  t("hint_scan")
5873
5847
  ] }),
5874
- report && /* @__PURE__ */ jsxs33(Text33, { children: [
5848
+ report && /* @__PURE__ */ jsxs34(Text34, { children: [
5875
5849
  " e:",
5876
5850
  t("hint_export")
5877
5851
  ] }),
5878
- report && report.violations.some((v) => v.type === "missing" || v.type === "wrong-version") && /* @__PURE__ */ jsxs33(Text33, { children: [
5852
+ report && report.violations.some((v) => v.type === "missing" || v.type === "wrong-version") && /* @__PURE__ */ jsxs34(Text34, { children: [
5879
5853
  " c:",
5880
5854
  t("hint_clean")
5881
5855
  ] }),
@@ -5887,10 +5861,10 @@ function ComplianceView() {
5887
5861
  }
5888
5862
 
5889
5863
  // src/app.tsx
5890
- import { jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
5864
+ import { Fragment as Fragment7, jsx as jsx36, jsxs as jsxs35 } from "react/jsx-runtime";
5891
5865
  function LicenseInitializer() {
5892
5866
  const initLicense = useLicenseStore((s) => s.initialize);
5893
- useEffect19(() => {
5867
+ useEffect20(() => {
5894
5868
  initLicense();
5895
5869
  }, []);
5896
5870
  return null;
@@ -5899,70 +5873,80 @@ function ViewRouter({ currentView }) {
5899
5873
  const isPro = useLicenseStore((s) => s.isPro);
5900
5874
  const isTeam = useLicenseStore((s) => s.isTeam);
5901
5875
  if (isProView(currentView) && !isPro()) {
5902
- return /* @__PURE__ */ jsx35(UpgradePrompt, { viewId: currentView });
5876
+ return /* @__PURE__ */ jsx36(UpgradePrompt, { viewId: currentView });
5903
5877
  }
5904
5878
  if (isTeamView(currentView) && !isTeam()) {
5905
- return /* @__PURE__ */ jsx35(UpgradePrompt, { viewId: currentView });
5879
+ return /* @__PURE__ */ jsx36(UpgradePrompt, { viewId: currentView });
5906
5880
  }
5907
5881
  switch (currentView) {
5908
5882
  case "dashboard":
5909
- return /* @__PURE__ */ jsx35(DashboardView, {});
5883
+ return /* @__PURE__ */ jsx36(DashboardView, {});
5910
5884
  case "installed":
5911
- return /* @__PURE__ */ jsx35(InstalledView, {});
5885
+ return /* @__PURE__ */ jsx36(InstalledView, {});
5912
5886
  case "search":
5913
- return /* @__PURE__ */ jsx35(SearchView, {});
5887
+ return /* @__PURE__ */ jsx36(SearchView, {});
5914
5888
  case "outdated":
5915
- return /* @__PURE__ */ jsx35(OutdatedView, {});
5889
+ return /* @__PURE__ */ jsx36(OutdatedView, {});
5916
5890
  case "package-info":
5917
- return /* @__PURE__ */ jsx35(PackageInfoView, {});
5891
+ return /* @__PURE__ */ jsx36(PackageInfoView, {});
5918
5892
  case "services":
5919
- return /* @__PURE__ */ jsx35(ServicesView, {});
5893
+ return /* @__PURE__ */ jsx36(ServicesView, {});
5920
5894
  case "doctor":
5921
- return /* @__PURE__ */ jsx35(DoctorView, {});
5895
+ return /* @__PURE__ */ jsx36(DoctorView, {});
5922
5896
  case "profiles":
5923
- return /* @__PURE__ */ jsx35(ProfilesView, {});
5897
+ return /* @__PURE__ */ jsx36(ProfilesView, {});
5924
5898
  case "smart-cleanup":
5925
- return /* @__PURE__ */ jsx35(SmartCleanupView, {});
5899
+ return /* @__PURE__ */ jsx36(SmartCleanupView, {});
5926
5900
  case "history":
5927
- return /* @__PURE__ */ jsx35(HistoryView, {});
5901
+ return /* @__PURE__ */ jsx36(HistoryView, {});
5928
5902
  case "rollback":
5929
- return /* @__PURE__ */ jsx35(RollbackView, {});
5903
+ return /* @__PURE__ */ jsx36(RollbackView, {});
5930
5904
  case "brewfile":
5931
- return /* @__PURE__ */ jsx35(BrewfileView, {});
5905
+ return /* @__PURE__ */ jsx36(BrewfileView, {});
5932
5906
  case "sync":
5933
- return /* @__PURE__ */ jsx35(SyncView, {});
5907
+ return /* @__PURE__ */ jsx36(SyncView, {});
5934
5908
  case "security-audit":
5935
- return /* @__PURE__ */ jsx35(SecurityAuditView, {});
5909
+ return /* @__PURE__ */ jsx36(SecurityAuditView, {});
5936
5910
  case "compliance":
5937
- return /* @__PURE__ */ jsx35(ComplianceView, {});
5911
+ return /* @__PURE__ */ jsx36(ComplianceView, {});
5938
5912
  case "account":
5939
- return /* @__PURE__ */ jsx35(AccountView, {});
5913
+ return /* @__PURE__ */ jsx36(AccountView, {});
5940
5914
  }
5941
5915
  }
5942
5916
  function App() {
5943
5917
  const { exit } = useApp();
5944
5918
  const currentView = useNavigationStore((s) => s.currentView);
5919
+ const isTestEnv = typeof process !== "undefined" && false;
5920
+ const [showWelcome, setShowWelcome] = useState17(isTestEnv ? false : null);
5921
+ useEffect20(() => {
5922
+ if (isTestEnv) return;
5923
+ void hasCompletedOnboarding().then((done) => setShowWelcome(!done));
5924
+ }, []);
5945
5925
  useGlobalKeyboard({ onQuit: exit });
5946
- return /* @__PURE__ */ jsxs34(AppLayout, { children: [
5947
- /* @__PURE__ */ jsx35(LicenseInitializer, {}),
5948
- /* @__PURE__ */ jsx35(ViewRouter, { currentView })
5926
+ if (showWelcome === null) {
5927
+ return /* @__PURE__ */ jsx36(AppLayout, { children: /* @__PURE__ */ jsx36(Fragment7, {}) });
5928
+ }
5929
+ if (showWelcome) {
5930
+ return /* @__PURE__ */ jsx36(AppLayout, { children: /* @__PURE__ */ jsx36(WelcomeView, { onContinue: () => setShowWelcome(false) }) });
5931
+ }
5932
+ return /* @__PURE__ */ jsxs35(AppLayout, { children: [
5933
+ /* @__PURE__ */ jsx36(LicenseInitializer, {}),
5934
+ /* @__PURE__ */ jsx36(ViewRouter, { currentView })
5949
5935
  ] });
5950
5936
  }
5951
5937
 
5952
5938
  // src/lib/crash-reporter.ts
5953
- import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir3 } from "fs/promises";
5954
- import { randomUUID as randomUUID3 } from "crypto";
5955
- import { homedir as homedir3, platform, release, arch } from "os";
5956
- import { join as join6 } from "path";
5939
+ import { readFile as readFile3 } from "fs/promises";
5940
+ import { homedir, platform, release, arch } from "os";
5941
+ import { join as join5 } from "path";
5957
5942
  var ENDPOINT_ENV = "BREW_TUI_CRASH_ENDPOINT";
5958
5943
  var TOKEN_ENV = "BREW_TUI_CRASH_TOKEN";
5959
- var CONFIG_PATH = join6(homedir3(), ".brew-tui", "crash-reporter.json");
5960
- var MACHINE_ID_PATH3 = join6(homedir3(), ".brew-tui", "machine-id");
5944
+ var CONFIG_PATH = join5(homedir(), ".brew-tui", "crash-reporter.json");
5961
5945
  var POST_TIMEOUT_MS = 5e3;
5962
5946
  var _installed = false;
5963
5947
  async function loadConfigFromDisk() {
5964
5948
  try {
5965
- const raw = await readFile5(CONFIG_PATH, "utf-8");
5949
+ const raw = await readFile3(CONFIG_PATH, "utf-8");
5966
5950
  const parsed = JSON.parse(raw);
5967
5951
  return {
5968
5952
  endpoint: typeof parsed.endpoint === "string" ? parsed.endpoint : null,
@@ -5973,20 +5957,6 @@ async function loadConfigFromDisk() {
5973
5957
  return { endpoint: null, token: null, enabled: false };
5974
5958
  }
5975
5959
  }
5976
- async function getMachineId4() {
5977
- try {
5978
- const id2 = (await readFile5(MACHINE_ID_PATH3, "utf-8")).trim();
5979
- if (id2) return id2;
5980
- } catch {
5981
- }
5982
- const id = randomUUID3();
5983
- try {
5984
- await mkdir3(join6(homedir3(), ".brew-tui"), { recursive: true, mode: 448 });
5985
- await writeFile5(MACHINE_ID_PATH3, id, { encoding: "utf-8", mode: 384 });
5986
- } catch {
5987
- }
5988
- return id;
5989
- }
5990
5960
  async function resolveConfig() {
5991
5961
  const envEndpoint = process.env[ENDPOINT_ENV]?.trim();
5992
5962
  const envToken = process.env[TOKEN_ENV]?.trim();
@@ -6044,8 +6014,8 @@ function buildReport(level, err, context, machineId, version) {
6044
6014
  async function reportError(err, context = {}) {
6045
6015
  const config = await resolveConfig();
6046
6016
  if (!config.enabled || !config.endpoint) return;
6047
- const machineId = await getMachineId4();
6048
- const version = true ? "0.6.1" : "unknown";
6017
+ const machineId = await getMachineId();
6018
+ const version = true ? "0.7.0" : "unknown";
6049
6019
  await postReport(buildReport("error", err, context, machineId, version), config);
6050
6020
  }
6051
6021
  async function installCrashReporter() {
@@ -6053,8 +6023,8 @@ async function installCrashReporter() {
6053
6023
  const config = await resolveConfig();
6054
6024
  if (!config.enabled || !config.endpoint) return;
6055
6025
  _installed = true;
6056
- const machineId = await getMachineId4();
6057
- const version = true ? "0.6.1" : "unknown";
6026
+ const machineId = await getMachineId();
6027
+ const version = true ? "0.7.0" : "unknown";
6058
6028
  process.on("uncaughtException", (err) => {
6059
6029
  void postReport(buildReport("fatal", err, { kind: "uncaughtException" }, machineId, version), config);
6060
6030
  });
@@ -6065,7 +6035,7 @@ async function installCrashReporter() {
6065
6035
  }
6066
6036
 
6067
6037
  // src/index.tsx
6068
- import { jsx as jsx36 } from "react/jsx-runtime";
6038
+ import { jsx as jsx37 } from "react/jsx-runtime";
6069
6039
  var [, , command, arg] = process.argv;
6070
6040
  async function runCli() {
6071
6041
  await ensureDataDirs();
@@ -6160,7 +6130,7 @@ async function runCli() {
6160
6130
  }
6161
6131
  if (isPro) {
6162
6132
  try {
6163
- const { loadSnapshots: loadSnapshots2 } = await import("./snapshot-RAPGMAJF.js");
6133
+ const { loadSnapshots: loadSnapshots2 } = await import("./snapshot-JDRSBMG6.js");
6164
6134
  const snapshots = await loadSnapshots2();
6165
6135
  if (snapshots.length > 0) {
6166
6136
  const latest = snapshots[0];
@@ -6172,7 +6142,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
6172
6142
  } catch {
6173
6143
  }
6174
6144
  try {
6175
- const { loadBrewfile: loadBrewfile2, computeDrift: computeDrift2 } = await import("./brewfile-manager-3SERRYNC.js");
6145
+ const { loadBrewfile: loadBrewfile2, computeDrift: computeDrift2 } = await import("./brewfile-manager-6LXONGSA.js");
6176
6146
  const schema = await loadBrewfile2();
6177
6147
  if (schema) {
6178
6148
  const drift = await computeDrift2(schema);
@@ -6181,7 +6151,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
6181
6151
  } catch {
6182
6152
  }
6183
6153
  try {
6184
- const { loadSyncConfig: loadSyncConfig2 } = await import("./sync-engine-CAFK4LHA.js");
6154
+ const { loadSyncConfig: loadSyncConfig2 } = await import("./sync-engine-4OA4JNR3.js");
6185
6155
  const syncConfig = await loadSyncConfig2();
6186
6156
  if (syncConfig?.lastSync) {
6187
6157
  console.log(`Sync: last sync ${formatDate(syncConfig.lastSync)}`);
@@ -6190,7 +6160,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
6190
6160
  }
6191
6161
  try {
6192
6162
  const { loadPolicy: loadPolicy2 } = await import("./policy-io-EECGRKNA.js");
6193
- const { checkCompliance: checkCompliance2 } = await import("./compliance-checker-X7P623UF.js");
6163
+ const { checkCompliance: checkCompliance2 } = await import("./compliance-checker-MAREAFDH.js");
6194
6164
  const policy = await loadPolicy2(`${process.env["HOME"] ?? "~"}/.brew-tui/policy.yaml`).catch(() => null);
6195
6165
  if (policy) {
6196
6166
  const report = await checkCompliance2(policy, true);
@@ -6204,7 +6174,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
6204
6174
  if (command === "install-brewbar") {
6205
6175
  await useLicenseStore.getState().initialize();
6206
6176
  const isPro = useLicenseStore.getState().isPro();
6207
- const { installBrewBar } = await import("./brewbar-installer-BAHS6EEZ.js");
6177
+ const { installBrewBar } = await import("./brewbar-installer-KJTIZIVU.js");
6208
6178
  try {
6209
6179
  await installBrewBar(isPro, arg === "--force");
6210
6180
  console.log(t("cli_brewbarInstalled"));
@@ -6215,7 +6185,7 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
6215
6185
  return;
6216
6186
  }
6217
6187
  if (command === "uninstall-brewbar") {
6218
- const { uninstallBrewBar } = await import("./brewbar-installer-BAHS6EEZ.js");
6188
+ const { uninstallBrewBar } = await import("./brewbar-installer-KJTIZIVU.js");
6219
6189
  try {
6220
6190
  await uninstallBrewBar();
6221
6191
  console.log(t("cli_brewbarUninstalled"));
@@ -6233,20 +6203,20 @@ Snapshots: ${snapshots.length} (latest: ${latest ? formatDate(latest.capturedAt)
6233
6203
  console.log(t("cli_deactivateCancelled"));
6234
6204
  return;
6235
6205
  }
6236
- await rm3(DATA_DIR, { recursive: true, force: true });
6206
+ await rm2(DATA_DIR, { recursive: true, force: true });
6237
6207
  console.log(t("delete_account_success"));
6238
6208
  return;
6239
6209
  }
6240
6210
  await ensureBrewBarRunning();
6241
6211
  process.env.BREW_TUI_TUI_MODE = "1";
6242
6212
  process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
6243
- render(/* @__PURE__ */ jsx36(App, {}));
6213
+ render(/* @__PURE__ */ jsx37(App, {}));
6244
6214
  }
6245
6215
  async function ensureBrewBarRunning() {
6246
6216
  if (process.platform !== "darwin") return;
6247
6217
  await useLicenseStore.getState().initialize();
6248
6218
  if (!useLicenseStore.getState().isPro()) return;
6249
- const { isBrewBarInstalled, installBrewBar, launchBrewBar } = await import("./brewbar-installer-BAHS6EEZ.js");
6219
+ const { isBrewBarInstalled, installBrewBar, launchBrewBar } = await import("./brewbar-installer-KJTIZIVU.js");
6250
6220
  try {
6251
6221
  if (!await isBrewBarInstalled()) {
6252
6222
  console.log(t("cli_brewbarInstalling"));