sickbay 1.3.1 → 1.4.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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Header
3
- } from "./chunk-WS67R6X3.js";
3
+ } from "./chunk-QC7BTWPM.js";
4
4
 
5
5
  // src/components/DiffApp.tsx
6
6
  import React, { useState, useEffect } from "react";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Header
3
- } from "./chunk-WS67R6X3.js";
3
+ } from "./chunk-QC7BTWPM.js";
4
4
  import {
5
5
  shortName
6
6
  } from "./chunk-7CHSSJZH.js";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-MBVA75EM.js";
4
4
  import {
5
5
  Header
6
- } from "./chunk-WS67R6X3.js";
6
+ } from "./chunk-QC7BTWPM.js";
7
7
  import {
8
8
  shortName
9
9
  } from "./chunk-7CHSSJZH.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Header
3
- } from "./chunk-WS67R6X3.js";
3
+ } from "./chunk-QC7BTWPM.js";
4
4
  import {
5
5
  shortName
6
6
  } from "./chunk-7CHSSJZH.js";
@@ -2,13 +2,13 @@ import {
2
2
  sparkline,
3
3
  trendArrow
4
4
  } from "./chunk-SHO3ZXTH.js";
5
+ import {
6
+ Header
7
+ } from "./chunk-QC7BTWPM.js";
5
8
  import {
6
9
  detectRegressions,
7
10
  loadHistory
8
11
  } from "./chunk-3OR2GFVE.js";
9
- import {
10
- Header
11
- } from "./chunk-WS67R6X3.js";
12
12
  import {
13
13
  shortName
14
14
  } from "./chunk-7CHSSJZH.js";
@@ -3,8 +3,12 @@ import {
3
3
  trendArrow
4
4
  } from "./chunk-SHO3ZXTH.js";
5
5
  import {
6
- LOADING_MESSAGES
7
- } from "./chunk-POUHUMJN.js";
6
+ LOADING_MESSAGES,
7
+ UpdateNotice
8
+ } from "./chunk-FNY6PWDN.js";
9
+ import {
10
+ checkForUpdate
11
+ } from "./chunk-TWCTPAXQ.js";
8
12
  import {
9
13
  loadHistory
10
14
  } from "./chunk-3OR2GFVE.js";
@@ -662,6 +666,7 @@ function TuiApp({
662
666
  const [activityLog, setActivityLog] = useState8([]);
663
667
  const [healthScrollOffset, setHealthScrollOffset] = useState8(0);
664
668
  const [scoreFlash, setScoreFlash] = useState8();
669
+ const [updateInfo, setUpdateInfo] = useState8(null);
665
670
  const ALL_PANELS = /* @__PURE__ */ new Set(["health", "score", "trend", "git", "quickwins", "activity"]);
666
671
  const [visiblePanels, setVisiblePanels] = useState8(
667
672
  animateOnMount ? /* @__PURE__ */ new Set() : ALL_PANELS
@@ -683,6 +688,11 @@ function TuiApp({
683
688
  );
684
689
  return () => timers.forEach(clearTimeout);
685
690
  }, []);
691
+ useEffect7(() => {
692
+ checkForUpdate("1.3.1").then((info) => {
693
+ if (info) setUpdateInfo(info);
694
+ });
695
+ }, []);
686
696
  const reportRef = useRef5(null);
687
697
  const monorepoReportRef = useRef5(null);
688
698
  useEffect7(() => {
@@ -863,7 +873,13 @@ function TuiApp({
863
873
  const projectName = monorepoReport ? `${monorepoReport.rootPath.split("/").pop()} (monorepo)` : report?.projectInfo?.name ?? "\u2014";
864
874
  const projectVersion = report?.projectInfo?.version;
865
875
  const scanLabel = lastScanTime ? `Last scan ${lastScanTime.toLocaleTimeString()}` : isScanning ? "Scanning\u2026" : "Not yet scanned";
866
- return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", width: columns, height: rows }, /* @__PURE__ */ React11.createElement(Box11, { paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React11.createElement(Box11, { gap: 1 }, /* @__PURE__ */ React11.createElement(Text11, { bold: true, color: "cyan" }, "SICKBAY"), /* @__PURE__ */ React11.createElement(Text11, { bold: true }, projectName), projectVersion && /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "v", projectVersion)), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, scanLabel)), /* @__PURE__ */ React11.createElement(Box11, { height: topHeight }, /* @__PURE__ */ React11.createElement(Box11, { width: "55%" }, /* @__PURE__ */ React11.createElement(
876
+ return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", width: columns, height: rows }, updateInfo && /* @__PURE__ */ React11.createElement(
877
+ UpdateNotice,
878
+ {
879
+ currentVersion: updateInfo.currentVersion,
880
+ latestVersion: updateInfo.latestVersion
881
+ }
882
+ ), /* @__PURE__ */ React11.createElement(Box11, { paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React11.createElement(Box11, { gap: 1 }, /* @__PURE__ */ React11.createElement(Text11, { bold: true, color: "cyan" }, "SICKBAY"), /* @__PURE__ */ React11.createElement(Text11, { bold: true }, projectName), projectVersion && /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "v", projectVersion)), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, scanLabel)), /* @__PURE__ */ React11.createElement(Box11, { height: topHeight }, /* @__PURE__ */ React11.createElement(Box11, { width: "55%" }, /* @__PURE__ */ React11.createElement(
867
883
  PanelBorder,
868
884
  {
869
885
  title: "HEALTH CHECKS",
@@ -0,0 +1,34 @@
1
+ // src/components/UpdateNotice.tsx
2
+ import React from "react";
3
+ import { Box, Text } from "ink";
4
+ function UpdateNotice({ currentVersion, latestVersion }) {
5
+ const line1 = `Update available: ${currentVersion} \u2192 ${latestVersion}`;
6
+ const line2 = 'Run "npx sickbay@latest" to upgrade';
7
+ const maxLength = Math.max(line1.length, line2.length);
8
+ const padding = 2;
9
+ const boxWidth = maxLength + padding * 2;
10
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "yellow", dimColor: true }, `\u256D${"\u2500".repeat(boxWidth)}\u256E`), /* @__PURE__ */ React.createElement(Box, { paddingX: padding }, /* @__PURE__ */ React.createElement(Text, { color: "yellow", dimColor: true }, line1)), /* @__PURE__ */ React.createElement(Box, { paddingX: padding }, /* @__PURE__ */ React.createElement(Text, { color: "yellow", dimColor: true }, line2)), /* @__PURE__ */ React.createElement(Text, { color: "yellow", dimColor: true }, `\u2570${"\u2500".repeat(boxWidth)}\u256F`));
11
+ }
12
+
13
+ // src/lib/messages.ts
14
+ var LOADING_MESSAGES = [
15
+ "Scanning for pre-existing conditions\u2026",
16
+ "Counting packages\u2026 still counting\u2026",
17
+ "Still here. Still scanning...",
18
+ "The unused dependencies know what they did...",
19
+ "Every unused export is a tiny cry for help...",
20
+ "Checking if 'TODO: fix later' was ever fixed later...",
21
+ "Your secrets are safe with us. Unlike your .env file...",
22
+ "Good things take time. This is one of the good things. Probably...",
23
+ "npm audit found issues. npm audit --fix found different issues.",
24
+ "This is fine...",
25
+ "Evaluating life choices. Yours. Via package.json.",
26
+ "Almost done. (This is not a legally binding statement.)",
27
+ "node_modules: depth unknown. Will not attempt.",
28
+ "Performing checks. Results may vary..."
29
+ ];
30
+
31
+ export {
32
+ LOADING_MESSAGES,
33
+ UpdateNotice
34
+ };
@@ -11,7 +11,7 @@ var ASCII_ART = `
11
11
  A vitals health check for your app
12
12
  `.trim();
13
13
  function Header({ projectName }) {
14
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "green" }, ASCII_ART), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " v", "1.3.0")), projectName && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " Analyzing "), /* @__PURE__ */ React.createElement(Text, { bold: true, color: "white" }, projectName)));
14
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "green" }, ASCII_ART), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " v", "1.3.1")), projectName && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " Analyzing "), /* @__PURE__ */ React.createElement(Text, { bold: true, color: "white" }, projectName)));
15
15
  }
16
16
 
17
17
  export {
@@ -0,0 +1,73 @@
1
+ // src/lib/update-check.ts
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
6
+ var REGISTRY_URL = "https://registry.npmjs.org/sickbay/latest";
7
+ var SICKBAY_DIR = join(homedir(), ".sickbay");
8
+ var CACHE_PATH = join(SICKBAY_DIR, "update-check.json");
9
+ function isNewerVersion(current, latest) {
10
+ try {
11
+ const cleanCurrent = current.split("-")[0];
12
+ const cleanLatest = latest.split("-")[0];
13
+ const currentParts = cleanCurrent.split(".").map(Number);
14
+ const latestParts = cleanLatest.split(".").map(Number);
15
+ if (currentParts.length !== 3 || latestParts.length !== 3 || currentParts.some(isNaN) || latestParts.some(isNaN)) {
16
+ return false;
17
+ }
18
+ for (let i = 0; i < 3; i++) {
19
+ if (latestParts[i] > currentParts[i]) return true;
20
+ if (latestParts[i] < currentParts[i]) return false;
21
+ }
22
+ return false;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+ async function checkForUpdate(currentVersion) {
28
+ try {
29
+ if (existsSync(CACHE_PATH)) {
30
+ try {
31
+ const raw = readFileSync(CACHE_PATH, "utf8");
32
+ const cache = JSON.parse(raw);
33
+ const age = Date.now() - cache.checkedAt;
34
+ if (age < CACHE_TTL_MS) {
35
+ if (isNewerVersion(currentVersion, cache.latestVersion)) {
36
+ return { currentVersion, latestVersion: cache.latestVersion };
37
+ }
38
+ return null;
39
+ }
40
+ } catch {
41
+ }
42
+ }
43
+ const response = await fetch(REGISTRY_URL, {
44
+ signal: AbortSignal.timeout(5e3)
45
+ });
46
+ if (!response.ok) {
47
+ return null;
48
+ }
49
+ const data = await response.json();
50
+ const latestVersion = data.version;
51
+ try {
52
+ mkdirSync(SICKBAY_DIR, { recursive: true });
53
+ writeFileSync(
54
+ CACHE_PATH,
55
+ JSON.stringify({ latestVersion, checkedAt: Date.now() })
56
+ );
57
+ } catch {
58
+ }
59
+ if (isNewerVersion(currentVersion, latestVersion)) {
60
+ return { currentVersion, latestVersion };
61
+ }
62
+ return null;
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+
68
+ export {
69
+ CACHE_TTL_MS,
70
+ REGISTRY_URL,
71
+ isNewerVersion,
72
+ checkForUpdate
73
+ };
package/dist/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- LOADING_MESSAGES
4
- } from "./chunk-POUHUMJN.js";
5
2
  import {
6
3
  ProgressList
7
4
  } from "./chunk-MBVA75EM.js";
5
+ import {
6
+ LOADING_MESSAGES,
7
+ UpdateNotice
8
+ } from "./chunk-FNY6PWDN.js";
8
9
  import {
9
10
  Header
10
- } from "./chunk-WS67R6X3.js";
11
+ } from "./chunk-QC7BTWPM.js";
11
12
  import {
12
13
  getScoreEmoji,
13
14
  runSickbay,
@@ -127,7 +128,8 @@ function App({
127
128
  enableAI,
128
129
  verbose,
129
130
  quotes,
130
- isMonorepo
131
+ isMonorepo,
132
+ updatePromise
131
133
  }) {
132
134
  const { exit } = useApp();
133
135
  const [phase, setPhase] = useState("loading");
@@ -140,6 +142,7 @@ function App({
140
142
  const [scanningPackage, setScanningPackage] = useState();
141
143
  const [loadingMsgIdx, setLoadingMsgIdx] = useState(0);
142
144
  const [scanDuration, setScanDuration] = useState(null);
145
+ const [updateInfo, setUpdateInfo] = useState(null);
143
146
  const hasRun = useRef(false);
144
147
  const scanStartTime = useRef(0);
145
148
  useEffect(() => {
@@ -149,6 +152,13 @@ function App({
149
152
  }, 4e3);
150
153
  return () => clearInterval(id);
151
154
  }, [isMonorepo]);
155
+ useEffect(() => {
156
+ if (updatePromise) {
157
+ updatePromise.then((info) => {
158
+ if (info) setUpdateInfo(info);
159
+ });
160
+ }
161
+ }, [updatePromise]);
152
162
  useEffect(() => {
153
163
  if (hasRun.current) return;
154
164
  hasRun.current = true;
@@ -270,7 +280,13 @@ function App({
270
280
  setTimeout(() => exit(err), 100);
271
281
  });
272
282
  }, []);
273
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React5.createElement(Header, { projectName }), phase === "loading" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, isMonorepo ? /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, /* @__PURE__ */ React5.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ", LOADING_MESSAGES[loadingMsgIdx])), scanningPackage && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "\u2192 "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, scanningPackage))) : /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "Running health checks..."), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React5.createElement(ProgressList, { items: progress })))), phase === "error" && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, "\u2717 Error: ", error)), phase === "results" && monorepoReport && /* @__PURE__ */ React5.createElement(MonorepoSummaryTable, { report: monorepoReport, scanDuration }), phase === "results" && report && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, report.checks.filter((c) => c.status !== "skipped").map((check) => /* @__PURE__ */ React5.createElement(CheckResultRow, { key: check.id, result: check })), /* @__PURE__ */ React5.createElement(Summary, { report, scanDuration }), /* @__PURE__ */ React5.createElement(QuickWins, { report }), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "View detailed report: "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "sickbay --web"))), phase === "opening-web" && (monorepoReport ?? report) && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, monorepoReport ? /* @__PURE__ */ React5.createElement(MonorepoSummaryTable, { report: monorepoReport, scanDuration }) : report ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, report.checks.filter((c) => c.status !== "skipped").map((check) => /* @__PURE__ */ React5.createElement(CheckResultRow, { key: check.id, result: check })), /* @__PURE__ */ React5.createElement(Summary, { report, scanDuration })) : null, /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, webUrl ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, "\u2713 Dashboard running at "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, webUrl), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (Ctrl+C to stop)")) : /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, /* @__PURE__ */ React5.createElement(Spinner, { type: "dots" })), " ", /* @__PURE__ */ React5.createElement(Gradient, { name: "retro" }, "Launching dashboard with AI insights...")))));
283
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React5.createElement(Header, { projectName }), updateInfo && /* @__PURE__ */ React5.createElement(
284
+ UpdateNotice,
285
+ {
286
+ currentVersion: updateInfo.currentVersion,
287
+ latestVersion: updateInfo.latestVersion
288
+ }
289
+ ), phase === "loading" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, isMonorepo ? /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, /* @__PURE__ */ React5.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ", LOADING_MESSAGES[loadingMsgIdx])), scanningPackage && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "\u2192 "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, scanningPackage))) : /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "Running health checks..."), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React5.createElement(ProgressList, { items: progress })))), phase === "error" && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, "\u2717 Error: ", error)), phase === "results" && monorepoReport && /* @__PURE__ */ React5.createElement(MonorepoSummaryTable, { report: monorepoReport, scanDuration }), phase === "results" && report && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, report.checks.filter((c) => c.status !== "skipped").map((check) => /* @__PURE__ */ React5.createElement(CheckResultRow, { key: check.id, result: check })), /* @__PURE__ */ React5.createElement(Summary, { report, scanDuration }), /* @__PURE__ */ React5.createElement(QuickWins, { report }), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "View detailed report: "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "sickbay --web"))), phase === "opening-web" && (monorepoReport ?? report) && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, monorepoReport ? /* @__PURE__ */ React5.createElement(MonorepoSummaryTable, { report: monorepoReport, scanDuration }) : report ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, report.checks.filter((c) => c.status !== "skipped").map((check) => /* @__PURE__ */ React5.createElement(CheckResultRow, { key: check.id, result: check })), /* @__PURE__ */ React5.createElement(Summary, { report, scanDuration })) : null, /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, webUrl ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, "\u2713 Dashboard running at "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, webUrl), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (Ctrl+C to stop)")) : /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, /* @__PURE__ */ React5.createElement(Spinner, { type: "dots" })), " ", /* @__PURE__ */ React5.createElement(Gradient, { name: "retro" }, "Launching dashboard with AI insights...")))));
274
290
  }
275
291
  function MonorepoSummaryTable({
276
292
  report,
@@ -287,13 +303,21 @@ if (existsSync(globalConfigPath)) {
287
303
  }
288
304
  config({ debug: false, quiet: true });
289
305
  var program = new Command();
290
- program.name("sickbay").description("React project health check CLI").version("1.3.0").enablePositionalOptions().passThroughOptions().option("-p, --path <path>", "project path to analyze", process.cwd()).option("-c, --checks <checks>", "comma-separated list of checks to run").option("--package <name>", "scope to a single named package (monorepo only)").option("--json", "output raw JSON report").option("--web", "open web dashboard after scan").option("--no-ai", "disable AI features").option("--no-quotes", "suppress personality quotes in output").option("--verbose", "show verbose output").action(async (options) => {
306
+ program.name("sickbay").description("React project health check CLI").version("1.3.1").enablePositionalOptions().passThroughOptions().option("-p, --path <path>", "project path to analyze", process.cwd()).option("-c, --checks <checks>", "comma-separated list of checks to run").option("--package <name>", "scope to a single named package (monorepo only)").option("--json", "output raw JSON report").option("--web", "open web dashboard after scan").option("--no-ai", "disable AI features").option("--no-quotes", "suppress personality quotes in output").option("--verbose", "show verbose output").action(async (options) => {
291
307
  if (options.path && options.path !== process.cwd()) {
292
308
  const projectEnvPath = join(options.path, ".env");
293
309
  if (existsSync(projectEnvPath)) {
294
310
  config({ path: projectEnvPath, override: true });
295
311
  }
296
312
  }
313
+ const updatePromise = (async () => {
314
+ try {
315
+ const { checkForUpdate } = await import("./update-check-M7IFOMLJ.js");
316
+ return await checkForUpdate("1.3.1");
317
+ } catch {
318
+ return null;
319
+ }
320
+ })();
297
321
  const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
298
322
  const { detectMonorepo, runSickbay: runSickbay2, runSickbayMonorepo: runSickbayMonorepo2 } = await import("./dist-YIK3YE5B.js");
299
323
  const monorepoInfo = await detectMonorepo(options.path);
@@ -329,7 +353,8 @@ program.name("sickbay").description("React project health check CLI").version("1
329
353
  openWeb: options.web,
330
354
  enableAI: options.ai !== false,
331
355
  verbose: options.verbose,
332
- quotes: options.quotes
356
+ quotes: options.quotes,
357
+ updatePromise
333
358
  })
334
359
  );
335
360
  return;
@@ -375,7 +400,8 @@ program.name("sickbay").description("React project health check CLI").version("1
375
400
  enableAI: options.ai !== false,
376
401
  verbose: options.verbose,
377
402
  quotes: options.quotes,
378
- isMonorepo: monorepoInfo.isMonorepo
403
+ isMonorepo: monorepoInfo.isMonorepo,
404
+ updatePromise
379
405
  })
380
406
  );
381
407
  });
@@ -392,7 +418,7 @@ program.command("fix").description("Interactively fix issues found by sickbay sc
392
418
  }
393
419
  const { resolveProject } = await import("./resolve-package-57YPNPWD.js");
394
420
  const resolution = await resolveProject(options.path, options.package);
395
- const { FixApp } = await import("./FixApp-AEO2W2EP.js");
421
+ const { FixApp } = await import("./FixApp-FRFLW6XV.js");
396
422
  const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
397
423
  const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
398
424
  render(
@@ -418,7 +444,7 @@ program.command("trend").description("Show score history and trends over time").
418
444
  const { resolveProject } = await import("./resolve-package-57YPNPWD.js");
419
445
  const resolution = await resolveProject(options.path, options.package);
420
446
  const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
421
- const { TrendApp } = await import("./TrendApp-JJ76OGDZ.js");
447
+ const { TrendApp } = await import("./TrendApp-WS45AJW6.js");
422
448
  render(
423
449
  React6.createElement(TrendApp, {
424
450
  projectPath,
@@ -440,7 +466,7 @@ program.command("stats").description("Show a quick codebase overview and project
440
466
  const { resolveProject } = await import("./resolve-package-57YPNPWD.js");
441
467
  const resolution = await resolveProject(options.path, options.package);
442
468
  const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
443
- const { StatsApp } = await import("./StatsApp-IHRVYLPF.js");
469
+ const { StatsApp } = await import("./StatsApp-4H2FX2OH.js");
444
470
  render(
445
471
  React6.createElement(StatsApp, {
446
472
  projectPath,
@@ -452,7 +478,7 @@ program.command("stats").description("Show a quick codebase overview and project
452
478
  );
453
479
  });
454
480
  program.command("tui").description("Launch the persistent developer dashboard").option("-p, --path <path>", "project path to monitor", process.cwd()).option("--no-watch", "disable file-watching auto-refresh").option("--no-quotes", "suppress personality quotes in output").option("--refresh <seconds>", "auto-refresh interval in seconds", "300").option("-c, --checks <checks>", "comma-separated list of checks to run").action(async (options) => {
455
- const { TuiApp } = await import("./TuiApp-YXSV6ZYY.js");
481
+ const { TuiApp } = await import("./TuiApp-6YGVWIYM.js");
456
482
  const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
457
483
  render(
458
484
  React6.createElement(TuiApp, {
@@ -475,7 +501,7 @@ program.command("doctor").description("Diagnose project setup and configuration
475
501
  const { resolveProject } = await import("./resolve-package-57YPNPWD.js");
476
502
  const resolution = await resolveProject(options.path, options.package);
477
503
  const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
478
- const { DoctorApp } = await import("./DoctorApp-ZFBKXFR4.js");
504
+ const { DoctorApp } = await import("./DoctorApp-ODLJDYJM.js");
479
505
  render(
480
506
  React6.createElement(DoctorApp, {
481
507
  projectPath,
@@ -523,7 +549,7 @@ program.command("diff <branch>").description("Compare health score against anoth
523
549
  }
524
550
  }
525
551
  const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
526
- const { DiffApp } = await import("./DiffApp-E2AQ3IOB.js");
552
+ const { DiffApp } = await import("./DiffApp-HIIMRBHH.js");
527
553
  render(
528
554
  React6.createElement(DiffApp, {
529
555
  projectPath: options.path,
@@ -0,0 +1,12 @@
1
+ import {
2
+ CACHE_TTL_MS,
3
+ REGISTRY_URL,
4
+ checkForUpdate,
5
+ isNewerVersion
6
+ } from "./chunk-TWCTPAXQ.js";
7
+ export {
8
+ CACHE_TTL_MS,
9
+ REGISTRY_URL,
10
+ checkForUpdate,
11
+ isNewerVersion
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sickbay",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Zero-config health check CLI for JavaScript and TypeScript projects",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,21 +0,0 @@
1
- // src/lib/messages.ts
2
- var LOADING_MESSAGES = [
3
- "Scanning for pre-existing conditions\u2026",
4
- "Counting packages\u2026 still counting\u2026",
5
- "Still here. Still scanning...",
6
- "The unused dependencies know what they did...",
7
- "Every unused export is a tiny cry for help...",
8
- "Checking if 'TODO: fix later' was ever fixed later...",
9
- "Your secrets are safe with us. Unlike your .env file...",
10
- "Good things take time. This is one of the good things. Probably...",
11
- "npm audit found issues. npm audit --fix found different issues.",
12
- "This is fine...",
13
- "Evaluating life choices. Yours. Via package.json.",
14
- "Almost done. (This is not a legally binding statement.)",
15
- "node_modules: depth unknown. Will not attempt.",
16
- "Performing checks. Results may vary..."
17
- ];
18
-
19
- export {
20
- LOADING_MESSAGES
21
- };