sickbay 1.15.2 → 1.15.4
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.
- package/README.md +2 -2
- package/dist/{DiffApp-ACTRBPEJ.js → DiffApp-KR745TCV.js} +2 -2
- package/dist/{DoctorApp-AT2VCFDQ.js → DoctorApp-VKJCRS2I.js} +3 -3
- package/dist/{FixApp-OFGNY2UR.js → FixApp-6JU4B3EJ.js} +3 -3
- package/dist/{StatsApp-YQJOEQIW.js → StatsApp-EYYWVSGZ.js} +3 -3
- package/dist/{TrendApp-IBHR4R5D.js → TrendApp-TB4E7ZUU.js} +3 -3
- package/dist/{TuiApp-APJXORT5.js → TuiApp-4JRQCD7M.js} +4 -4
- package/dist/{chunk-QYJLQBEL.js → chunk-MCHTDD7R.js} +1 -1
- package/dist/{chunk-J4C4MHXN.js → chunk-PD5OA3SZ.js} +1 -1
- package/dist/{chunk-6GIJCWXK.js → chunk-WEGMDILT.js} +80 -24
- package/dist/config.js +1 -1
- package/dist/{dist-Q76NLAL5.js → dist-G7GDVJT5.js} +1 -1
- package/dist/index.js +67 -53
- package/dist/{init-XUMTXUVV.js → init-BBKWOHRV.js} +1 -1
- package/dist/{resolve-package-RN2JUNIX.js → resolve-package-CB3KDANT.js} +2 -2
- package/dist/{web-U22VUQGS.js → web-EUG7IJKA.js} +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# sickbay
|
|
2
2
|
|
|
3
|
-
The terminal interface for Sickbay. Built with [Ink](https://github.com/vadimdemedes/ink) (React for terminals) and [Commander](https://github.com/tj/commander.js).
|
|
3
|
+
The terminal interface for Sickbay. Published to npm as the unscoped `sickbay` package. Built with [Ink](https://github.com/vadimdemedes/ink) (React for terminals) and [Commander](https://github.com/tj/commander.js).
|
|
4
4
|
|
|
5
5
|
## Quickstart
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Header
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PD5OA3SZ.js";
|
|
4
4
|
|
|
5
5
|
// src/components/DiffApp.tsx
|
|
6
6
|
import React, { useState, useEffect } from "react";
|
|
@@ -119,7 +119,7 @@ function DiffApp({ projectPath, branch, jsonOutput, checks, verbose }) {
|
|
|
119
119
|
useEffect(() => {
|
|
120
120
|
(async () => {
|
|
121
121
|
try {
|
|
122
|
-
const { runSickbay } = await import("./dist-
|
|
122
|
+
const { runSickbay } = await import("./dist-G7GDVJT5.js");
|
|
123
123
|
const currentReport = await runSickbay({
|
|
124
124
|
projectPath,
|
|
125
125
|
checks,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shortName
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MCHTDD7R.js";
|
|
4
4
|
import {
|
|
5
5
|
Header
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PD5OA3SZ.js";
|
|
7
7
|
import {
|
|
8
8
|
detectPackageManager,
|
|
9
9
|
detectProject
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-WEGMDILT.js";
|
|
11
11
|
|
|
12
12
|
// src/components/DoctorApp.tsx
|
|
13
13
|
import React, { useState, useEffect } from "react";
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shortName
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MCHTDD7R.js";
|
|
4
4
|
import {
|
|
5
5
|
ProgressList
|
|
6
6
|
} from "./chunk-MBVA75EM.js";
|
|
7
7
|
import {
|
|
8
8
|
Header
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-PD5OA3SZ.js";
|
|
10
10
|
import {
|
|
11
11
|
runSickbay
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-WEGMDILT.js";
|
|
13
13
|
|
|
14
14
|
// src/components/FixApp.tsx
|
|
15
15
|
import React, { useState, useEffect, useCallback } from "react";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shortName
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MCHTDD7R.js";
|
|
4
4
|
import {
|
|
5
5
|
Header
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PD5OA3SZ.js";
|
|
7
7
|
import {
|
|
8
8
|
detectProject
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-WEGMDILT.js";
|
|
10
10
|
|
|
11
11
|
// src/components/StatsApp.tsx
|
|
12
12
|
import React, { useState, useEffect } from "react";
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-SHO3ZXTH.js";
|
|
5
5
|
import {
|
|
6
6
|
shortName
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MCHTDD7R.js";
|
|
8
8
|
import {
|
|
9
9
|
Header
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-PD5OA3SZ.js";
|
|
11
|
+
import "./chunk-WEGMDILT.js";
|
|
12
12
|
import {
|
|
13
13
|
detectRegressions,
|
|
14
14
|
loadHistory
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
detectMonorepo,
|
|
15
15
|
runSickbay,
|
|
16
16
|
runSickbayMonorepo
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WEGMDILT.js";
|
|
18
18
|
import {
|
|
19
19
|
loadHistory
|
|
20
20
|
} from "./chunk-3OR2GFVE.js";
|
|
@@ -689,7 +689,7 @@ function TuiApp({
|
|
|
689
689
|
return () => timers.forEach(clearTimeout);
|
|
690
690
|
}, []);
|
|
691
691
|
useEffect7(() => {
|
|
692
|
-
checkForUpdate("1.15.
|
|
692
|
+
checkForUpdate("1.15.3").then((info) => {
|
|
693
693
|
if (info) setUpdateInfo(info);
|
|
694
694
|
});
|
|
695
695
|
}, []);
|
|
@@ -715,7 +715,7 @@ function TuiApp({
|
|
|
715
715
|
} catch {
|
|
716
716
|
}
|
|
717
717
|
try {
|
|
718
|
-
const { getDependencyTree } = await import("./dist-
|
|
718
|
+
const { getDependencyTree } = await import("./dist-G7GDVJT5.js");
|
|
719
719
|
const { saveDepTree } = await import("./history-XLNVZEDI.js");
|
|
720
720
|
const tree = await getDependencyTree(projectPath, result.projectInfo.packageManager);
|
|
721
721
|
saveDepTree(projectPath, tree);
|
|
@@ -819,7 +819,7 @@ function TuiApp({
|
|
|
819
819
|
const webReport = monorepoReportRef.current ?? reportRef.current;
|
|
820
820
|
if (!webReport) return;
|
|
821
821
|
try {
|
|
822
|
-
const { serveWeb } = await import("./web-
|
|
822
|
+
const { serveWeb } = await import("./web-EUG7IJKA.js");
|
|
823
823
|
const { default: openBrowser } = await import("open");
|
|
824
824
|
let aiService;
|
|
825
825
|
if (withAI && process.env.ANTHROPIC_API_KEY && !monorepoReportRef.current) {
|
|
@@ -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.15.
|
|
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.15.3")), 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 {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// ../../packages/core/dist/index.js
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync as existsSync27, readFileSync as readFileSync31 } from "fs";
|
|
3
|
+
import { basename as basename3, join as join38, relative as relative2 } from "path";
|
|
3
4
|
import { readFileSync, readdirSync, statSync, existsSync } from "fs";
|
|
4
5
|
import { join, extname } from "path";
|
|
5
6
|
import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
@@ -77,20 +78,20 @@ import { existsSync as existsSync21 } from "fs";
|
|
|
77
78
|
import { dirname as dirname4, join as join31, relative } from "path";
|
|
78
79
|
import { execa as execa10 } from "execa";
|
|
79
80
|
import { execa as execa11 } from "execa";
|
|
80
|
-
import { readFileSync as readFileSync26, readdirSync as readdirSync16, statSync as statSync15 } from "fs";
|
|
81
|
+
import { existsSync as existsSync22, readFileSync as readFileSync26, readdirSync as readdirSync16, statSync as statSync15 } from "fs";
|
|
81
82
|
import { join as join32, extname as extname7 } from "path";
|
|
82
|
-
import { readFileSync as readFileSync27, readdirSync as readdirSync17, statSync as statSync16, existsSync as
|
|
83
|
+
import { readFileSync as readFileSync27, readdirSync as readdirSync17, statSync as statSync16, existsSync as existsSync23 } from "fs";
|
|
83
84
|
import { join as join33, extname as extname8, basename as basename2 } from "path";
|
|
84
85
|
import { statSync as statSync17, readFileSync as readFileSync28 } from "fs";
|
|
85
86
|
import { join as join34 } from "path";
|
|
86
87
|
import { execa as execa12 } from "execa";
|
|
87
88
|
import { globby } from "globby";
|
|
88
|
-
import { readFileSync as readFileSync29, readdirSync as readdirSync18, statSync as statSync18, existsSync as
|
|
89
|
+
import { readFileSync as readFileSync29, readdirSync as readdirSync18, statSync as statSync18, existsSync as existsSync24 } from "fs";
|
|
89
90
|
import { join as join35, extname as extname9 } from "path";
|
|
90
|
-
import { existsSync as
|
|
91
|
+
import { existsSync as existsSync25 } from "fs";
|
|
91
92
|
import { join as join36 } from "path";
|
|
92
93
|
import { execa as execa13 } from "execa";
|
|
93
|
-
import { existsSync as
|
|
94
|
+
import { existsSync as existsSync26, readFileSync as readFileSync30 } from "fs";
|
|
94
95
|
import { join as join37, resolve as resolve2 } from "path";
|
|
95
96
|
import { globby as globby2 } from "globby";
|
|
96
97
|
import picomatch2 from "picomatch";
|
|
@@ -361,7 +362,7 @@ var NextBestPracticesAdvisor = class extends BaseAdvisor {
|
|
|
361
362
|
const hasStrictMode = NEXT_CONFIG_FILES.some((f) => {
|
|
362
363
|
try {
|
|
363
364
|
const content = readFileSync2(join2(projectPath, f), "utf-8");
|
|
364
|
-
return
|
|
365
|
+
return /reactStrictMode\s*:\s*true/.test(content);
|
|
365
366
|
} catch {
|
|
366
367
|
return false;
|
|
367
368
|
}
|
|
@@ -581,6 +582,8 @@ function scanComponentFiles(dir) {
|
|
|
581
582
|
var LOCK_FILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb"];
|
|
582
583
|
var UniversalBestPracticesAdvisor = class extends BaseAdvisor {
|
|
583
584
|
name = "universal-best-practices";
|
|
585
|
+
// Empty array means "applies to all frameworks" (see BaseAdvisor.isApplicableToContext).
|
|
586
|
+
// Explicit type annotation avoids the `never[]` widening that previously required a double cast.
|
|
584
587
|
frameworks = [];
|
|
585
588
|
async run(projectPath, _context) {
|
|
586
589
|
const recommendations = [];
|
|
@@ -2047,6 +2050,7 @@ async function detectProject(projectPath) {
|
|
|
2047
2050
|
}
|
|
2048
2051
|
function detectFramework(deps) {
|
|
2049
2052
|
if ("next" in deps) return "next";
|
|
2053
|
+
if ("@angular/core" in deps) return "angular";
|
|
2050
2054
|
if ("@vitejs/plugin-react" in deps || "vite" in deps) return "vite";
|
|
2051
2055
|
if ("react-scripts" in deps) return "cra";
|
|
2052
2056
|
if ("react" in deps) return "react";
|
|
@@ -2552,7 +2556,8 @@ var GitRunner = class extends BaseRunner {
|
|
|
2552
2556
|
const commitCount = logCountResult.status === "fulfilled" ? parseInt(logCountResult.value.stdout.trim(), 10) : 0;
|
|
2553
2557
|
const contributorCount = contributorsResult.status === "fulfilled" ? contributorsResult.value.stdout.trim().split("\n").filter(Boolean).length : 0;
|
|
2554
2558
|
const issues = [];
|
|
2555
|
-
const
|
|
2559
|
+
const lastCommitMonths = lastCommit.includes("year") ? parseGitRelativeCount(lastCommit) * 12 : lastCommit.includes("month") ? parseGitRelativeCount(lastCommit) : 0;
|
|
2560
|
+
const isStale = lastCommitMonths > staleMonths;
|
|
2556
2561
|
if (isStale) {
|
|
2557
2562
|
issues.push({
|
|
2558
2563
|
severity: "warning",
|
|
@@ -2595,6 +2600,12 @@ var GitRunner = class extends BaseRunner {
|
|
|
2595
2600
|
}
|
|
2596
2601
|
}
|
|
2597
2602
|
};
|
|
2603
|
+
function parseGitRelativeCount(s) {
|
|
2604
|
+
const trimmed = s.trim().toLowerCase();
|
|
2605
|
+
if (trimmed.startsWith("a ") || trimmed.startsWith("an ")) return 1;
|
|
2606
|
+
const match = trimmed.match(/^(\d+)\b/);
|
|
2607
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
2608
|
+
}
|
|
2598
2609
|
var HEAVY_DEPS = {
|
|
2599
2610
|
moment: {
|
|
2600
2611
|
alternative: "dayjs or date-fns",
|
|
@@ -4223,18 +4234,24 @@ function getUpdateType(current, latest) {
|
|
|
4223
4234
|
if (curMin < latMin) return "minor";
|
|
4224
4235
|
return "patch";
|
|
4225
4236
|
}
|
|
4237
|
+
var SOURCE_DIRS2 = ["src", "app", "lib"];
|
|
4226
4238
|
var COMPONENT_EXTENSIONS3 = /* @__PURE__ */ new Set([".tsx", ".jsx"]);
|
|
4227
4239
|
var ReactPerfRunner = class extends BaseRunner {
|
|
4228
4240
|
name = "react-perf";
|
|
4229
4241
|
category = "performance";
|
|
4230
4242
|
applicableFrameworks = ["react", "next", "remix"];
|
|
4243
|
+
async isApplicable(projectPath) {
|
|
4244
|
+
return SOURCE_DIRS2.some((dir) => existsSync22(join32(projectPath, dir)));
|
|
4245
|
+
}
|
|
4231
4246
|
async run(projectPath, options) {
|
|
4232
4247
|
const elapsed = timer();
|
|
4233
4248
|
const isExcluded = createExcludeFilter(options?.checkConfig?.exclude ?? []);
|
|
4234
4249
|
try {
|
|
4235
4250
|
const hasReactCompiler = detectReactCompiler(projectPath);
|
|
4236
4251
|
const findings = [];
|
|
4237
|
-
const files =
|
|
4252
|
+
const files = SOURCE_DIRS2.flatMap(
|
|
4253
|
+
(dir) => existsSync22(join32(projectPath, dir)) ? scanDirectory2(join32(projectPath, dir), projectPath, isExcluded) : []
|
|
4254
|
+
);
|
|
4238
4255
|
for (const file of files) {
|
|
4239
4256
|
findings.push(...analyzeFile(file.path, file.fullPath, file.lines));
|
|
4240
4257
|
}
|
|
@@ -4451,7 +4468,7 @@ var SecretsRunner = class extends BaseRunner {
|
|
|
4451
4468
|
const findings = [];
|
|
4452
4469
|
const envFiles = [".env", ".env.local", ".env.production"];
|
|
4453
4470
|
for (const envFile of envFiles) {
|
|
4454
|
-
if (
|
|
4471
|
+
if (existsSync23(join33(projectPath, envFile))) {
|
|
4455
4472
|
const gitignorePath = join33(projectPath, ".gitignore");
|
|
4456
4473
|
let ignored = false;
|
|
4457
4474
|
try {
|
|
@@ -4468,9 +4485,14 @@ var SecretsRunner = class extends BaseRunner {
|
|
|
4468
4485
|
}
|
|
4469
4486
|
}
|
|
4470
4487
|
}
|
|
4471
|
-
|
|
4472
|
-
|
|
4488
|
+
const SCAN_DIRS = ["src", "app", "lib", "config", "scripts", ".github/workflows"];
|
|
4489
|
+
for (const dir of SCAN_DIRS) {
|
|
4490
|
+
const fullDir = join33(projectPath, dir);
|
|
4491
|
+
if (existsSync23(fullDir)) {
|
|
4492
|
+
findings.push(...scanDirectory3(fullDir, projectPath, isExcluded));
|
|
4493
|
+
}
|
|
4473
4494
|
}
|
|
4495
|
+
findings.push(...scanRootFiles(projectPath, isExcluded));
|
|
4474
4496
|
const issues = findings.map((f) => ({
|
|
4475
4497
|
severity: "critical",
|
|
4476
4498
|
message: f.line > 0 ? `${f.file}:${f.line} \u2014 ${f.pattern} detected` : `${f.file} \u2014 ${f.pattern}`,
|
|
@@ -4541,6 +4563,29 @@ function scanDirectory3(dir, projectRoot, isExcluded) {
|
|
|
4541
4563
|
function isTestFile2(filename) {
|
|
4542
4564
|
return /\.(test|spec)\.(ts|tsx|js|jsx|mts|cts)$/.test(filename);
|
|
4543
4565
|
}
|
|
4566
|
+
function scanRootFiles(projectRoot, isExcluded) {
|
|
4567
|
+
const findings = [];
|
|
4568
|
+
try {
|
|
4569
|
+
for (const entry of readdirSync17(projectRoot)) {
|
|
4570
|
+
const fullPath = join33(projectRoot, entry);
|
|
4571
|
+
let stat;
|
|
4572
|
+
try {
|
|
4573
|
+
stat = statSync16(fullPath);
|
|
4574
|
+
} catch {
|
|
4575
|
+
continue;
|
|
4576
|
+
}
|
|
4577
|
+
if (!stat.isFile()) continue;
|
|
4578
|
+
if (!SCAN_EXTENSIONS.has(extname8(entry))) continue;
|
|
4579
|
+
if (SKIP_FILES.has(basename2(entry))) continue;
|
|
4580
|
+
if (entry === "package.json") continue;
|
|
4581
|
+
if (isTestFile2(entry)) continue;
|
|
4582
|
+
if (isExcluded(entry)) continue;
|
|
4583
|
+
findings.push(...scanFile(fullPath, projectRoot));
|
|
4584
|
+
}
|
|
4585
|
+
} catch {
|
|
4586
|
+
}
|
|
4587
|
+
return findings;
|
|
4588
|
+
}
|
|
4544
4589
|
function scanFile(filePath, projectRoot) {
|
|
4545
4590
|
const findings = [];
|
|
4546
4591
|
try {
|
|
@@ -4760,7 +4805,7 @@ var TodoScannerRunner = class extends BaseRunner {
|
|
|
4760
4805
|
name = "todo-scanner";
|
|
4761
4806
|
category = "code-quality";
|
|
4762
4807
|
async isApplicable(projectPath) {
|
|
4763
|
-
return
|
|
4808
|
+
return existsSync24(join35(projectPath, "src"));
|
|
4764
4809
|
}
|
|
4765
4810
|
async run(projectPath, options) {
|
|
4766
4811
|
const elapsed = timer();
|
|
@@ -4859,7 +4904,7 @@ var TypeScriptRunner = class extends BaseRunner {
|
|
|
4859
4904
|
name = "typescript";
|
|
4860
4905
|
category = "code-quality";
|
|
4861
4906
|
async isApplicable(projectPath) {
|
|
4862
|
-
return
|
|
4907
|
+
return existsSync25(join36(projectPath, "tsconfig.json"));
|
|
4863
4908
|
}
|
|
4864
4909
|
async run(projectPath, options) {
|
|
4865
4910
|
const elapsed = timer();
|
|
@@ -5114,12 +5159,12 @@ async function discoverPackages(rootPath, patterns) {
|
|
|
5114
5159
|
ignore: ["**/node_modules/**"]
|
|
5115
5160
|
});
|
|
5116
5161
|
return matched.filter((dir) => {
|
|
5117
|
-
return dir !== resolve2(rootPath) &&
|
|
5162
|
+
return dir !== resolve2(rootPath) && existsSync26(join37(dir, "package.json"));
|
|
5118
5163
|
});
|
|
5119
5164
|
}
|
|
5120
5165
|
function detectSignals(rootPath) {
|
|
5121
5166
|
const pnpmWs = join37(rootPath, "pnpm-workspace.yaml");
|
|
5122
|
-
if (
|
|
5167
|
+
if (existsSync26(pnpmWs)) {
|
|
5123
5168
|
try {
|
|
5124
5169
|
const content = readFileSync30(pnpmWs, "utf-8");
|
|
5125
5170
|
const patterns = parseYamlPackagesArray(content);
|
|
@@ -5129,7 +5174,7 @@ function detectSignals(rootPath) {
|
|
|
5129
5174
|
}
|
|
5130
5175
|
}
|
|
5131
5176
|
const lernaJson = join37(rootPath, "lerna.json");
|
|
5132
|
-
if (
|
|
5177
|
+
if (existsSync26(lernaJson)) {
|
|
5133
5178
|
try {
|
|
5134
5179
|
const lerna = JSON.parse(readFileSync30(lernaJson, "utf-8"));
|
|
5135
5180
|
const patterns = Array.isArray(lerna.packages) ? lerna.packages : ["packages/*"];
|
|
@@ -5139,27 +5184,27 @@ function detectSignals(rootPath) {
|
|
|
5139
5184
|
}
|
|
5140
5185
|
}
|
|
5141
5186
|
const pkgJson = join37(rootPath, "package.json");
|
|
5142
|
-
if (
|
|
5187
|
+
if (existsSync26(pkgJson)) {
|
|
5143
5188
|
try {
|
|
5144
5189
|
const pkg = JSON.parse(readFileSync30(pkgJson, "utf-8"));
|
|
5145
5190
|
const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : Array.isArray(pkg.workspaces?.packages) ? pkg.workspaces.packages : void 0;
|
|
5146
5191
|
if (workspaces && workspaces.length > 0) {
|
|
5147
|
-
if (
|
|
5192
|
+
if (existsSync26(join37(rootPath, "turbo.json"))) {
|
|
5148
5193
|
return { type: "turbo", patterns: workspaces };
|
|
5149
5194
|
}
|
|
5150
|
-
if (
|
|
5195
|
+
if (existsSync26(join37(rootPath, "nx.json"))) {
|
|
5151
5196
|
return { type: "nx", patterns: workspaces };
|
|
5152
5197
|
}
|
|
5153
|
-
const hasYarn =
|
|
5198
|
+
const hasYarn = existsSync26(join37(rootPath, "yarn.lock"));
|
|
5154
5199
|
return { type: hasYarn ? "yarn" : "npm", patterns: workspaces };
|
|
5155
5200
|
}
|
|
5156
5201
|
} catch {
|
|
5157
5202
|
}
|
|
5158
5203
|
}
|
|
5159
|
-
if (
|
|
5204
|
+
if (existsSync26(join37(rootPath, "turbo.json"))) {
|
|
5160
5205
|
return { type: "turbo", patterns: ["packages/*", "apps/*"] };
|
|
5161
5206
|
}
|
|
5162
|
-
if (
|
|
5207
|
+
if (existsSync26(join37(rootPath, "nx.json"))) {
|
|
5163
5208
|
return { type: "nx", patterns: ["packages/*", "apps/*", "libs/*"] };
|
|
5164
5209
|
}
|
|
5165
5210
|
return null;
|
|
@@ -5266,6 +5311,17 @@ var ALL_ADVISORS = [
|
|
|
5266
5311
|
new NextBestPracticesAdvisor(),
|
|
5267
5312
|
new UniversalBestPracticesAdvisor()
|
|
5268
5313
|
];
|
|
5314
|
+
function readPackageName(pkgPath) {
|
|
5315
|
+
const pkgJsonPath = join38(pkgPath, "package.json");
|
|
5316
|
+
if (existsSync27(pkgJsonPath)) {
|
|
5317
|
+
try {
|
|
5318
|
+
const pkg = JSON.parse(readFileSync31(pkgJsonPath, "utf-8"));
|
|
5319
|
+
if (pkg.name) return pkg.name;
|
|
5320
|
+
} catch {
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
return basename3(pkgPath);
|
|
5324
|
+
}
|
|
5269
5325
|
function getAvailableChecks(context) {
|
|
5270
5326
|
const runners = context ? ALL_RUNNERS.filter((r) => r.isApplicableToContext(context)) : ALL_RUNNERS;
|
|
5271
5327
|
return runners.map((r) => ({ name: r.name, category: r.category }));
|
|
@@ -5370,9 +5426,9 @@ async function runSickbayMonorepo(options = {}) {
|
|
|
5370
5426
|
monorepoInfo.packagePaths.map(async (pkgPath) => {
|
|
5371
5427
|
const pkgConfig = await loadConfig(pkgPath);
|
|
5372
5428
|
const mergedConfig = mergeConfigs(rootConfig, pkgConfig);
|
|
5429
|
+
options.onPackageStart?.(readPackageName(pkgPath));
|
|
5373
5430
|
const report = await runSickbay({ ...options, projectPath: pkgPath, _config: mergedConfig });
|
|
5374
5431
|
const context = await detectContext(pkgPath);
|
|
5375
|
-
options.onPackageStart?.(report.projectInfo.name);
|
|
5376
5432
|
const packageReport = {
|
|
5377
5433
|
name: report.projectInfo.name,
|
|
5378
5434
|
path: pkgPath,
|
package/dist/config.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -8,40 +8,54 @@ import {
|
|
|
8
8
|
} from "./chunk-TYG7ZQBP.js";
|
|
9
9
|
import {
|
|
10
10
|
Header
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-PD5OA3SZ.js";
|
|
12
12
|
import {
|
|
13
13
|
getScoreEmoji,
|
|
14
14
|
runSickbay,
|
|
15
15
|
runSickbayMonorepo
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WEGMDILT.js";
|
|
17
17
|
|
|
18
18
|
// src/index.ts
|
|
19
19
|
import { existsSync } from "fs";
|
|
20
20
|
import { homedir } from "os";
|
|
21
21
|
import { join } from "path";
|
|
22
|
-
import
|
|
22
|
+
import React7 from "react";
|
|
23
23
|
import { Command } from "commander";
|
|
24
24
|
import { config } from "dotenv";
|
|
25
25
|
import { render } from "ink";
|
|
26
26
|
|
|
27
27
|
// src/components/App.tsx
|
|
28
|
-
import
|
|
29
|
-
import { Box as
|
|
28
|
+
import React6, { useState, useEffect, useRef } from "react";
|
|
29
|
+
import { Box as Box6, Text as Text6, useApp } from "ink";
|
|
30
30
|
import Gradient from "ink-gradient";
|
|
31
31
|
import Spinner from "ink-spinner";
|
|
32
32
|
|
|
33
|
+
// src/components/BestPractices.tsx
|
|
34
|
+
import React from "react";
|
|
35
|
+
import { Box, Text } from "ink";
|
|
36
|
+
function BestPractices({ report }) {
|
|
37
|
+
const recs = report.recommendations;
|
|
38
|
+
if (!recs || recs.length === 0) return null;
|
|
39
|
+
const sorted = [...recs].sort((a, b) => {
|
|
40
|
+
const order = { recommend: 0, suggest: 1 };
|
|
41
|
+
return order[a.severity] - order[b.severity];
|
|
42
|
+
});
|
|
43
|
+
const display = sorted.slice(0, 5);
|
|
44
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "\u{1F4A1} Advisor:"), display.map((rec) => /* @__PURE__ */ React.createElement(Box, { key: rec.id, marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: rec.severity === "recommend" ? "yellow" : "gray" }, rec.severity === "recommend" ? "\u25CF " : "\u25CB "), /* @__PURE__ */ React.createElement(Text, { bold: true }, rec.title), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2014 ", rec.message))));
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
// src/components/CheckResult.tsx
|
|
34
|
-
import
|
|
35
|
-
import { Box as
|
|
48
|
+
import React3 from "react";
|
|
49
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
36
50
|
|
|
37
51
|
// src/components/ScoreBar.tsx
|
|
38
|
-
import
|
|
39
|
-
import { Box, Text } from "ink";
|
|
52
|
+
import React2 from "react";
|
|
53
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
40
54
|
function ScoreBar({ score, width = 20 }) {
|
|
41
55
|
const filled = Math.round(score / 100 * width);
|
|
42
56
|
const empty = width - filled;
|
|
43
57
|
const color = score >= 80 ? "green" : score >= 60 ? "yellow" : "red";
|
|
44
|
-
return /* @__PURE__ */
|
|
58
|
+
return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color }, "\u2588".repeat(filled)), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2591".repeat(empty)), /* @__PURE__ */ React2.createElement(Text2, { color }, " ", score, "/100"));
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
// src/components/CheckResult.tsx
|
|
@@ -61,20 +75,20 @@ var CATEGORY_ICONS = {
|
|
|
61
75
|
function CheckResultRow({ result }) {
|
|
62
76
|
const statusColor = result.status === "pass" ? "green" : result.status === "fail" ? "red" : result.status === "skipped" ? "gray" : "yellow";
|
|
63
77
|
const icon = CATEGORY_ICONS[result.category] ?? "\u2022";
|
|
64
|
-
return /* @__PURE__ */
|
|
65
|
-
|
|
78
|
+
return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, null, icon, " "), /* @__PURE__ */ React3.createElement(Text3, { bold: true }, result.name), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " via ", result.toolsUsed.join(", "))), /* @__PURE__ */ React3.createElement(Box3, { marginLeft: 2 }, /* @__PURE__ */ React3.createElement(ScoreBar, { score: result.score, width: 16 }), /* @__PURE__ */ React3.createElement(Text3, { color: statusColor }, " ", STATUS_ICONS[result.status], " ", result.status)), result.issues.slice(0, 3).map((issue) => /* @__PURE__ */ React3.createElement(Box3, { key: `${issue.severity}-${issue.message}`, marginLeft: 2 }, /* @__PURE__ */ React3.createElement(
|
|
79
|
+
Text3,
|
|
66
80
|
{
|
|
67
81
|
color: issue.severity === "critical" ? "red" : issue.severity === "warning" ? "yellow" : "gray"
|
|
68
82
|
},
|
|
69
83
|
issue.severity === "critical" ? " \u2717" : issue.severity === "warning" ? " \u26A0" : " \u2139",
|
|
70
84
|
" ",
|
|
71
85
|
issue.message
|
|
72
|
-
))), result.issues.length > 3 && /* @__PURE__ */
|
|
86
|
+
))), result.issues.length > 3 && /* @__PURE__ */ React3.createElement(Box3, { marginLeft: 4 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "... and ", result.issues.length - 3, " more")));
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
// src/components/QuickWins.tsx
|
|
76
|
-
import
|
|
77
|
-
import { Box as
|
|
90
|
+
import React4 from "react";
|
|
91
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
78
92
|
function replacePackageManager(cmd, pm) {
|
|
79
93
|
if (pm === "npm") return cmd;
|
|
80
94
|
const install = pm === "pnpm" ? "pnpm add" : pm === "yarn" ? "yarn add" : "bun add";
|
|
@@ -90,12 +104,12 @@ function QuickWins({ report }) {
|
|
|
90
104
|
return order[a.severity] - order[b.severity];
|
|
91
105
|
}).slice(0, 5);
|
|
92
106
|
if (fixes.length === 0) return null;
|
|
93
|
-
return /* @__PURE__ */
|
|
107
|
+
return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "\u{1F525} Quick Wins:"), fixes.map((fix) => /* @__PURE__ */ React4.createElement(Box4, { key: `${fix.severity}-${fix.message}`, marginLeft: 2 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "\u2192 "), /* @__PURE__ */ React4.createElement(Text4, null, fix.fix.description), fix.fix.command && /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, ": ", replacePackageManager(fix.fix.command, pm)))));
|
|
94
108
|
}
|
|
95
109
|
|
|
96
110
|
// src/components/Summary.tsx
|
|
97
|
-
import
|
|
98
|
-
import { Box as
|
|
111
|
+
import React5 from "react";
|
|
112
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
99
113
|
function formatDuration(ms) {
|
|
100
114
|
if (ms < 1e3) return `${ms}ms`;
|
|
101
115
|
const seconds = ms / 1e3;
|
|
@@ -105,7 +119,7 @@ function formatDuration(ms) {
|
|
|
105
119
|
return `${minutes}m ${remainingSeconds}s`;
|
|
106
120
|
}
|
|
107
121
|
function Summary({ report, scanDuration }) {
|
|
108
|
-
return /* @__PURE__ */
|
|
122
|
+
return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "\u2501".repeat(52)), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { bold: true }, "Overall Health Score: "), /* @__PURE__ */ React5.createElement(ScoreBar, { score: report.overallScore, width: 12 }), /* @__PURE__ */ React5.createElement(Text5, null, " ", getScoreEmoji(report.overallScore)), scanDuration != null && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ", formatDuration(scanDuration))), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, " \u2717 ", report.summary.critical, " critical"), /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, " \u26A0 ", report.summary.warnings, " warnings"), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, " i ", report.summary.info, " info")), report.quote && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { italic: true, dimColor: true }, '"', report.quote.text, '"'), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2014 ", report.quote.source)), report.config?.hasCustomConfig && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true, italic: true }, "Custom config active", report.config.disabledChecks.length > 0 && ` (${report.config.disabledChecks.length} check${report.config.disabledChecks.length === 1 ? "" : "s"} disabled)`)));
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
// src/components/App.tsx
|
|
@@ -176,7 +190,7 @@ function App({
|
|
|
176
190
|
setMonorepoReport(r);
|
|
177
191
|
setProjectName(`monorepo (${r.packages.length} packages)`);
|
|
178
192
|
try {
|
|
179
|
-
const { getDependencyTree } = await import("./dist-
|
|
193
|
+
const { getDependencyTree } = await import("./dist-G7GDVJT5.js");
|
|
180
194
|
const { saveDepTree } = await import("./history-XLNVZEDI.js");
|
|
181
195
|
const packages = {};
|
|
182
196
|
for (const pkg of r.packages) {
|
|
@@ -188,7 +202,7 @@ function App({
|
|
|
188
202
|
if (openWeb) {
|
|
189
203
|
setPhase("opening-web");
|
|
190
204
|
try {
|
|
191
|
-
const { serveWeb } = await import("./web-
|
|
205
|
+
const { serveWeb } = await import("./web-EUG7IJKA.js");
|
|
192
206
|
const { default: openBrowser } = await import("open");
|
|
193
207
|
let aiService;
|
|
194
208
|
if (enableAI && process.env.ANTHROPIC_API_KEY) {
|
|
@@ -245,7 +259,7 @@ function App({
|
|
|
245
259
|
} catch {
|
|
246
260
|
}
|
|
247
261
|
try {
|
|
248
|
-
const { getDependencyTree } = await import("./dist-
|
|
262
|
+
const { getDependencyTree } = await import("./dist-G7GDVJT5.js");
|
|
249
263
|
const { saveDepTree } = await import("./history-XLNVZEDI.js");
|
|
250
264
|
const tree = await getDependencyTree(projectPath, r.projectInfo.packageManager);
|
|
251
265
|
saveDepTree(projectPath, tree);
|
|
@@ -254,7 +268,7 @@ function App({
|
|
|
254
268
|
if (openWeb) {
|
|
255
269
|
setPhase("opening-web");
|
|
256
270
|
try {
|
|
257
|
-
const { serveWeb } = await import("./web-
|
|
271
|
+
const { serveWeb } = await import("./web-EUG7IJKA.js");
|
|
258
272
|
const { default: openBrowser } = await import("open");
|
|
259
273
|
let aiService;
|
|
260
274
|
if (enableAI && process.env.ANTHROPIC_API_KEY) {
|
|
@@ -280,20 +294,20 @@ function App({
|
|
|
280
294
|
setTimeout(() => exit(err), 100);
|
|
281
295
|
});
|
|
282
296
|
}, []);
|
|
283
|
-
return /* @__PURE__ */
|
|
297
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React6.createElement(Header, { projectName }), updateInfo && /* @__PURE__ */ React6.createElement(
|
|
284
298
|
UpdateNotice,
|
|
285
299
|
{
|
|
286
300
|
currentVersion: updateInfo.currentVersion,
|
|
287
301
|
latestVersion: updateInfo.latestVersion
|
|
288
302
|
}
|
|
289
|
-
), phase === "loading" && /* @__PURE__ */
|
|
303
|
+
), phase === "loading" && /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, isMonorepo ? /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " ", LOADING_MESSAGES[loadingMsgIdx])), scanningPackage && /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2192 "), /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, scanningPackage))) : /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Running health checks..."), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React6.createElement(ProgressList, { items: progress })))), phase === "error" && /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "\u2717 Error: ", error)), phase === "results" && monorepoReport && /* @__PURE__ */ React6.createElement(MonorepoSummaryTable, { report: monorepoReport, scanDuration }), phase === "results" && report && /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, report.checks.filter((c) => c.status !== "skipped").map((check) => /* @__PURE__ */ React6.createElement(CheckResultRow, { key: check.id, result: check })), /* @__PURE__ */ React6.createElement(Summary, { report, scanDuration }), /* @__PURE__ */ React6.createElement(QuickWins, { report }), /* @__PURE__ */ React6.createElement(BestPractices, { report }), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "View detailed report: "), /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, "sickbay --web"))), phase === "opening-web" && (monorepoReport ?? report) && /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, monorepoReport ? /* @__PURE__ */ React6.createElement(MonorepoSummaryTable, { report: monorepoReport, scanDuration }) : report ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, report.checks.filter((c) => c.status !== "skipped").map((check) => /* @__PURE__ */ React6.createElement(CheckResultRow, { key: check.id, result: check })), /* @__PURE__ */ React6.createElement(Summary, { report, scanDuration })) : null, /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, webUrl ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, "\u2713 Dashboard running at "), /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, webUrl), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (Ctrl+C to stop)")) : /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), " ", /* @__PURE__ */ React6.createElement(Gradient, { name: "retro" }, "Launching dashboard with AI insights...")))));
|
|
290
304
|
}
|
|
291
305
|
function MonorepoSummaryTable({
|
|
292
306
|
report,
|
|
293
307
|
scanDuration
|
|
294
308
|
}) {
|
|
295
309
|
const scoreColor = (score) => score >= 80 ? "green" : score >= 60 ? "yellow" : "red";
|
|
296
|
-
return /* @__PURE__ */
|
|
310
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box6, { marginBottom: 1 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Monorepo \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, report.monorepoType, " workspaces \xB7 "), /* @__PURE__ */ React6.createElement(Text6, null, report.packages.length, " packages")), report.packages.map((pkg) => /* @__PURE__ */ React6.createElement(Box6, { key: pkg.path, marginLeft: 2, gap: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: scoreColor(pkg.score) }, scoreBar(pkg.score)), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: scoreColor(pkg.score) }, String(pkg.score).padStart(3)), /* @__PURE__ */ React6.createElement(Text6, null, pkg.name), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, pkg.framework), pkg.summary.critical > 0 && /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, " ", pkg.summary.critical, " critical"))), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, gap: 2 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Overall: "), /* @__PURE__ */ React6.createElement(Text6, { color: scoreColor(report.overallScore), bold: true }, report.overallScore), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\xB7 "), /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, report.summary.critical, " critical"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\xB7 "), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, report.summary.warnings, " warnings"), scanDuration !== null && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\xB7 "), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, formatDuration2(scanDuration)))), report.quote && /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { italic: true, dimColor: true }, '"', report.quote.text, '"'), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \u2014 ", report.quote.source)), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Per-package details: "), /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, "sickbay --web")));
|
|
297
311
|
}
|
|
298
312
|
|
|
299
313
|
// src/index.ts
|
|
@@ -303,7 +317,7 @@ if (existsSync(globalConfigPath)) {
|
|
|
303
317
|
}
|
|
304
318
|
config({ debug: false, quiet: true });
|
|
305
319
|
var program = new Command();
|
|
306
|
-
program.name("sickbay").description("React project health check CLI").version("1.15.
|
|
320
|
+
program.name("sickbay").description("React project health check CLI").version("1.15.3", "-v, --version").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) => {
|
|
307
321
|
if (options.path && options.path !== process.cwd()) {
|
|
308
322
|
const projectEnvPath = join(options.path, ".env");
|
|
309
323
|
if (existsSync(projectEnvPath)) {
|
|
@@ -313,13 +327,13 @@ program.name("sickbay").description("React project health check CLI").version("1
|
|
|
313
327
|
const updatePromise = (async () => {
|
|
314
328
|
try {
|
|
315
329
|
const { checkForUpdate } = await import("./update-check-M7IFOMLJ.js");
|
|
316
|
-
return await checkForUpdate("1.15.
|
|
330
|
+
return await checkForUpdate("1.15.3");
|
|
317
331
|
} catch {
|
|
318
332
|
return null;
|
|
319
333
|
}
|
|
320
334
|
})();
|
|
321
335
|
const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
|
|
322
|
-
const { detectMonorepo, runSickbay: runSickbay2, runSickbayMonorepo: runSickbayMonorepo2 } = await import("./dist-
|
|
336
|
+
const { detectMonorepo, runSickbay: runSickbay2, runSickbayMonorepo: runSickbayMonorepo2 } = await import("./dist-G7GDVJT5.js");
|
|
323
337
|
const monorepoInfo = await detectMonorepo(options.path);
|
|
324
338
|
if (options.package && monorepoInfo.isMonorepo) {
|
|
325
339
|
const { readFileSync } = await import("fs");
|
|
@@ -347,7 +361,7 @@ program.name("sickbay").description("React project health check CLI").version("1
|
|
|
347
361
|
process.exit(0);
|
|
348
362
|
}
|
|
349
363
|
render(
|
|
350
|
-
|
|
364
|
+
React7.createElement(App, {
|
|
351
365
|
projectPath: targetPath,
|
|
352
366
|
checks,
|
|
353
367
|
openWeb: options.web,
|
|
@@ -383,7 +397,7 @@ program.name("sickbay").description("React project health check CLI").version("1
|
|
|
383
397
|
} catch {
|
|
384
398
|
}
|
|
385
399
|
try {
|
|
386
|
-
const { getDependencyTree } = await import("./dist-
|
|
400
|
+
const { getDependencyTree } = await import("./dist-G7GDVJT5.js");
|
|
387
401
|
const { saveDepTree } = await import("./history-XLNVZEDI.js");
|
|
388
402
|
const tree = await getDependencyTree(options.path, report.projectInfo.packageManager);
|
|
389
403
|
saveDepTree(options.path, tree);
|
|
@@ -393,7 +407,7 @@ program.name("sickbay").description("React project health check CLI").version("1
|
|
|
393
407
|
process.exit(0);
|
|
394
408
|
}
|
|
395
409
|
render(
|
|
396
|
-
|
|
410
|
+
React7.createElement(App, {
|
|
397
411
|
projectPath: options.path,
|
|
398
412
|
checks,
|
|
399
413
|
openWeb: options.web,
|
|
@@ -410,16 +424,16 @@ program.command("init").description("Initialize .sickbay/ folder and run an init
|
|
|
410
424
|
"add newly available checks to existing config without touching existing entries"
|
|
411
425
|
).option("--reset-config", "regenerate config from scratch (overwrites existing)").action(async (options) => {
|
|
412
426
|
if (options.resetConfig) {
|
|
413
|
-
const { generateConfigFile } = await import("./init-
|
|
427
|
+
const { generateConfigFile } = await import("./init-BBKWOHRV.js");
|
|
414
428
|
await generateConfigFile(options.path, { force: true });
|
|
415
429
|
return;
|
|
416
430
|
}
|
|
417
431
|
if (options.sync) {
|
|
418
|
-
const { syncConfigFile } = await import("./init-
|
|
432
|
+
const { syncConfigFile } = await import("./init-BBKWOHRV.js");
|
|
419
433
|
await syncConfigFile(options.path);
|
|
420
434
|
return;
|
|
421
435
|
}
|
|
422
|
-
const { initSickbay } = await import("./init-
|
|
436
|
+
const { initSickbay } = await import("./init-BBKWOHRV.js");
|
|
423
437
|
await initSickbay(options.path);
|
|
424
438
|
});
|
|
425
439
|
program.command("fix").description("Interactively fix issues found by sickbay scan").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 package (monorepo only)").option("--all", "apply all available fixes without prompting").option("--dry-run", "show what would be fixed without executing").option("--verbose", "show verbose output").action(async (options) => {
|
|
@@ -429,13 +443,13 @@ program.command("fix").description("Interactively fix issues found by sickbay sc
|
|
|
429
443
|
config({ path: projectEnvPath, override: true });
|
|
430
444
|
}
|
|
431
445
|
}
|
|
432
|
-
const { resolveProject } = await import("./resolve-package-
|
|
446
|
+
const { resolveProject } = await import("./resolve-package-CB3KDANT.js");
|
|
433
447
|
const resolution = await resolveProject(options.path, options.package);
|
|
434
|
-
const { FixApp } = await import("./FixApp-
|
|
448
|
+
const { FixApp } = await import("./FixApp-6JU4B3EJ.js");
|
|
435
449
|
const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
|
|
436
450
|
const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
|
|
437
451
|
render(
|
|
438
|
-
|
|
452
|
+
React7.createElement(FixApp, {
|
|
439
453
|
projectPath,
|
|
440
454
|
checks,
|
|
441
455
|
applyAll: options.all ?? false,
|
|
@@ -454,12 +468,12 @@ program.command("trend").description("Show score history and trends over time").
|
|
|
454
468
|
config({ path: projectEnvPath, override: true });
|
|
455
469
|
}
|
|
456
470
|
}
|
|
457
|
-
const { resolveProject } = await import("./resolve-package-
|
|
471
|
+
const { resolveProject } = await import("./resolve-package-CB3KDANT.js");
|
|
458
472
|
const resolution = await resolveProject(options.path, options.package);
|
|
459
473
|
const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
|
|
460
|
-
const { TrendApp } = await import("./TrendApp-
|
|
474
|
+
const { TrendApp } = await import("./TrendApp-TB4E7ZUU.js");
|
|
461
475
|
render(
|
|
462
|
-
|
|
476
|
+
React7.createElement(TrendApp, {
|
|
463
477
|
projectPath,
|
|
464
478
|
last: parseInt(options.last, 10),
|
|
465
479
|
jsonOutput: options.json ?? false,
|
|
@@ -476,12 +490,12 @@ program.command("stats").description("Show a quick codebase overview and project
|
|
|
476
490
|
config({ path: projectEnvPath, override: true });
|
|
477
491
|
}
|
|
478
492
|
}
|
|
479
|
-
const { resolveProject } = await import("./resolve-package-
|
|
493
|
+
const { resolveProject } = await import("./resolve-package-CB3KDANT.js");
|
|
480
494
|
const resolution = await resolveProject(options.path, options.package);
|
|
481
495
|
const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
|
|
482
|
-
const { StatsApp } = await import("./StatsApp-
|
|
496
|
+
const { StatsApp } = await import("./StatsApp-EYYWVSGZ.js");
|
|
483
497
|
render(
|
|
484
|
-
|
|
498
|
+
React7.createElement(StatsApp, {
|
|
485
499
|
projectPath,
|
|
486
500
|
jsonOutput: options.json ?? false,
|
|
487
501
|
isMonorepo: resolution.isMonorepo && !resolution.targetPath,
|
|
@@ -491,10 +505,10 @@ program.command("stats").description("Show a quick codebase overview and project
|
|
|
491
505
|
);
|
|
492
506
|
});
|
|
493
507
|
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) => {
|
|
494
|
-
const { TuiApp } = await import("./TuiApp-
|
|
508
|
+
const { TuiApp } = await import("./TuiApp-4JRQCD7M.js");
|
|
495
509
|
const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
|
|
496
510
|
render(
|
|
497
|
-
|
|
511
|
+
React7.createElement(TuiApp, {
|
|
498
512
|
projectPath: options.path,
|
|
499
513
|
checks,
|
|
500
514
|
watchEnabled: options.watch !== false,
|
|
@@ -511,12 +525,12 @@ program.command("doctor").description("Diagnose project setup and configuration
|
|
|
511
525
|
config({ path: projectEnvPath, override: true });
|
|
512
526
|
}
|
|
513
527
|
}
|
|
514
|
-
const { resolveProject } = await import("./resolve-package-
|
|
528
|
+
const { resolveProject } = await import("./resolve-package-CB3KDANT.js");
|
|
515
529
|
const resolution = await resolveProject(options.path, options.package);
|
|
516
530
|
const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
|
|
517
|
-
const { DoctorApp } = await import("./DoctorApp-
|
|
531
|
+
const { DoctorApp } = await import("./DoctorApp-VKJCRS2I.js");
|
|
518
532
|
render(
|
|
519
|
-
|
|
533
|
+
React7.createElement(DoctorApp, {
|
|
520
534
|
projectPath,
|
|
521
535
|
autoFix: options.fix ?? false,
|
|
522
536
|
jsonOutput: options.json ?? false,
|
|
@@ -527,13 +541,13 @@ program.command("doctor").description("Diagnose project setup and configuration
|
|
|
527
541
|
);
|
|
528
542
|
});
|
|
529
543
|
program.command("badge").description("Generate a health score badge for your README").option("-p, --path <path>", "project path", process.cwd()).option("--package <name>", "scope to a single package (monorepo only)").option("--html", "output HTML <img> tag instead of markdown").option("--url", "output bare badge URL only").option("--label <text>", "custom badge label", "sickbay").option("--scan", "run a fresh scan instead of using last report").action(async (options) => {
|
|
530
|
-
const { resolveProject } = await import("./resolve-package-
|
|
544
|
+
const { resolveProject } = await import("./resolve-package-CB3KDANT.js");
|
|
531
545
|
const resolution = await resolveProject(options.path, options.package);
|
|
532
546
|
const projectPath = resolution.isMonorepo ? resolution.targetPath ?? options.path : resolution.targetPath;
|
|
533
547
|
const { loadScoreFromLastReport, badgeUrl, badgeMarkdown, badgeHtml } = await import("./badge-KQ73KEIN.js");
|
|
534
548
|
let score = options.scan ? null : loadScoreFromLastReport(projectPath);
|
|
535
549
|
if (score === null) {
|
|
536
|
-
const { runSickbay: runSickbay2 } = await import("./dist-
|
|
550
|
+
const { runSickbay: runSickbay2 } = await import("./dist-G7GDVJT5.js");
|
|
537
551
|
const report = await runSickbay2({ projectPath, quotes: false });
|
|
538
552
|
try {
|
|
539
553
|
const { saveEntry, saveLastReport } = await import("./history-XLNVZEDI.js");
|
|
@@ -566,9 +580,9 @@ program.command("diff <branch>").description("Compare health score against anoth
|
|
|
566
580
|
}
|
|
567
581
|
}
|
|
568
582
|
const checks = options.checks ? options.checks.split(",").map((s) => s.trim()) : void 0;
|
|
569
|
-
const { DiffApp } = await import("./DiffApp-
|
|
583
|
+
const { DiffApp } = await import("./DiffApp-KR745TCV.js");
|
|
570
584
|
render(
|
|
571
|
-
|
|
585
|
+
React7.createElement(DiffApp, {
|
|
572
586
|
projectPath: options.path,
|
|
573
587
|
branch,
|
|
574
588
|
jsonOutput: options.json ?? false,
|
|
@@ -106,7 +106,7 @@ async function serveWeb(report, preferredPort = 3030, aiService) {
|
|
|
106
106
|
if (url === "/sickbay-config.json") {
|
|
107
107
|
const basePath = "isMonorepo" in report ? report.rootPath : report.projectPath;
|
|
108
108
|
try {
|
|
109
|
-
const { loadConfig } = await import("./dist-
|
|
109
|
+
const { loadConfig } = await import("./dist-G7GDVJT5.js");
|
|
110
110
|
const config = await loadConfig(basePath);
|
|
111
111
|
if (config) {
|
|
112
112
|
res.writeHead(200, { "Content-Type": "application/json" });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sickbay",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.4",
|
|
4
4
|
"description": "Zero-config health check CLI for JavaScript and TypeScript projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -63,5 +63,6 @@
|
|
|
63
63
|
"tsup": "catalog:",
|
|
64
64
|
"typescript": "catalog:",
|
|
65
65
|
"vitest": "catalog:"
|
|
66
|
-
}
|
|
66
|
+
},
|
|
67
|
+
"_bundleNote": "tsup bundles @nebulord/sickbay-core inline (see tsup.config.ts noExternal). Core's runtime deps are mirrored in `dependencies` below so the bundled require() calls resolve at runtime. Do NOT delete entries that look unused from cli source — they are needed by core's bundled code. Drift is enforced by `pnpm check:bundled-deps` (scripts/check-bundled-deps.mjs)."
|
|
67
68
|
}
|