@kb-labs/devkit 1.0.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.
- package/.cursorrules +32 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/actions/setup-node-pnpm/action.yml +47 -0
- package/.github/workflow-templates/ci.yml +13 -0
- package/.github/workflow-templates/drift-check.yml +10 -0
- package/.github/workflow-templates/profiles-validate.yml +16 -0
- package/.github/workflow-templates/release.yml +8 -0
- package/.github/workflows/ci-reusable.yml +131 -0
- package/.github/workflows/drift-check-reusable.yml +23 -0
- package/.github/workflows/fixtures.yml +74 -0
- package/.github/workflows/profiles-validate-reusable.yml +67 -0
- package/.github/workflows/release-reusable.yml +50 -0
- package/.vscode/settings.json +23 -0
- package/AGENTS.md +130 -0
- package/LICENSE +21 -0
- package/README.md +1542 -0
- package/agents/devkit-maintainer/context.globs +15 -0
- package/agents/devkit-maintainer/permissions.yml +17 -0
- package/agents/devkit-maintainer/prompt.md +28 -0
- package/agents/devkit-maintainer/runbook.md +31 -0
- package/agents/docs-crafter/prompt.md +24 -0
- package/agents/docs-crafter/runbook.md +18 -0
- package/agents/release-manager/context.globs +7 -0
- package/agents/release-manager/prompt.md +27 -0
- package/agents/release-manager/runbook.md +17 -0
- package/agents/test-generator/context.globs +7 -0
- package/agents/test-generator/prompt.md +27 -0
- package/agents/test-generator/runbook.md +18 -0
- package/bin/devkit-architecture.mjs +1225 -0
- package/bin/devkit-build-order.mjs +500 -0
- package/bin/devkit-check-build-readiness.mjs +222 -0
- package/bin/devkit-check-commands.mjs +394 -0
- package/bin/devkit-check-configs.mjs +461 -0
- package/bin/devkit-check-deprecated.mjs +532 -0
- package/bin/devkit-check-duplicates.mjs +431 -0
- package/bin/devkit-check-exports.mjs +561 -0
- package/bin/devkit-check-imports.mjs +712 -0
- package/bin/devkit-check-paths.mjs +670 -0
- package/bin/devkit-check-scripts.mjs +335 -0
- package/bin/devkit-check-structure.mjs +495 -0
- package/bin/devkit-check-types.mjs +450 -0
- package/bin/devkit-ci.mjs +261 -0
- package/bin/devkit-core-gate.mjs +225 -0
- package/bin/devkit-fix-deps.mjs +1169 -0
- package/bin/devkit-freshness.mjs +199 -0
- package/bin/devkit-health.mjs +489 -0
- package/bin/devkit-migrate-configs.mjs +370 -0
- package/bin/devkit-paths.mjs +192 -0
- package/bin/devkit-stats.mjs +453 -0
- package/bin/devkit-sync.mjs +12 -0
- package/bin/devkit-tsup-external.mjs +148 -0
- package/bin/devkit-types-audit.mjs +615 -0
- package/bin/devkit-types-order.mjs +637 -0
- package/bin/devkit-validate-naming.mjs +203 -0
- package/bin/devkit-visualize.mjs +452 -0
- package/bin/kb-devkit-qa-history.mjs +453 -0
- package/bin/kb-devkit-qa.mjs +915 -0
- package/eslint/node.js +134 -0
- package/eslint/react.js +260 -0
- package/package.json +186 -0
- package/prettier/index.json +10 -0
- package/sync/index.mjs +693 -0
- package/templates/configs/README.md +306 -0
- package/templates/configs/eslint.config.js +27 -0
- package/templates/configs/package.json.bin +25 -0
- package/templates/configs/package.json.lib +30 -0
- package/templates/configs/tsconfig.build.json +15 -0
- package/templates/configs/tsconfig.json +9 -0
- package/templates/configs/tsup.config.bin.ts +34 -0
- package/templates/configs/tsup.config.cli.ts +41 -0
- package/templates/configs/tsup.config.dual.ts +46 -0
- package/templates/configs/tsup.config.ts +36 -0
- package/templates/package-json/README.md +290 -0
- package/templates/package-json/package.json.bin.template +40 -0
- package/templates/package-json/package.json.template +45 -0
- package/tsconfig/base.json +21 -0
- package/tsconfig/cli.json +12 -0
- package/tsconfig/dist/__tests__/cache.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/cache.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/cache.spec.js +85 -0
- package/tsconfig/dist/__tests__/cache.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.js +153 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.js +99 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/kb-error.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/kb-error.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/kb-error.spec.js +190 -0
- package/tsconfig/dist/__tests__/kb-error.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.js +142 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.js +100 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/product-config.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/product-config.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/product-config.spec.js +298 -0
- package/tsconfig/dist/__tests__/product-config.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/runtime.spec.d.ts +2 -0
- package/tsconfig/dist/__tests__/runtime.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/runtime.spec.js +127 -0
- package/tsconfig/dist/__tests__/runtime.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.js +251 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/validate-config.spec.d.ts +2 -0
- package/tsconfig/dist/__tests__/validate-config.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/validate-config.spec.js +14 -0
- package/tsconfig/dist/__tests__/validate-config.spec.js.map +1 -0
- package/tsconfig/dist/api/init-workspace.d.ts +10 -0
- package/tsconfig/dist/api/init-workspace.d.ts.map +1 -0
- package/tsconfig/dist/api/init-workspace.js +191 -0
- package/tsconfig/dist/api/init-workspace.js.map +1 -0
- package/tsconfig/dist/api/product-config.d.ts +21 -0
- package/tsconfig/dist/api/product-config.d.ts.map +1 -0
- package/tsconfig/dist/api/product-config.js +192 -0
- package/tsconfig/dist/api/product-config.js.map +1 -0
- package/tsconfig/dist/api/read-config.d.ts +22 -0
- package/tsconfig/dist/api/read-config.d.ts.map +1 -0
- package/tsconfig/dist/api/read-config.js +105 -0
- package/tsconfig/dist/api/read-config.js.map +1 -0
- package/tsconfig/dist/api/upsert-lockfile.d.ts +10 -0
- package/tsconfig/dist/api/upsert-lockfile.d.ts.map +1 -0
- package/tsconfig/dist/api/upsert-lockfile.js +63 -0
- package/tsconfig/dist/api/upsert-lockfile.js.map +1 -0
- package/tsconfig/dist/cache/fs-cache.d.ts +38 -0
- package/tsconfig/dist/cache/fs-cache.d.ts.map +1 -0
- package/tsconfig/dist/cache/fs-cache.js +142 -0
- package/tsconfig/dist/cache/fs-cache.js.map +1 -0
- package/tsconfig/dist/errors/kb-error.d.ts +32 -0
- package/tsconfig/dist/errors/kb-error.d.ts.map +1 -0
- package/tsconfig/dist/errors/kb-error.js +54 -0
- package/tsconfig/dist/errors/kb-error.js.map +1 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.d.ts +2 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.d.ts.map +1 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.js +22 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.js.map +1 -0
- package/tsconfig/dist/fs/fs.d.ts +6 -0
- package/tsconfig/dist/fs/fs.d.ts.map +1 -0
- package/tsconfig/dist/fs/fs.js +12 -0
- package/tsconfig/dist/fs/fs.js.map +1 -0
- package/tsconfig/dist/fs/index.d.ts +2 -0
- package/tsconfig/dist/fs/index.d.ts.map +1 -0
- package/tsconfig/dist/fs/index.js +2 -0
- package/tsconfig/dist/fs/index.js.map +1 -0
- package/tsconfig/dist/hash/config-hash.d.ts +17 -0
- package/tsconfig/dist/hash/config-hash.d.ts.map +1 -0
- package/tsconfig/dist/hash/config-hash.js +55 -0
- package/tsconfig/dist/hash/config-hash.js.map +1 -0
- package/tsconfig/dist/index.d.ts +5 -0
- package/tsconfig/dist/index.d.ts.map +1 -0
- package/tsconfig/dist/index.js +5 -0
- package/tsconfig/dist/index.js.map +1 -0
- package/tsconfig/dist/lockfile/lockfile.d.ts +54 -0
- package/tsconfig/dist/lockfile/lockfile.d.ts.map +1 -0
- package/tsconfig/dist/lockfile/lockfile.js +141 -0
- package/tsconfig/dist/lockfile/lockfile.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.js +65 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.js +34 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js +90 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js.map +1 -0
- package/tsconfig/dist/logging/index.d.ts +6 -0
- package/tsconfig/dist/logging/index.d.ts.map +1 -0
- package/tsconfig/dist/logging/index.js +6 -0
- package/tsconfig/dist/logging/index.js.map +1 -0
- package/tsconfig/dist/logging/logger.d.ts +9 -0
- package/tsconfig/dist/logging/logger.d.ts.map +1 -0
- package/tsconfig/dist/logging/logger.js +101 -0
- package/tsconfig/dist/logging/logger.js.map +1 -0
- package/tsconfig/dist/logging/redaction.d.ts +7 -0
- package/tsconfig/dist/logging/redaction.d.ts.map +1 -0
- package/tsconfig/dist/logging/redaction.js +23 -0
- package/tsconfig/dist/logging/redaction.js.map +1 -0
- package/tsconfig/dist/logging/sinks/json.d.ts +4 -0
- package/tsconfig/dist/logging/sinks/json.d.ts.map +1 -0
- package/tsconfig/dist/logging/sinks/json.js +23 -0
- package/tsconfig/dist/logging/sinks/json.js.map +1 -0
- package/tsconfig/dist/logging/sinks/stdout.d.ts +3 -0
- package/tsconfig/dist/logging/sinks/stdout.d.ts.map +1 -0
- package/tsconfig/dist/logging/sinks/stdout.js +24 -0
- package/tsconfig/dist/logging/sinks/stdout.js.map +1 -0
- package/tsconfig/dist/logging/types/index.d.ts +2 -0
- package/tsconfig/dist/logging/types/index.d.ts.map +1 -0
- package/tsconfig/dist/logging/types/index.js +2 -0
- package/tsconfig/dist/logging/types/index.js.map +1 -0
- package/tsconfig/dist/logging/types/types.d.ts +37 -0
- package/tsconfig/dist/logging/types/types.d.ts.map +1 -0
- package/tsconfig/dist/logging/types/types.js +2 -0
- package/tsconfig/dist/logging/types/types.js.map +1 -0
- package/tsconfig/dist/merge/layered-merge.d.ts +16 -0
- package/tsconfig/dist/merge/layered-merge.d.ts.map +1 -0
- package/tsconfig/dist/merge/layered-merge.js +97 -0
- package/tsconfig/dist/merge/layered-merge.js.map +1 -0
- package/tsconfig/dist/preset/resolve-preset.d.ts +29 -0
- package/tsconfig/dist/preset/resolve-preset.d.ts.map +1 -0
- package/tsconfig/dist/preset/resolve-preset.js +104 -0
- package/tsconfig/dist/preset/resolve-preset.js.map +1 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.d.ts +2 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.d.ts.map +1 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.js +25 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.js.map +1 -0
- package/tsconfig/dist/repo/index.d.ts +2 -0
- package/tsconfig/dist/repo/index.d.ts.map +1 -0
- package/tsconfig/dist/repo/index.js +2 -0
- package/tsconfig/dist/repo/index.js.map +1 -0
- package/tsconfig/dist/repo/repo.d.ts +6 -0
- package/tsconfig/dist/repo/repo.d.ts.map +1 -0
- package/tsconfig/dist/repo/repo.js +25 -0
- package/tsconfig/dist/repo/repo.js.map +1 -0
- package/tsconfig/dist/runtime/index.d.ts +2 -0
- package/tsconfig/dist/runtime/index.d.ts.map +1 -0
- package/tsconfig/dist/runtime/index.js +2 -0
- package/tsconfig/dist/runtime/index.js.map +1 -0
- package/tsconfig/dist/runtime/runtime.d.ts +46 -0
- package/tsconfig/dist/runtime/runtime.d.ts.map +1 -0
- package/tsconfig/dist/runtime/runtime.js +126 -0
- package/tsconfig/dist/runtime/runtime.js.map +1 -0
- package/tsconfig/dist/tsconfig.tools.tsbuildinfo +1 -0
- package/tsconfig/dist/tsconfig.tsbuildinfo +1 -0
- package/tsconfig/dist/types/index.d.ts +2 -0
- package/tsconfig/dist/types/index.d.ts.map +1 -0
- package/tsconfig/dist/types/index.js +2 -0
- package/tsconfig/dist/types/index.js.map +1 -0
- package/tsconfig/dist/types/init.d.ts +34 -0
- package/tsconfig/dist/types/init.d.ts.map +1 -0
- package/tsconfig/dist/types/init.js +6 -0
- package/tsconfig/dist/types/init.js.map +1 -0
- package/tsconfig/dist/types/preset.d.ts +27 -0
- package/tsconfig/dist/types/preset.d.ts.map +1 -0
- package/tsconfig/dist/types/preset.js +6 -0
- package/tsconfig/dist/types/preset.js.map +1 -0
- package/tsconfig/dist/types/types.d.ts +6 -0
- package/tsconfig/dist/types/types.d.ts.map +1 -0
- package/tsconfig/dist/types/types.js +2 -0
- package/tsconfig/dist/types/types.js.map +1 -0
- package/tsconfig/dist/utils/__tests__/env.spec.d.ts +2 -0
- package/tsconfig/dist/utils/__tests__/env.spec.d.ts.map +1 -0
- package/tsconfig/dist/utils/__tests__/env.spec.js +33 -0
- package/tsconfig/dist/utils/__tests__/env.spec.js.map +1 -0
- package/tsconfig/dist/utils/env.d.ts +7 -0
- package/tsconfig/dist/utils/env.d.ts.map +1 -0
- package/tsconfig/dist/utils/env.js +25 -0
- package/tsconfig/dist/utils/env.js.map +1 -0
- package/tsconfig/dist/utils/fs-atomic.d.ts +15 -0
- package/tsconfig/dist/utils/fs-atomic.d.ts.map +1 -0
- package/tsconfig/dist/utils/fs-atomic.js +45 -0
- package/tsconfig/dist/utils/fs-atomic.js.map +1 -0
- package/tsconfig/dist/utils/index.d.ts +3 -0
- package/tsconfig/dist/utils/index.d.ts.map +1 -0
- package/tsconfig/dist/utils/index.js +3 -0
- package/tsconfig/dist/utils/index.js.map +1 -0
- package/tsconfig/dist/utils/paths.d.ts +21 -0
- package/tsconfig/dist/utils/paths.d.ts.map +1 -0
- package/tsconfig/dist/utils/paths.js +32 -0
- package/tsconfig/dist/utils/paths.js.map +1 -0
- package/tsconfig/dist/utils/product-normalize.d.ts +27 -0
- package/tsconfig/dist/utils/product-normalize.d.ts.map +1 -0
- package/tsconfig/dist/utils/product-normalize.js +45 -0
- package/tsconfig/dist/utils/product-normalize.js.map +1 -0
- package/tsconfig/dist/validation/validate-config.d.ts +7 -0
- package/tsconfig/dist/validation/validate-config.d.ts.map +1 -0
- package/tsconfig/dist/validation/validate-config.js +22 -0
- package/tsconfig/dist/validation/validate-config.js.map +1 -0
- package/tsconfig/lib.json +13 -0
- package/tsconfig/node.json +12 -0
- package/tsconfig/react-app.json +8 -0
- package/tsconfig/react-lib.json +8 -0
- package/tsconfig/test.json +15 -0
- package/tsup/bin.js +172 -0
- package/tsup/dual.js +155 -0
- package/tsup/external-sync.mjs +41 -0
- package/tsup/external.mjs +109 -0
- package/tsup/node.js +65 -0
- package/tsup/react-lib.js +18 -0
- package/tsup/sdk.js +118 -0
- package/vite/react-app.js +50 -0
- package/vitest/node.js +37 -0
- package/vitest/react.js +39 -0
- package/vitest/vitest-setup.ts +9 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @kb-labs/devkit - Package Build Freshness Tracker
|
|
5
|
+
*
|
|
6
|
+
* Detects stale packages in monorepo dependency chains.
|
|
7
|
+
* Critical: identifies when package A uses old version of dependency B,
|
|
8
|
+
* even though B has been rebuilt with newer version.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx kb-devkit-freshness # Default table output
|
|
12
|
+
* npx kb-devkit-freshness --json # JSON for AI agents
|
|
13
|
+
* npx kb-devkit-freshness --md # Markdown for docs
|
|
14
|
+
* npx kb-devkit-freshness --tree # Dependency tree view
|
|
15
|
+
* npx kb-devkit-freshness --package=cli-core # Single package analysis
|
|
16
|
+
* npx kb-devkit-freshness --only-stale # Show only stale packages
|
|
17
|
+
* npx kb-devkit-freshness --age-days=30 # Packages not built in 30+ days
|
|
18
|
+
* npx kb-devkit-freshness --high-impact=5 # Packages affecting 5+ others
|
|
19
|
+
* npx kb-devkit-freshness --suggest-rebuild # Show rebuild order
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
|
|
26
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
|
|
28
|
+
// ANSI colors
|
|
29
|
+
const colors = {
|
|
30
|
+
reset: '\x1b[0m',
|
|
31
|
+
red: '\x1b[31m',
|
|
32
|
+
green: '\x1b[32m',
|
|
33
|
+
yellow: '\x1b[33m',
|
|
34
|
+
blue: '\x1b[34m',
|
|
35
|
+
magenta: '\x1b[35m',
|
|
36
|
+
cyan: '\x1b[36m',
|
|
37
|
+
gray: '\x1b[90m',
|
|
38
|
+
bold: '\x1b[1m',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function log(message, color = 'reset') {
|
|
42
|
+
if (!options.json) {
|
|
43
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse CLI arguments
|
|
48
|
+
const args = process.argv.slice(2);
|
|
49
|
+
const options = {
|
|
50
|
+
package: args.find((arg) => arg.startsWith('--package='))?.split('=')[1],
|
|
51
|
+
json: args.includes('--json'),
|
|
52
|
+
md: args.includes('--md'),
|
|
53
|
+
tree: args.includes('--tree'),
|
|
54
|
+
onlyStale: args.includes('--only-stale'),
|
|
55
|
+
suggestRebuild: args.includes('--suggest-rebuild'),
|
|
56
|
+
ageDays: parseInt(args.find((arg) => arg.startsWith('--age-days='))?.split('=')[1]) || null,
|
|
57
|
+
highImpact: parseInt(args.find((arg) => arg.startsWith('--high-impact='))?.split('=')[1]) || null,
|
|
58
|
+
help: args.includes('--help') || args.includes('-h'),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Show help text
|
|
63
|
+
*/
|
|
64
|
+
function showHelp() {
|
|
65
|
+
console.log(`
|
|
66
|
+
📦 KB Labs Package Build Freshness Tracker
|
|
67
|
+
|
|
68
|
+
Detects stale packages in monorepo dependency chains.
|
|
69
|
+
|
|
70
|
+
USAGE:
|
|
71
|
+
npx kb-devkit-freshness [options]
|
|
72
|
+
|
|
73
|
+
OPTIONS:
|
|
74
|
+
--json Output JSON format (AI-optimized)
|
|
75
|
+
--md Output Markdown format
|
|
76
|
+
--tree Show dependency tree view
|
|
77
|
+
--package=<name> Analyze single package
|
|
78
|
+
--only-stale Show only stale packages
|
|
79
|
+
--age-days=<N> Show packages not built in N+ days
|
|
80
|
+
--high-impact=<N> Show packages affecting N+ others
|
|
81
|
+
--suggest-rebuild Show suggested rebuild order
|
|
82
|
+
-h, --help Show this help
|
|
83
|
+
|
|
84
|
+
EXAMPLES:
|
|
85
|
+
# Default table output
|
|
86
|
+
npx kb-devkit-freshness
|
|
87
|
+
|
|
88
|
+
# JSON for AI agents
|
|
89
|
+
npx kb-devkit-freshness --json
|
|
90
|
+
|
|
91
|
+
# Single package analysis with tree view
|
|
92
|
+
npx kb-devkit-freshness --package=cli-core --tree
|
|
93
|
+
|
|
94
|
+
# Find high-impact stale packages
|
|
95
|
+
npx kb-devkit-freshness --only-stale --high-impact=5
|
|
96
|
+
|
|
97
|
+
# Packages not built in 30+ days
|
|
98
|
+
npx kb-devkit-freshness --age-days=30 --suggest-rebuild
|
|
99
|
+
|
|
100
|
+
STALENESS CRITERIA:
|
|
101
|
+
1. Version Mismatch - Built version != current version
|
|
102
|
+
2. Time-based - Source files newer than dist files
|
|
103
|
+
3. Dependency Staleness - Dependency rebuilt after this package (CRITICAL!)
|
|
104
|
+
|
|
105
|
+
For more info: https://github.com/kb-labs/kb-labs
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Main function
|
|
111
|
+
*/
|
|
112
|
+
async function main() {
|
|
113
|
+
if (options.help) {
|
|
114
|
+
showHelp();
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const rootDir = process.cwd();
|
|
119
|
+
|
|
120
|
+
if (!options.json && !options.md) {
|
|
121
|
+
log('\n🔍 KB Labs Package Build Freshness Tracker\n', 'blue');
|
|
122
|
+
if (options.package) {
|
|
123
|
+
log(`Analyzing package: ${options.package}\n`, 'gray');
|
|
124
|
+
} else {
|
|
125
|
+
log('Analyzing all packages...\n', 'gray');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Phase 1: Find packages
|
|
131
|
+
const { findPackages } = await import('../src/freshness/metadata.js');
|
|
132
|
+
const packages = findPackages(rootDir, options.package);
|
|
133
|
+
|
|
134
|
+
if (packages.length === 0) {
|
|
135
|
+
log('⚠️ No KB Labs packages found', 'yellow');
|
|
136
|
+
log(' Run this command from the monorepo root\n', 'gray');
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!options.json && !options.md) {
|
|
141
|
+
log(`Found ${packages.length} package(s) to analyze\n`, 'gray');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Phase 2: Collect metadata
|
|
145
|
+
const { collectAllMetadata } = await import('../src/freshness/metadata.js');
|
|
146
|
+
const metadata = await collectAllMetadata(packages);
|
|
147
|
+
|
|
148
|
+
// Phase 3: Build dependency graph
|
|
149
|
+
const { buildDependencyGraph } = await import('../src/freshness/graph.js');
|
|
150
|
+
const graph = buildDependencyGraph(packages, metadata);
|
|
151
|
+
|
|
152
|
+
// Phase 4: Analyze staleness
|
|
153
|
+
const { analyzeStaleness } = await import('../src/freshness/analyzer.js');
|
|
154
|
+
const freshnessResults = new Map();
|
|
155
|
+
|
|
156
|
+
for (const pkg of packages) {
|
|
157
|
+
const packageJson = JSON.parse(fs.readFileSync(pkg, 'utf-8'));
|
|
158
|
+
const packageName = packageJson.name;
|
|
159
|
+
const meta = metadata.get(packageName);
|
|
160
|
+
|
|
161
|
+
if (meta) {
|
|
162
|
+
const result = analyzeStaleness(packageName, meta, graph, metadata, options);
|
|
163
|
+
freshnessResults.set(packageName, result);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Phase 5: Propagate staleness and calculate impact
|
|
168
|
+
const { propagateStaleness } = await import('../src/freshness/propagate.js');
|
|
169
|
+
propagateStaleness(freshnessResults, graph);
|
|
170
|
+
|
|
171
|
+
// Phase 6: Format output
|
|
172
|
+
const formatters = await import('../src/freshness/formatters.js');
|
|
173
|
+
|
|
174
|
+
if (options.json) {
|
|
175
|
+
formatters.printJSON(freshnessResults, graph, metadata, options);
|
|
176
|
+
} else if (options.md) {
|
|
177
|
+
formatters.printMarkdown(freshnessResults, graph, metadata, options);
|
|
178
|
+
} else if (options.tree) {
|
|
179
|
+
formatters.printTree(freshnessResults, graph, metadata, options);
|
|
180
|
+
} else {
|
|
181
|
+
formatters.printTable(freshnessResults, graph, metadata, options);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Exit code: 1 if stale packages found, 0 otherwise
|
|
185
|
+
const staleCount = Array.from(freshnessResults.values()).filter(
|
|
186
|
+
(r) => r.status === 'stale'
|
|
187
|
+
).length;
|
|
188
|
+
|
|
189
|
+
process.exit(staleCount > 0 ? 1 : 0);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
192
|
+
if (process.env.DEBUG) {
|
|
193
|
+
console.error(error.stack);
|
|
194
|
+
}
|
|
195
|
+
process.exit(2);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
main();
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @kb-labs/devkit - Health Check
|
|
5
|
+
*
|
|
6
|
+
* Comprehensive monorepo health check that runs all critical validations
|
|
7
|
+
* and provides a single health score with actionable recommendations.
|
|
8
|
+
*
|
|
9
|
+
* Checks:
|
|
10
|
+
* 1. Missing runtime dependencies (imports not in package.json)
|
|
11
|
+
* 2. Workspace vs link inconsistencies (cross-repo deps)
|
|
12
|
+
* 3. Build failures
|
|
13
|
+
* 4. TypeScript errors
|
|
14
|
+
* 5. Circular dependencies
|
|
15
|
+
* 6. Orphan packages
|
|
16
|
+
* 7. Duplicate dependencies
|
|
17
|
+
* 8. Naming conventions
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* kb-devkit-health # Full health check
|
|
21
|
+
* kb-devkit-health --quick # Skip slow checks (builds, types)
|
|
22
|
+
* kb-devkit-health --json # JSON output
|
|
23
|
+
* kb-devkit-health --package cli-core # Check specific package
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import fs from 'node:fs';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import { execSync } from 'node:child_process';
|
|
30
|
+
|
|
31
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
|
|
33
|
+
// ANSI colors
|
|
34
|
+
const colors = {
|
|
35
|
+
reset: '\x1b[0m',
|
|
36
|
+
red: '\x1b[31m',
|
|
37
|
+
green: '\x1b[32m',
|
|
38
|
+
yellow: '\x1b[33m',
|
|
39
|
+
blue: '\x1b[34m',
|
|
40
|
+
cyan: '\x1b[36m',
|
|
41
|
+
gray: '\x1b[90m',
|
|
42
|
+
bold: '\x1b[1m',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Parse CLI args
|
|
46
|
+
const args = process.argv.slice(2);
|
|
47
|
+
const quick = args.includes('--quick');
|
|
48
|
+
const jsonOutput = args.includes('--json');
|
|
49
|
+
const packageFilter = args.find((arg) => arg.startsWith('--package='))?.split('=')[1];
|
|
50
|
+
|
|
51
|
+
// Find monorepo root
|
|
52
|
+
function findMonorepoRoot() {
|
|
53
|
+
let dir = process.cwd();
|
|
54
|
+
while (dir !== '/') {
|
|
55
|
+
if (fs.existsSync(path.join(dir, 'pnpm-workspace.yaml'))) {
|
|
56
|
+
return dir;
|
|
57
|
+
}
|
|
58
|
+
dir = path.dirname(dir);
|
|
59
|
+
}
|
|
60
|
+
throw new Error('Not in a pnpm workspace');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const rootDir = findMonorepoRoot();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Find all packages in monorepo
|
|
67
|
+
*/
|
|
68
|
+
function findAllPackages() {
|
|
69
|
+
const packages = [];
|
|
70
|
+
const searchDirs = [
|
|
71
|
+
'packages',
|
|
72
|
+
'kb-labs-*/packages',
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
for (const pattern of searchDirs) {
|
|
76
|
+
const dirs = pattern.includes('*')
|
|
77
|
+
? execSync(`find ${rootDir} -type d -path "*/${pattern}" 2>/dev/null || true`, { encoding: 'utf-8' })
|
|
78
|
+
.split('\n')
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
: [path.join(rootDir, pattern)];
|
|
81
|
+
|
|
82
|
+
for (const dir of dirs) {
|
|
83
|
+
if (!fs.existsSync(dir)) {continue;}
|
|
84
|
+
|
|
85
|
+
const subDirs = fs.readdirSync(dir, { withFileTypes: true })
|
|
86
|
+
.filter((d) => d.isDirectory())
|
|
87
|
+
.map((d) => path.join(dir, d.name));
|
|
88
|
+
|
|
89
|
+
for (const subDir of subDirs) {
|
|
90
|
+
const pkgPath = path.join(subDir, 'package.json');
|
|
91
|
+
if (fs.existsSync(pkgPath)) {
|
|
92
|
+
try {
|
|
93
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
94
|
+
if (packageFilter && !pkg.name.includes(packageFilter)) {continue;}
|
|
95
|
+
packages.push({ name: pkg.name, path: subDir, pkg });
|
|
96
|
+
} catch (err) {
|
|
97
|
+
// Skip invalid package.json
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return packages;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if dist/ is stale (older than src/)
|
|
109
|
+
*/
|
|
110
|
+
function isDistStale(pkgPath) {
|
|
111
|
+
const distFile = path.join(pkgPath, 'dist/index.js');
|
|
112
|
+
if (!fs.existsSync(distFile)) {return false;}
|
|
113
|
+
|
|
114
|
+
const distMtime = fs.statSync(distFile).mtime.getTime();
|
|
115
|
+
|
|
116
|
+
// Check all source files
|
|
117
|
+
const srcDir = path.join(pkgPath, 'src');
|
|
118
|
+
if (!fs.existsSync(srcDir)) {return false;}
|
|
119
|
+
|
|
120
|
+
const checkDir = (dir) => {
|
|
121
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
const fullPath = path.join(dir, entry.name);
|
|
124
|
+
if (entry.isDirectory()) {
|
|
125
|
+
if (checkDir(fullPath)) {return true;}
|
|
126
|
+
} else if (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) {
|
|
127
|
+
const srcMtime = fs.statSync(fullPath).mtime.getTime();
|
|
128
|
+
if (srcMtime > distMtime) {return true;}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return checkDir(srcDir);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check 1: Missing runtime dependencies
|
|
139
|
+
* Parse dist/index.js and check if all imports are declared in package.json
|
|
140
|
+
* NOTE: Results may be inaccurate if dist/ is stale
|
|
141
|
+
*/
|
|
142
|
+
function checkMissingRuntimeDeps(packages) {
|
|
143
|
+
const issues = [];
|
|
144
|
+
const stalePackages = [];
|
|
145
|
+
|
|
146
|
+
for (const { name, path: pkgPath, pkg } of packages) {
|
|
147
|
+
const distFile = path.join(pkgPath, 'dist/index.js');
|
|
148
|
+
if (!fs.existsSync(distFile)) {continue;}
|
|
149
|
+
|
|
150
|
+
// Check if dist is stale
|
|
151
|
+
if (isDistStale(pkgPath)) {
|
|
152
|
+
stalePackages.push(name);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const content = fs.readFileSync(distFile, 'utf-8');
|
|
156
|
+
const importMatches = content.matchAll(/from ['"]([^'"]+)['"]/g);
|
|
157
|
+
|
|
158
|
+
const declaredDeps = {
|
|
159
|
+
...pkg.dependencies,
|
|
160
|
+
...pkg.peerDependencies,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Node.js built-in modules (don't need to be in package.json)
|
|
164
|
+
const nodeBuiltins = new Set([
|
|
165
|
+
'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants',
|
|
166
|
+
'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https',
|
|
167
|
+
'module', 'net', 'os', 'path', 'perf_hooks', 'process', 'punycode',
|
|
168
|
+
'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'sys',
|
|
169
|
+
'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi', 'worker_threads',
|
|
170
|
+
'zlib', 'fs/promises',
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
const missing = new Set();
|
|
174
|
+
|
|
175
|
+
for (const [, importPath] of importMatches) {
|
|
176
|
+
// Skip relative imports and node builtins
|
|
177
|
+
if (importPath.startsWith('.') || importPath.startsWith('node:')) {continue;}
|
|
178
|
+
|
|
179
|
+
// Extract package name (handle @scope/package)
|
|
180
|
+
const pkgName = importPath.startsWith('@')
|
|
181
|
+
? importPath.split('/').slice(0, 2).join('/')
|
|
182
|
+
: importPath.split('/')[0];
|
|
183
|
+
|
|
184
|
+
// Skip Node.js built-ins
|
|
185
|
+
if (nodeBuiltins.has(pkgName)) {continue;}
|
|
186
|
+
|
|
187
|
+
// Check if declared
|
|
188
|
+
if (!declaredDeps[pkgName]) {
|
|
189
|
+
missing.add(pkgName);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (missing.size > 0) {
|
|
194
|
+
issues.push({
|
|
195
|
+
package: name,
|
|
196
|
+
missing: Array.from(missing),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { issues, stalePackages };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Check 2: Workspace vs link inconsistencies
|
|
206
|
+
* Find cross-repo dependencies that use workspace:* instead of link:
|
|
207
|
+
*/
|
|
208
|
+
function checkWorkspaceLinkIssues(packages) {
|
|
209
|
+
const issues = [];
|
|
210
|
+
|
|
211
|
+
// Build map of package locations by repo
|
|
212
|
+
const packagesByRepo = new Map();
|
|
213
|
+
for (const { name, path: pkgPath } of packages) {
|
|
214
|
+
const repo = pkgPath.split('/').find((p) => p.startsWith('kb-labs-'));
|
|
215
|
+
if (!packagesByRepo.has(repo)) {
|
|
216
|
+
packagesByRepo.set(repo, []);
|
|
217
|
+
}
|
|
218
|
+
packagesByRepo.get(repo).push(name);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for (const { name, path: pkgPath, pkg } of packages) {
|
|
222
|
+
const myRepo = pkgPath.split('/').find((p) => p.startsWith('kb-labs-'));
|
|
223
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
224
|
+
|
|
225
|
+
for (const [depName, depVersion] of Object.entries(deps)) {
|
|
226
|
+
if (depVersion !== 'workspace:*') {continue;}
|
|
227
|
+
|
|
228
|
+
// Check if this dep is in a different repo
|
|
229
|
+
let depRepo = null;
|
|
230
|
+
for (const [repo, pkgs] of packagesByRepo.entries()) {
|
|
231
|
+
if (pkgs.includes(depName)) {
|
|
232
|
+
depRepo = repo;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (depRepo && depRepo !== myRepo) {
|
|
238
|
+
issues.push({
|
|
239
|
+
package: name,
|
|
240
|
+
dependency: depName,
|
|
241
|
+
myRepo,
|
|
242
|
+
depRepo,
|
|
243
|
+
currentVersion: depVersion,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return issues;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Check 3: Build failures
|
|
254
|
+
* Try to build each package and collect errors
|
|
255
|
+
*/
|
|
256
|
+
function checkBuilds(packages) {
|
|
257
|
+
const issues = [];
|
|
258
|
+
|
|
259
|
+
for (const { name, path: pkgPath } of packages) {
|
|
260
|
+
try {
|
|
261
|
+
execSync('pnpm run build', {
|
|
262
|
+
cwd: pkgPath,
|
|
263
|
+
stdio: 'pipe',
|
|
264
|
+
encoding: 'utf-8',
|
|
265
|
+
timeout: 30000, // 30s timeout per package
|
|
266
|
+
});
|
|
267
|
+
} catch (err) {
|
|
268
|
+
issues.push({
|
|
269
|
+
package: name,
|
|
270
|
+
error: err.stderr?.trim() || err.message,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return issues;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Check 4: TypeScript errors (using existing types-audit)
|
|
280
|
+
*/
|
|
281
|
+
function checkTypeScriptErrors() {
|
|
282
|
+
try {
|
|
283
|
+
const output = execSync('node kb-labs-devkit/bin/devkit-types-audit.mjs --errors-only --json', {
|
|
284
|
+
cwd: rootDir,
|
|
285
|
+
encoding: 'utf-8',
|
|
286
|
+
stdio: 'pipe',
|
|
287
|
+
});
|
|
288
|
+
const result = JSON.parse(output);
|
|
289
|
+
return result.packagesWithErrors || [];
|
|
290
|
+
} catch (err) {
|
|
291
|
+
return null; // Tool might not exist yet
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Run all checks and compute health score
|
|
297
|
+
*/
|
|
298
|
+
async function runHealthCheck() {
|
|
299
|
+
const startTime = Date.now();
|
|
300
|
+
const packages = findAllPackages();
|
|
301
|
+
|
|
302
|
+
if (!jsonOutput) {
|
|
303
|
+
console.log(`${colors.cyan}${colors.bold}🏥 KB Labs Monorepo Health Check${colors.reset}\n`);
|
|
304
|
+
console.log(`${colors.gray}Analyzing ${packages.length} package(s)...${colors.reset}\n`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const depsCheck = checkMissingRuntimeDeps(packages);
|
|
308
|
+
const results = {
|
|
309
|
+
missingRuntimeDeps: depsCheck.issues,
|
|
310
|
+
stalePackages: depsCheck.stalePackages,
|
|
311
|
+
workspaceLinkIssues: checkWorkspaceLinkIssues(packages),
|
|
312
|
+
buildFailures: quick ? [] : checkBuilds(packages),
|
|
313
|
+
typeScriptErrors: quick ? [] : checkTypeScriptErrors(),
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Compute health score
|
|
317
|
+
let score = 100;
|
|
318
|
+
const criticalIssues = [];
|
|
319
|
+
const warnings = [];
|
|
320
|
+
|
|
321
|
+
// Stale packages warning (doesn't affect score, but important)
|
|
322
|
+
if (results.stalePackages.length > 0) {
|
|
323
|
+
warnings.push({
|
|
324
|
+
type: 'stale-builds',
|
|
325
|
+
severity: 'warning',
|
|
326
|
+
count: results.stalePackages.length,
|
|
327
|
+
message: `${results.stalePackages.length} package(s) with stale dist/ (results may be inaccurate)`,
|
|
328
|
+
details: results.stalePackages,
|
|
329
|
+
recommendation: 'Run: npx kb-devkit-freshness --only-stale',
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Missing runtime deps: -10 per package
|
|
334
|
+
if (results.missingRuntimeDeps.length > 0) {
|
|
335
|
+
const penalty = Math.min(results.missingRuntimeDeps.length * 10, 50);
|
|
336
|
+
score -= penalty;
|
|
337
|
+
criticalIssues.push({
|
|
338
|
+
type: 'missing-runtime-deps',
|
|
339
|
+
severity: 'critical',
|
|
340
|
+
count: results.missingRuntimeDeps.length,
|
|
341
|
+
message: `${results.missingRuntimeDeps.length} package(s) with missing runtime dependencies`,
|
|
342
|
+
details: results.missingRuntimeDeps,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Workspace vs link issues: -5 per package
|
|
347
|
+
if (results.workspaceLinkIssues.length > 0) {
|
|
348
|
+
const penalty = Math.min(results.workspaceLinkIssues.length * 5, 25);
|
|
349
|
+
score -= penalty;
|
|
350
|
+
criticalIssues.push({
|
|
351
|
+
type: 'workspace-link-issues',
|
|
352
|
+
severity: 'critical',
|
|
353
|
+
count: results.workspaceLinkIssues.length,
|
|
354
|
+
message: `${results.workspaceLinkIssues.length} cross-repo dep(s) using workspace:* instead of link:`,
|
|
355
|
+
details: results.workspaceLinkIssues,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Build failures: -15 per package
|
|
360
|
+
if (results.buildFailures.length > 0) {
|
|
361
|
+
const penalty = Math.min(results.buildFailures.length * 15, 60);
|
|
362
|
+
score -= penalty;
|
|
363
|
+
criticalIssues.push({
|
|
364
|
+
type: 'build-failures',
|
|
365
|
+
severity: 'critical',
|
|
366
|
+
count: results.buildFailures.length,
|
|
367
|
+
message: `${results.buildFailures.length} package(s) failed to build`,
|
|
368
|
+
details: results.buildFailures,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// TypeScript errors: warning only
|
|
373
|
+
if (results.typeScriptErrors && results.typeScriptErrors.length > 0) {
|
|
374
|
+
warnings.push({
|
|
375
|
+
type: 'typescript-errors',
|
|
376
|
+
severity: 'warning',
|
|
377
|
+
count: results.typeScriptErrors.length,
|
|
378
|
+
message: `${results.typeScriptErrors.length} package(s) with TypeScript errors`,
|
|
379
|
+
details: results.typeScriptErrors,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Compute grade
|
|
384
|
+
const grade = score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'F';
|
|
385
|
+
|
|
386
|
+
const duration = Date.now() - startTime;
|
|
387
|
+
|
|
388
|
+
// Output results
|
|
389
|
+
if (jsonOutput) {
|
|
390
|
+
console.log(JSON.stringify({
|
|
391
|
+
score,
|
|
392
|
+
grade,
|
|
393
|
+
duration,
|
|
394
|
+
packages: packages.length,
|
|
395
|
+
criticalIssues,
|
|
396
|
+
warnings,
|
|
397
|
+
details: results,
|
|
398
|
+
}, null, 2));
|
|
399
|
+
} else {
|
|
400
|
+
printResults(score, grade, criticalIssues, warnings, duration);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Exit with error if critical issues found
|
|
404
|
+
if (criticalIssues.length > 0) {
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Pretty print results
|
|
411
|
+
*/
|
|
412
|
+
function printResults(score, grade, criticalIssues, warnings, duration) {
|
|
413
|
+
// Critical issues
|
|
414
|
+
if (criticalIssues.length > 0) {
|
|
415
|
+
console.log(`${colors.red}${colors.bold}❌ CRITICAL ISSUES (blocking)${colors.reset}\n`);
|
|
416
|
+
for (const issue of criticalIssues) {
|
|
417
|
+
console.log(` ${colors.red}•${colors.reset} ${issue.message}`);
|
|
418
|
+
|
|
419
|
+
// Show top 5 details
|
|
420
|
+
const details = issue.details.slice(0, 5);
|
|
421
|
+
for (const detail of details) {
|
|
422
|
+
if (issue.type === 'missing-runtime-deps') {
|
|
423
|
+
console.log(` ${colors.gray}${detail.package}: ${detail.missing.join(', ')}${colors.reset}`);
|
|
424
|
+
} else if (issue.type === 'workspace-link-issues') {
|
|
425
|
+
console.log(` ${colors.gray}${detail.package} → ${detail.dependency} (${detail.myRepo} → ${detail.depRepo})${colors.reset}`);
|
|
426
|
+
} else if (issue.type === 'build-failures') {
|
|
427
|
+
console.log(` ${colors.gray}${detail.package}${colors.reset}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (issue.details.length > 5) {
|
|
432
|
+
console.log(` ${colors.gray}... and ${issue.details.length - 5} more${colors.reset}`);
|
|
433
|
+
}
|
|
434
|
+
console.log();
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Warnings
|
|
439
|
+
if (warnings.length > 0) {
|
|
440
|
+
console.log(`${colors.yellow}${colors.bold}⚠️ WARNINGS${colors.reset}\n`);
|
|
441
|
+
for (const warning of warnings) {
|
|
442
|
+
console.log(` ${colors.yellow}•${colors.reset} ${warning.message}`);
|
|
443
|
+
|
|
444
|
+
// Show stale packages
|
|
445
|
+
if (warning.type === 'stale-builds' && warning.details.length > 0) {
|
|
446
|
+
const showPackages = warning.details.slice(0, 5);
|
|
447
|
+
for (const pkg of showPackages) {
|
|
448
|
+
console.log(` ${colors.gray}${pkg}${colors.reset}`);
|
|
449
|
+
}
|
|
450
|
+
if (warning.details.length > 5) {
|
|
451
|
+
console.log(` ${colors.gray}... and ${warning.details.length - 5} more${colors.reset}`);
|
|
452
|
+
}
|
|
453
|
+
console.log(` ${colors.cyan}→ ${warning.recommendation}${colors.reset}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
console.log();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Health score
|
|
460
|
+
const scoreColor = score >= 80 ? colors.green : score >= 60 ? colors.yellow : colors.red;
|
|
461
|
+
console.log(`${colors.bold}Health Score:${colors.reset} ${scoreColor}${score}/100 (Grade ${grade})${colors.reset}`);
|
|
462
|
+
console.log(`${colors.gray}Duration: ${duration}ms${colors.reset}\n`);
|
|
463
|
+
|
|
464
|
+
// Recommendations
|
|
465
|
+
if (criticalIssues.length > 0) {
|
|
466
|
+
console.log(`${colors.bold}Recommended Actions:${colors.reset}\n`);
|
|
467
|
+
|
|
468
|
+
if (criticalIssues.some((i) => i.type === 'missing-runtime-deps')) {
|
|
469
|
+
console.log(` 1. Fix missing runtime dependencies:`);
|
|
470
|
+
console.log(` ${colors.cyan}kb-devkit-fix-deps --add-missing${colors.reset}\n`);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (criticalIssues.some((i) => i.type === 'workspace-link-issues')) {
|
|
474
|
+
console.log(` 2. Fix workspace vs link issues manually in package.json`);
|
|
475
|
+
console.log(` Change ${colors.gray}workspace:*${colors.reset} → ${colors.cyan}link:../../../kb-labs-xxx/packages/xxx${colors.reset}\n`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (criticalIssues.some((i) => i.type === 'build-failures')) {
|
|
479
|
+
console.log(` 3. Fix build failures:`);
|
|
480
|
+
console.log(` ${colors.cyan}pnpm --filter <package> run build${colors.reset}\n`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Run health check
|
|
486
|
+
runHealthCheck().catch((err) => {
|
|
487
|
+
console.error(`${colors.red}Health check failed: ${err.message}${colors.reset}`);
|
|
488
|
+
process.exit(1);
|
|
489
|
+
});
|