@kb-labs/quality-entry 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +120 -0
  2. package/dist/cli/commands/build-order.d.ts +16 -0
  3. package/dist/cli/commands/build-order.js +113 -0
  4. package/dist/cli/commands/build-order.js.map +1 -0
  5. package/dist/cli/commands/check-builds.d.ts +10 -0
  6. package/dist/cli/commands/check-builds.js +93 -0
  7. package/dist/cli/commands/check-builds.js.map +1 -0
  8. package/dist/cli/commands/check-tests.d.ts +10 -0
  9. package/dist/cli/commands/check-tests.js +114 -0
  10. package/dist/cli/commands/check-tests.js.map +1 -0
  11. package/dist/cli/commands/check-types.d.ts +10 -0
  12. package/dist/cli/commands/check-types.js +108 -0
  13. package/dist/cli/commands/check-types.js.map +1 -0
  14. package/dist/cli/commands/cycles.d.ts +17 -0
  15. package/dist/cli/commands/cycles.js +85 -0
  16. package/dist/cli/commands/cycles.js.map +1 -0
  17. package/dist/cli/commands/dead-code.d.ts +10 -0
  18. package/dist/cli/commands/dead-code.js +217 -0
  19. package/dist/cli/commands/dead-code.js.map +1 -0
  20. package/dist/cli/commands/fix-deps.d.ts +28 -0
  21. package/dist/cli/commands/fix-deps.js +389 -0
  22. package/dist/cli/commands/fix-deps.js.map +1 -0
  23. package/dist/cli/commands/flags.d.ts +344 -0
  24. package/dist/cli/commands/flags.js +298 -0
  25. package/dist/cli/commands/flags.js.map +1 -0
  26. package/dist/cli/commands/health.d.ts +10 -0
  27. package/dist/cli/commands/health.js +210 -0
  28. package/dist/cli/commands/health.js.map +1 -0
  29. package/dist/cli/commands/index.d.ts +14 -0
  30. package/dist/cli/commands/index.js +1747 -0
  31. package/dist/cli/commands/index.js.map +1 -0
  32. package/dist/cli/commands/stats.d.ts +10 -0
  33. package/dist/cli/commands/stats.js +282 -0
  34. package/dist/cli/commands/stats.js.map +1 -0
  35. package/dist/cli/commands/visualize.d.ts +26 -0
  36. package/dist/cli/commands/visualize.js +210 -0
  37. package/dist/cli/commands/visualize.js.map +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +666 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/manifest.d.ts +168 -0
  42. package/dist/manifest.js +666 -0
  43. package/dist/manifest.js.map +1 -0
  44. package/dist/rest/handlers/build-order-handler.d.ts +17 -0
  45. package/dist/rest/handlers/build-order-handler.js +28 -0
  46. package/dist/rest/handlers/build-order-handler.js.map +1 -0
  47. package/dist/rest/handlers/builds-handler.d.ts +12 -0
  48. package/dist/rest/handlers/builds-handler.js +17 -0
  49. package/dist/rest/handlers/builds-handler.js.map +1 -0
  50. package/dist/rest/handlers/cycles-handler.d.ts +13 -0
  51. package/dist/rest/handlers/cycles-handler.js +23 -0
  52. package/dist/rest/handlers/cycles-handler.js.map +1 -0
  53. package/dist/rest/handlers/dependencies-handler.d.ts +14 -0
  54. package/dist/rest/handlers/dependencies-handler.js +19 -0
  55. package/dist/rest/handlers/dependencies-handler.js.map +1 -0
  56. package/dist/rest/handlers/graph-handler.d.ts +30 -0
  57. package/dist/rest/handlers/graph-handler.js +155 -0
  58. package/dist/rest/handlers/graph-handler.js.map +1 -0
  59. package/dist/rest/handlers/health-handler.d.ts +15 -0
  60. package/dist/rest/handlers/health-handler.js +19 -0
  61. package/dist/rest/handlers/health-handler.js.map +1 -0
  62. package/dist/rest/handlers/stale-handler.d.ts +30 -0
  63. package/dist/rest/handlers/stale-handler.js +20 -0
  64. package/dist/rest/handlers/stale-handler.js.map +1 -0
  65. package/dist/rest/handlers/stats-handler.d.ts +16 -0
  66. package/dist/rest/handlers/stats-handler.js +29 -0
  67. package/dist/rest/handlers/stats-handler.js.map +1 -0
  68. package/dist/rest/handlers/tests-handler.d.ts +14 -0
  69. package/dist/rest/handlers/tests-handler.js +19 -0
  70. package/dist/rest/handlers/tests-handler.js.map +1 -0
  71. package/dist/rest/handlers/types-handler.d.ts +12 -0
  72. package/dist/rest/handlers/types-handler.js +17 -0
  73. package/dist/rest/handlers/types-handler.js.map +1 -0
  74. package/package.json +98 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/commands/check-types.ts"],"names":[],"mappings":"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","file":"check-types.js","sourcesContent":["/**\n * quality:check-types - TypeScript type safety analysis\n *\n * Analyzes TypeScript type errors, warnings, and coverage across monorepo.\n * Uses TypeScript Compiler API for semantic analysis.\n */\n\nimport { defineCommand, type PluginContextV3 } from '@kb-labs/sdk';\nimport { analyzeTypes } from '@kb-labs/quality-core/types';\nimport { CACHE_KEYS, type TypeAnalysisResult } from '@kb-labs/quality-contracts';\nimport { type CheckTypesFlags } from './flags.js';\n\n// Input type with backward compatibility\ntype CheckTypesInput = CheckTypesFlags & { argv?: string[] };\n\ntype CheckTypesCommandResult = {\n exitCode: number;\n result?: TypeAnalysisResult;\n meta?: Record<string, unknown>;\n};\n\nexport default defineCommand({\n id: 'quality:check-types',\n description: 'Analyze TypeScript type safety across monorepo',\n\n handler: {\n async execute(\n ctx: PluginContextV3,\n input: CheckTypesInput\n ): Promise<CheckTypesCommandResult> {\n const { ui, platform } = ctx;\n\n // V3: Flags come in input.flags object (not auto-merged)\n const flags = (input as any).flags ?? input;\n\n const cacheKey = `${CACHE_KEYS.TYPE_ANALYSIS}:${flags.package || 'all'}`;\n\n // Check cache unless refresh requested\n if (!flags.refresh) {\n const cached = await platform.cache.get<TypeAnalysisResult>(cacheKey);\n if (cached) {\n outputTypeAnalysis({ ...cached, cached: true }, flags, ui);\n return { exitCode: cached.totalErrors > 0 ? 1 : 0, result: cached };\n }\n }\n\n // Run type analysis\n const result = await analyzeTypes(ctx.cwd, {\n packageFilter: flags.package,\n errorsOnly: flags['errors-only'],\n });\n\n // Cache result (10 min TTL)\n await platform.cache.set(cacheKey, result, 10 * 60 * 1000);\n\n // Track analytics\n await platform.analytics.track('quality:check-types', {\n packageFilter: flags.package || 'all',\n totalPackages: result.totalPackages,\n totalErrors: result.totalErrors,\n avgCoverage: result.avgCoverage,\n cached: false,\n });\n\n // Output results\n outputTypeAnalysis({ ...result, cached: false }, flags, ui);\n\n return {\n exitCode: result.totalErrors > 0 ? 1 : 0,\n result,\n meta: { cached: false },\n };\n },\n },\n});\n\n/**\n * Output type analysis results\n */\nfunction outputTypeAnalysis(\n result: TypeAnalysisResult & { cached: boolean },\n flags: CheckTypesInput,\n ui: any\n): void {\n if (flags.json) {\n ui?.json?.(result);\n return;\n }\n\n const errorsOnly = flags['errors-only'] || false;\n\n // Build sections\n const sections: Array<{ header: string; items: string[] }> = [];\n\n // Status section\n const statusIcon = result.totalErrors === 0 ? '✅' : '❌';\n const statusText =\n result.totalErrors === 0 ? 'All packages passed type checks' : 'Type errors detected';\n const statusItems = [\n `${statusIcon} ${statusText}`,\n `Analyzed: ${result.totalPackages} package(s)`,\n `Errors: ${result.totalErrors}`,\n `Warnings: ${result.totalWarnings}`,\n `Avg Coverage: ${result.avgCoverage.toFixed(1)}%`,\n ];\n\n sections.push({ header: 'Type Safety Status', items: statusItems });\n\n // Packages with errors\n if (result.packagesWithErrors > 0) {\n const errorItems: string[] = [];\n const packagesToShow = result.packages\n .filter((pkg) => !errorsOnly || pkg.errors > 0)\n .slice(0, 10);\n\n for (const pkg of packagesToShow) {\n const status = pkg.errors > 0 ? '❌' : '✅';\n errorItems.push(`${status} ${pkg.name}`);\n\n const details = [\n pkg.errors > 0 ? `${pkg.errors} error(s)` : null,\n pkg.warnings > 0 ? `${pkg.warnings} warning(s)` : null,\n `${pkg.coverage.toFixed(1)}% coverage`,\n pkg.anyCount > 0 ? `${pkg.anyCount} any` : null,\n pkg.tsIgnoreCount > 0 ? `${pkg.tsIgnoreCount} @ts-ignore` : null,\n ]\n .filter(Boolean)\n .join(', ');\n\n errorItems.push(` ${details}`);\n }\n\n if (result.packages.length > 10) {\n errorItems.push(`... and ${result.packages.length - 10} more packages`);\n }\n\n sections.push({\n header: `Packages with Type Errors (${result.packagesWithErrors})`,\n items: errorItems,\n });\n }\n\n // Coverage distribution\n const excellent = result.packages.filter((p) => p.coverage >= 90).length;\n const good = result.packages.filter((p) => p.coverage >= 70 && p.coverage < 90).length;\n const poor = result.packages.filter((p) => p.coverage < 70).length;\n\n if (result.packages.length > 0) {\n const coverageItems = [\n `✅ Excellent (≥90%): ${excellent} package(s)`,\n `⚠️ Good (70-90%): ${good} package(s)`,\n poor > 0 ? `❌ Poor (<70%): ${poor} package(s)` : null,\n ].filter(Boolean) as string[];\n\n sections.push({ header: 'Type Coverage Distribution', items: coverageItems });\n }\n\n // Summary\n const summaryItems = [\n `Total packages: ${result.totalPackages}`,\n `Duration: ${(result.duration / 1000).toFixed(1)}s`,\n result.cached ? '💾 Cached (use --refresh to recheck)' : '🔄 Fresh analysis',\n ];\n\n sections.push({ header: 'Summary', items: summaryItems });\n\n const title =\n result.totalErrors === 0\n ? '✅ All Type Checks Passed'\n : `❌ ${result.packagesWithErrors} Package(s) with Type Errors`;\n\n ui?.success?.('Type analysis completed', {\n title,\n sections,\n });\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
2
+
3
+ /**
4
+ * quality:cycles - Detect circular dependencies
5
+ *
6
+ * Uses DFS to find all circular dependency chains in the monorepo.
7
+ */
8
+ type CyclesFlags = {
9
+ json?: boolean;
10
+ argv?: string[];
11
+ };
12
+ type CyclesInput = CyclesFlags & {
13
+ argv?: string[];
14
+ };
15
+ declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CyclesInput, unknown>;
16
+
17
+ export { _default as default };
@@ -0,0 +1,85 @@
1
+ import { defineCommand } from '@kb-labs/sdk';
2
+ import { buildDependencyGraph, findCircularDependencies } from '@kb-labs/quality-core/graph';
3
+
4
+ // src/cli/commands/cycles.ts
5
+ var cycles_default = defineCommand({
6
+ id: "quality:cycles",
7
+ description: "Detect circular dependencies in monorepo",
8
+ handler: {
9
+ async execute(ctx, input) {
10
+ const { ui } = ctx;
11
+ const flags = input.flags ?? input;
12
+ const graph = buildDependencyGraph(ctx.cwd);
13
+ const cycles = findCircularDependencies(graph);
14
+ outputCycles(cycles, flags, ui);
15
+ return {
16
+ exitCode: cycles.length > 0 ? 1 : 0,
17
+ cycles
18
+ };
19
+ }
20
+ }
21
+ });
22
+ function outputCycles(cycles, flags, ui) {
23
+ if (flags.json) {
24
+ ui?.json?.({ cycles, count: cycles.length });
25
+ return;
26
+ }
27
+ if (cycles.length === 0) {
28
+ ui?.success?.("No circular dependencies found", {
29
+ title: "\u2705 Circular Dependencies Check",
30
+ sections: [
31
+ {
32
+ header: "Result",
33
+ items: ["\u2705 No circular dependencies detected"]
34
+ }
35
+ ]
36
+ });
37
+ return;
38
+ }
39
+ const sections = [];
40
+ sections.push({
41
+ header: "Summary",
42
+ items: [
43
+ `\u26A0\uFE0F Found ${cycles.length} circular dependency chain(s)`,
44
+ "These must be broken to enable clean builds"
45
+ ]
46
+ });
47
+ for (let i = 0; i < cycles.length; i++) {
48
+ const cycle = cycles[i];
49
+ if (!cycle) {
50
+ continue;
51
+ }
52
+ const cycleItems = [];
53
+ for (let j = 0; j < cycle.length; j++) {
54
+ const pkg = cycle[j];
55
+ if (j === cycle.length - 1) {
56
+ cycleItems.push(` ${j + 1}. ${pkg}`);
57
+ const firstPkg = cycle[0];
58
+ cycleItems.push(` ${j + 2}. ${firstPkg ?? "?"} \u2B05 circular!`);
59
+ } else {
60
+ cycleItems.push(` ${j + 1}. ${pkg}`);
61
+ }
62
+ }
63
+ sections.push({
64
+ header: `Cycle ${i + 1} (${cycle.length} packages)`,
65
+ items: cycleItems
66
+ });
67
+ }
68
+ sections.push({
69
+ header: "Recommendations",
70
+ items: [
71
+ "1. Extract shared code into a new package",
72
+ "2. Move one dependency to devDependencies",
73
+ "3. Refactor to remove bidirectional dependencies",
74
+ "4. Use dependency injection patterns"
75
+ ]
76
+ });
77
+ ui?.error?.("Circular dependencies detected", {
78
+ title: `\u26A0\uFE0F Circular Dependencies Detected (${cycles.length})`,
79
+ sections
80
+ });
81
+ }
82
+
83
+ export { cycles_default as default };
84
+ //# sourceMappingURL=cycles.js.map
85
+ //# sourceMappingURL=cycles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/commands/cycles.ts"],"names":[],"mappings":"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","file":"cycles.js","sourcesContent":["/**\n * quality:cycles - Detect circular dependencies\n *\n * Uses DFS to find all circular dependency chains in the monorepo.\n */\n\nimport { defineCommand, type PluginContextV3 } from '@kb-labs/sdk';\nimport {\n buildDependencyGraph,\n findCircularDependencies,\n} from '@kb-labs/quality-core/graph';\n\ntype CyclesFlags = {\n json?: boolean;\n argv?: string[];\n};\n\ntype CyclesInput = CyclesFlags & { argv?: string[] };\n\ntype CyclesCommandResult = {\n exitCode: number;\n cycles?: string[][];\n};\n\nexport default defineCommand({\n id: 'quality:cycles',\n description: 'Detect circular dependencies in monorepo',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CyclesInput): Promise<CyclesCommandResult> {\n const { ui } = ctx;\n\n // V3: Flags come in input.flags object (not auto-merged)\n const flags = (input as any).flags ?? input;\n\n // Build dependency graph\n const graph = buildDependencyGraph(ctx.cwd);\n\n // Find circular dependencies\n const cycles = findCircularDependencies(graph);\n\n // Output results\n outputCycles(cycles, flags, ui);\n\n return {\n exitCode: cycles.length > 0 ? 1 : 0,\n cycles,\n };\n },\n },\n});\n\n/**\n * Output circular dependencies\n */\nfunction outputCycles(cycles: string[][], flags: any, ui: any) {\n if (flags.json) {\n ui?.json?.({ cycles, count: cycles.length });\n return;\n }\n\n if (cycles.length === 0) {\n ui?.success?.('No circular dependencies found', {\n title: '✅ Circular Dependencies Check',\n sections: [\n {\n header: 'Result',\n items: ['✅ No circular dependencies detected'],\n },\n ],\n });\n return;\n }\n\n // Build sections\n const sections: Array<{ header: string; items: string[] }> = [];\n\n // Summary\n sections.push({\n header: 'Summary',\n items: [\n `⚠️ Found ${cycles.length} circular dependency chain(s)`,\n 'These must be broken to enable clean builds',\n ],\n });\n\n // Show each cycle\n for (let i = 0; i < cycles.length; i++) {\n const cycle = cycles[i];\n if (!cycle) {continue;}\n const cycleItems: string[] = [];\n\n for (let j = 0; j < cycle.length; j++) {\n const pkg = cycle[j];\n if (j === cycle.length - 1) {\n cycleItems.push(` ${j + 1}. ${pkg}`);\n const firstPkg = cycle[0];\n cycleItems.push(` ${j + 2}. ${firstPkg ?? '?'} ⬅ circular!`);\n } else {\n cycleItems.push(` ${j + 1}. ${pkg}`);\n }\n }\n\n sections.push({\n header: `Cycle ${i + 1} (${cycle.length} packages)`,\n items: cycleItems,\n });\n }\n\n // Recommendations\n sections.push({\n header: 'Recommendations',\n items: [\n '1. Extract shared code into a new package',\n '2. Move one dependency to devDependencies',\n '3. Refactor to remove bidirectional dependencies',\n '4. Use dependency injection patterns',\n ],\n });\n\n ui?.error?.('Circular dependencies detected', {\n title: `⚠️ Circular Dependencies Detected (${cycles.length})`,\n sections,\n });\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
2
+ import { DeadCodeResult, DeadCodeRemovalResult, DeadCodeBackupManifest } from '@kb-labs/quality-contracts';
3
+ import { DeadCodeFlags } from './flags.js';
4
+
5
+ type DeadCodeInput = DeadCodeFlags & {
6
+ argv?: string[];
7
+ };
8
+ declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, DeadCodeInput, DeadCodeResult | DeadCodeRemovalResult | DeadCodeBackupManifest[]>;
9
+
10
+ export { _default as default };
@@ -0,0 +1,217 @@
1
+ import { defineCommand } from '@kb-labs/sdk';
2
+ import { CACHE_KEYS } from '@kb-labs/quality-contracts';
3
+ import { listBackups, restoreFromBackup, scanDeadFiles, removeDeadFiles } from '@kb-labs/quality-core/dead-code';
4
+
5
+ // src/cli/commands/dead-code.ts
6
+ var dead_code_default = defineCommand({
7
+ id: "quality:dead-code",
8
+ description: "Detect and optionally remove dead (unreachable) source files",
9
+ handler: {
10
+ async execute(ctx, input) {
11
+ const { ui, platform } = ctx;
12
+ const flags = input.flags ?? input;
13
+ if (flags["list-backups"]) {
14
+ const backups = listBackups(ctx.cwd);
15
+ if (flags.json) {
16
+ ui?.json?.(backups);
17
+ } else {
18
+ outputBackupList(backups, ui);
19
+ }
20
+ return { exitCode: 0, result: backups };
21
+ }
22
+ if (flags.restore) {
23
+ try {
24
+ const result2 = await restoreFromBackup(ctx.cwd, flags.restore);
25
+ if (flags.json) {
26
+ ui?.json?.(result2);
27
+ } else {
28
+ ui?.success?.(`Restored ${result2.restoredFiles} file(s) from backup ${flags.restore}`, {});
29
+ if (result2.restoredExports > 0) {
30
+ ui?.info?.(`Note: ${result2.restoredExports} package.json export(s) were cleaned. You may need to rebuild.`);
31
+ }
32
+ }
33
+ return { exitCode: 0 };
34
+ } catch (err) {
35
+ if (flags.json) {
36
+ ui?.json?.({ error: err.message });
37
+ } else {
38
+ ui?.error?.(err.message);
39
+ }
40
+ return { exitCode: 1 };
41
+ }
42
+ }
43
+ const cacheKey = `${CACHE_KEYS.DEAD_CODE}:${flags.package || "all"}`;
44
+ if (!flags.refresh && !flags["auto-remove"]) {
45
+ const cached = await platform.cache.get(cacheKey);
46
+ if (cached) {
47
+ outputDeadCodeReport({ ...cached, cached: true }, flags, ui);
48
+ return { exitCode: cached.summary.totalDead > 0 ? 1 : 0, result: cached };
49
+ }
50
+ }
51
+ const result = await scanDeadFiles(ctx.cwd, {
52
+ packageFilter: flags.package,
53
+ verbose: flags.verbose
54
+ });
55
+ await platform.cache.set(cacheKey, result, 5 * 60 * 1e3);
56
+ await platform.analytics.track("quality:dead-code", {
57
+ totalPackages: result.summary.totalPackages,
58
+ totalFiles: result.summary.totalFiles,
59
+ totalDead: result.summary.totalDead,
60
+ totalDeadBytes: result.summary.totalDeadBytes,
61
+ duration: result.duration,
62
+ autoRemove: !!flags["auto-remove"],
63
+ dryRun: !!flags["dry-run"]
64
+ });
65
+ if (flags["auto-remove"]) {
66
+ if (result.summary.totalDead === 0) {
67
+ if (flags.json) {
68
+ ui?.json?.({ message: "No dead files found", ...result });
69
+ } else {
70
+ ui?.success?.("No dead files found. Nothing to remove.", {});
71
+ }
72
+ return { exitCode: 0, result };
73
+ }
74
+ const removalResult = await removeDeadFiles(ctx.cwd, result, {
75
+ dryRun: flags["dry-run"]
76
+ });
77
+ if (flags.json) {
78
+ ui?.json?.(removalResult);
79
+ } else {
80
+ outputRemovalReport(removalResult, flags, ui);
81
+ }
82
+ return { exitCode: 0, result: removalResult };
83
+ }
84
+ outputDeadCodeReport({ ...result, cached: false }, flags, ui);
85
+ return {
86
+ exitCode: result.summary.totalDead > 0 ? 1 : 0,
87
+ result
88
+ };
89
+ }
90
+ }
91
+ });
92
+ function outputDeadCodeReport(result, flags, ui) {
93
+ if (flags.json) {
94
+ ui?.json?.(result);
95
+ return;
96
+ }
97
+ const sections = [];
98
+ const packagesWithDead = result.packages.filter((p) => p.deadFiles.length > 0);
99
+ if (packagesWithDead.length > 0) {
100
+ const items = [];
101
+ for (const pkg of packagesWithDead) {
102
+ items.push(`${pkg.packageName} (${pkg.deadFiles.length} dead file${pkg.deadFiles.length === 1 ? "" : "s"})`);
103
+ for (const dead of pkg.deadFiles.slice(0, 5)) {
104
+ const sizeKb = (dead.sizeBytes / 1024).toFixed(1);
105
+ items.push(` ${dead.relativePath} (${sizeKb} KB)`);
106
+ }
107
+ if (pkg.deadFiles.length > 5) {
108
+ items.push(` ... and ${pkg.deadFiles.length - 5} more`);
109
+ }
110
+ }
111
+ sections.push({ header: "Dead Files", items });
112
+ }
113
+ if (flags.verbose) {
114
+ for (const pkg of result.packages) {
115
+ if (pkg.entryPoints.length > 0) {
116
+ const items = pkg.entryPoints.slice(0, 10).map((ep) => ` ${ep}`);
117
+ if (pkg.entryPoints.length > 10) {
118
+ items.push(` ... and ${pkg.entryPoints.length - 10} more`);
119
+ }
120
+ sections.push({ header: `Entry Points: ${pkg.packageName}`, items });
121
+ }
122
+ }
123
+ }
124
+ const allWarnings = result.packages.flatMap((p) => p.warnings);
125
+ if (allWarnings.length > 0) {
126
+ sections.push({
127
+ header: "Warnings",
128
+ items: allWarnings.slice(0, 10)
129
+ });
130
+ }
131
+ const totalDeadKb = (result.summary.totalDeadBytes / 1024).toFixed(1);
132
+ const summaryItems = [
133
+ `Packages scanned: ${result.summary.totalPackages}`,
134
+ `Total source files: ${result.summary.totalFiles}`,
135
+ `Alive files: ${result.summary.totalAlive}`,
136
+ `Dead files: ${result.summary.totalDead}`,
137
+ result.summary.totalDead > 0 ? `Dead code size: ${totalDeadKb} KB` : null,
138
+ result.summary.emptyDirectories.length > 0 ? `Empty directories: ${result.summary.emptyDirectories.length}` : null,
139
+ `Duration: ${(result.duration / 1e3).toFixed(1)}s`,
140
+ result.cached ? "(cached \u2014 use --refresh to rescan)" : null
141
+ ].filter(Boolean);
142
+ sections.push({ header: "Summary", items: summaryItems });
143
+ if (result.summary.totalDead > 0) {
144
+ sections.push({
145
+ header: "Next Steps",
146
+ items: [
147
+ "Run with --auto-remove --dry-run to preview removal",
148
+ "Run with --auto-remove to remove and create backup",
149
+ "Run with --verbose to see entry points"
150
+ ]
151
+ });
152
+ }
153
+ const title = result.summary.totalDead === 0 ? "No dead files found" : `${result.summary.totalDead} dead file(s) found`;
154
+ ui?.success?.("Dead code analysis completed", { title, sections });
155
+ }
156
+ function outputRemovalReport(result, flags, ui) {
157
+ const isDryRun = flags["dry-run"];
158
+ const prefix = isDryRun ? "[DRY RUN] Would" : "Successfully";
159
+ const sections = [];
160
+ const items = [
161
+ `${prefix} remove ${result.filesRemoved} file(s)`,
162
+ `${prefix} free ${(result.bytesRemoved / 1024).toFixed(1)} KB`
163
+ ];
164
+ if (result.emptyDirsRemoved > 0) {
165
+ items.push(`${prefix} remove ${result.emptyDirsRemoved} empty director${result.emptyDirsRemoved === 1 ? "y" : "ies"}`);
166
+ }
167
+ if (result.exportsCleanedUp > 0) {
168
+ items.push(`${prefix} clean ${result.exportsCleanedUp} package.json export(s)`);
169
+ }
170
+ sections.push({ header: isDryRun ? "Preview" : "Removal Complete", items });
171
+ if (!isDryRun) {
172
+ sections.push({
173
+ header: "Backup",
174
+ items: [
175
+ `Backup ID: ${result.backupId}`,
176
+ `To restore: pnpm kb quality:dead-code --restore ${result.backupId}`
177
+ ]
178
+ });
179
+ }
180
+ const fileItems = [];
181
+ for (const file of result.manifest.removedFiles.slice(0, 10)) {
182
+ const sizeKb = (file.sizeBytes / 1024).toFixed(1);
183
+ fileItems.push(`${file.backupPath} (${sizeKb} KB)`);
184
+ }
185
+ if (result.manifest.removedFiles.length > 10) {
186
+ fileItems.push(`... and ${result.manifest.removedFiles.length - 10} more`);
187
+ }
188
+ sections.push({ header: "Files", items: fileItems });
189
+ const title = isDryRun ? `[DRY RUN] Would remove ${result.filesRemoved} file(s)` : `Removed ${result.filesRemoved} file(s)`;
190
+ ui?.success?.(title, { title, sections });
191
+ }
192
+ function outputBackupList(backups, ui) {
193
+ if (backups.length === 0) {
194
+ ui?.info?.("No backups found.");
195
+ return;
196
+ }
197
+ const sections = [];
198
+ for (const backup of backups) {
199
+ const date = new Date(backup.createdAt).toLocaleString();
200
+ const items = [
201
+ `Date: ${date}`,
202
+ `Git: ${backup.gitBranch} @ ${backup.gitSha.slice(0, 8)}`,
203
+ `Files: ${backup.totalFilesRemoved}`,
204
+ `Size: ${(backup.totalBytesRemoved / 1024).toFixed(1)} KB`,
205
+ `Restore: pnpm kb quality:dead-code --restore ${backup.id}`
206
+ ];
207
+ sections.push({ header: `Backup: ${backup.id}`, items });
208
+ }
209
+ ui?.success?.(`${backups.length} backup(s) found`, {
210
+ title: "Dead Code Backups",
211
+ sections
212
+ });
213
+ }
214
+
215
+ export { dead_code_default as default };
216
+ //# sourceMappingURL=dead-code.js.map
217
+ //# sourceMappingURL=dead-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/cli/commands/dead-code.ts"],"names":["result"],"mappings":"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","file":"dead-code.js","sourcesContent":["/**\n * quality:dead-code - Detect and optionally remove dead source files\n *\n * Uses reachability analysis: builds import graph, walks from entry points,\n * files not reachable are dead. Zero false positives by design.\n */\n\nimport { defineCommand, type PluginContextV3 } from '@kb-labs/sdk';\nimport type { DeadCodeResult, DeadCodeRemovalResult, DeadCodeBackupManifest } from '@kb-labs/quality-contracts';\nimport { CACHE_KEYS } from '@kb-labs/quality-contracts';\nimport {\n scanDeadFiles,\n removeDeadFiles,\n restoreFromBackup,\n listBackups,\n} from '@kb-labs/quality-core/dead-code';\nimport type { DeadCodeFlags } from './flags.js';\n\ntype DeadCodeInput = DeadCodeFlags & { argv?: string[] };\n\ntype DeadCodeCommandResult = {\n exitCode: number;\n result?: DeadCodeResult | DeadCodeRemovalResult | DeadCodeBackupManifest[];\n meta?: Record<string, unknown>;\n};\n\nexport default defineCommand({\n id: 'quality:dead-code',\n description: 'Detect and optionally remove dead (unreachable) source files',\n\n handler: {\n async execute(\n ctx: PluginContextV3,\n input: DeadCodeInput,\n ): Promise<DeadCodeCommandResult> {\n const { ui, platform } = ctx;\n const flags = (input as any).flags ?? input;\n\n // --- Sub-operations ---\n\n // List backups\n if (flags['list-backups']) {\n const backups = listBackups(ctx.cwd);\n if (flags.json) {\n ui?.json?.(backups);\n } else {\n outputBackupList(backups, ui);\n }\n return { exitCode: 0, result: backups };\n }\n\n // Restore from backup\n if (flags.restore) {\n try {\n const result = await restoreFromBackup(ctx.cwd, flags.restore);\n if (flags.json) {\n ui?.json?.(result);\n } else {\n ui?.success?.(`Restored ${result.restoredFiles} file(s) from backup ${flags.restore}`, {});\n if (result.restoredExports > 0) {\n ui?.info?.(`Note: ${result.restoredExports} package.json export(s) were cleaned. You may need to rebuild.`);\n }\n }\n return { exitCode: 0 };\n } catch (err: any) {\n if (flags.json) {\n ui?.json?.({ error: err.message });\n } else {\n ui?.error?.(err.message);\n }\n return { exitCode: 1 };\n }\n }\n\n // --- Main scan flow ---\n\n // Check cache\n const cacheKey = `${CACHE_KEYS.DEAD_CODE}:${flags.package || 'all'}`;\n if (!flags.refresh && !flags['auto-remove']) {\n const cached = await platform.cache.get<DeadCodeResult>(cacheKey);\n if (cached) {\n outputDeadCodeReport({ ...cached, cached: true }, flags, ui);\n return { exitCode: cached.summary.totalDead > 0 ? 1 : 0, result: cached };\n }\n }\n\n // Run scan\n const result = await scanDeadFiles(ctx.cwd, {\n packageFilter: flags.package,\n verbose: flags.verbose,\n });\n\n // Cache results for 5 minutes\n await platform.cache.set(cacheKey, result, 5 * 60 * 1000);\n\n // Track analytics\n await platform.analytics.track('quality:dead-code', {\n totalPackages: result.summary.totalPackages,\n totalFiles: result.summary.totalFiles,\n totalDead: result.summary.totalDead,\n totalDeadBytes: result.summary.totalDeadBytes,\n duration: result.duration,\n autoRemove: !!flags['auto-remove'],\n dryRun: !!flags['dry-run'],\n });\n\n // Auto-remove flow\n if (flags['auto-remove']) {\n if (result.summary.totalDead === 0) {\n if (flags.json) {\n ui?.json?.({ message: 'No dead files found', ...result });\n } else {\n ui?.success?.('No dead files found. Nothing to remove.', {});\n }\n return { exitCode: 0, result };\n }\n\n const removalResult = await removeDeadFiles(ctx.cwd, result, {\n dryRun: flags['dry-run'],\n });\n\n if (flags.json) {\n ui?.json?.(removalResult);\n } else {\n outputRemovalReport(removalResult, flags, ui);\n }\n\n return { exitCode: 0, result: removalResult };\n }\n\n // Output scan results\n outputDeadCodeReport({ ...result, cached: false }, flags, ui);\n\n return {\n exitCode: result.summary.totalDead > 0 ? 1 : 0,\n result,\n };\n },\n },\n});\n\n// --- Output helpers ---\n\nfunction outputDeadCodeReport(\n result: DeadCodeResult & { cached?: boolean },\n flags: any,\n ui: any,\n): void {\n if (flags.json) {\n ui?.json?.(result);\n return;\n }\n\n const sections: Array<{ header: string; items: string[] }> = [];\n\n // Per-package results (only packages with dead files)\n const packagesWithDead = result.packages.filter(p => p.deadFiles.length > 0);\n\n if (packagesWithDead.length > 0) {\n const items: string[] = [];\n for (const pkg of packagesWithDead) {\n items.push(`${pkg.packageName} (${pkg.deadFiles.length} dead file${pkg.deadFiles.length === 1 ? '' : 's'})`);\n for (const dead of pkg.deadFiles.slice(0, 5)) {\n const sizeKb = (dead.sizeBytes / 1024).toFixed(1);\n items.push(` ${dead.relativePath} (${sizeKb} KB)`);\n }\n if (pkg.deadFiles.length > 5) {\n items.push(` ... and ${pkg.deadFiles.length - 5} more`);\n }\n }\n sections.push({ header: 'Dead Files', items });\n }\n\n // Verbose: show entry points\n if (flags.verbose) {\n for (const pkg of result.packages) {\n if (pkg.entryPoints.length > 0) {\n const items = pkg.entryPoints.slice(0, 10).map(ep => ` ${ep}`);\n if (pkg.entryPoints.length > 10) {\n items.push(` ... and ${pkg.entryPoints.length - 10} more`);\n }\n sections.push({ header: `Entry Points: ${pkg.packageName}`, items });\n }\n }\n }\n\n // Warnings\n const allWarnings = result.packages.flatMap(p => p.warnings);\n if (allWarnings.length > 0) {\n sections.push({\n header: 'Warnings',\n items: allWarnings.slice(0, 10),\n });\n }\n\n // Summary\n const totalDeadKb = (result.summary.totalDeadBytes / 1024).toFixed(1);\n const summaryItems = [\n `Packages scanned: ${result.summary.totalPackages}`,\n `Total source files: ${result.summary.totalFiles}`,\n `Alive files: ${result.summary.totalAlive}`,\n `Dead files: ${result.summary.totalDead}`,\n result.summary.totalDead > 0 ? `Dead code size: ${totalDeadKb} KB` : null,\n result.summary.emptyDirectories.length > 0\n ? `Empty directories: ${result.summary.emptyDirectories.length}`\n : null,\n `Duration: ${(result.duration / 1000).toFixed(1)}s`,\n result.cached ? '(cached — use --refresh to rescan)' : null,\n ].filter(Boolean) as string[];\n sections.push({ header: 'Summary', items: summaryItems });\n\n // Recommendations\n if (result.summary.totalDead > 0) {\n sections.push({\n header: 'Next Steps',\n items: [\n 'Run with --auto-remove --dry-run to preview removal',\n 'Run with --auto-remove to remove and create backup',\n 'Run with --verbose to see entry points',\n ],\n });\n }\n\n const title =\n result.summary.totalDead === 0\n ? 'No dead files found'\n : `${result.summary.totalDead} dead file(s) found`;\n\n ui?.success?.('Dead code analysis completed', { title, sections });\n}\n\nfunction outputRemovalReport(\n result: DeadCodeRemovalResult,\n flags: any,\n ui: any,\n): void {\n const isDryRun = flags['dry-run'];\n const prefix = isDryRun ? '[DRY RUN] Would' : 'Successfully';\n\n const sections: Array<{ header: string; items: string[] }> = [];\n\n const items = [\n `${prefix} remove ${result.filesRemoved} file(s)`,\n `${prefix} free ${(result.bytesRemoved / 1024).toFixed(1)} KB`,\n ];\n\n if (result.emptyDirsRemoved > 0) {\n items.push(`${prefix} remove ${result.emptyDirsRemoved} empty director${result.emptyDirsRemoved === 1 ? 'y' : 'ies'}`);\n }\n if (result.exportsCleanedUp > 0) {\n items.push(`${prefix} clean ${result.exportsCleanedUp} package.json export(s)`);\n }\n\n sections.push({ header: isDryRun ? 'Preview' : 'Removal Complete', items });\n\n if (!isDryRun) {\n sections.push({\n header: 'Backup',\n items: [\n `Backup ID: ${result.backupId}`,\n `To restore: pnpm kb quality:dead-code --restore ${result.backupId}`,\n ],\n });\n }\n\n // Show removed files\n const fileItems: string[] = [];\n for (const file of result.manifest.removedFiles.slice(0, 10)) {\n const sizeKb = (file.sizeBytes / 1024).toFixed(1);\n fileItems.push(`${file.backupPath} (${sizeKb} KB)`);\n }\n if (result.manifest.removedFiles.length > 10) {\n fileItems.push(`... and ${result.manifest.removedFiles.length - 10} more`);\n }\n sections.push({ header: 'Files', items: fileItems });\n\n const title = isDryRun\n ? `[DRY RUN] Would remove ${result.filesRemoved} file(s)`\n : `Removed ${result.filesRemoved} file(s)`;\n\n ui?.success?.(title, { title, sections });\n}\n\nfunction outputBackupList(\n backups: DeadCodeBackupManifest[],\n ui: any,\n): void {\n if (backups.length === 0) {\n ui?.info?.('No backups found.');\n return;\n }\n\n const sections: Array<{ header: string; items: string[] }> = [];\n\n for (const backup of backups) {\n const date = new Date(backup.createdAt).toLocaleString();\n const items = [\n `Date: ${date}`,\n `Git: ${backup.gitBranch} @ ${backup.gitSha.slice(0, 8)}`,\n `Files: ${backup.totalFilesRemoved}`,\n `Size: ${(backup.totalBytesRemoved / 1024).toFixed(1)} KB`,\n `Restore: pnpm kb quality:dead-code --restore ${backup.id}`,\n ];\n sections.push({ header: `Backup: ${backup.id}`, items });\n }\n\n ui?.success?.(`${backups.length} backup(s) found`, {\n title: 'Dead Code Backups',\n sections,\n });\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
2
+ import { FixDepsFlags } from './flags.js';
3
+
4
+ type FixDepsInput = FixDepsFlags & {
5
+ argv?: string[];
6
+ };
7
+ interface FixResult {
8
+ packagesScanned: number;
9
+ removedDeps: Array<{
10
+ package: string;
11
+ dep: string;
12
+ }>;
13
+ addedDeps: Array<{
14
+ package: string;
15
+ dep: string;
16
+ version: string;
17
+ }>;
18
+ alignedDeps: Array<{
19
+ dep: string;
20
+ from: string;
21
+ to: string;
22
+ packages: string[];
23
+ }>;
24
+ dryRun: boolean;
25
+ }
26
+ declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, FixDepsInput, FixResult>;
27
+
28
+ export { _default as default };