@kb-labs/quality-entry 2.93.0 → 2.96.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/dist/cli/commands/build-order.d.ts +5 -12
- package/dist/cli/commands/build-order.js +45 -97
- package/dist/cli/commands/build-order.js.map +1 -1
- package/dist/cli/commands/check-layers.d.ts +9 -0
- package/dist/cli/commands/check-layers.js +48 -0
- package/dist/cli/commands/check-layers.js.map +1 -0
- package/dist/cli/commands/context.d.ts +9 -0
- package/dist/cli/commands/context.js +94 -0
- package/dist/cli/commands/context.js.map +1 -0
- package/dist/cli/commands/coupling.d.ts +9 -0
- package/dist/cli/commands/coupling.js +35 -0
- package/dist/cli/commands/coupling.js.map +1 -0
- package/dist/cli/commands/dead-code.d.ts +4 -5
- package/dist/cli/commands/dead-code.js +41 -197
- package/dist/cli/commands/dead-code.js.map +1 -1
- package/dist/cli/commands/fix-deps.d.ts +2 -5
- package/dist/cli/commands/fix-deps.js +74 -83
- package/dist/cli/commands/fix-deps.js.map +1 -1
- package/dist/cli/commands/flags.d.ts +115 -1
- package/dist/cli/commands/flags.js +46 -1
- package/dist/cli/commands/flags.js.map +1 -1
- package/dist/cli/commands/gate.d.ts +9 -0
- package/dist/cli/commands/gate.js +59 -0
- package/dist/cli/commands/gate.js.map +1 -0
- package/dist/cli/commands/health.d.ts +4 -5
- package/dist/cli/commands/health.js +42 -191
- package/dist/cli/commands/health.js.map +1 -1
- package/dist/cli/commands/history.d.ts +9 -0
- package/dist/cli/commands/history.js +55 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/index.d.ts +6 -2
- package/dist/cli/commands/index.js +411 -828
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/snapshot.d.ts +9 -0
- package/dist/cli/commands/snapshot.js +91 -0
- package/dist/cli/commands/snapshot.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +4 -5
- package/dist/cli/commands/stats.js +17 -265
- package/dist/cli/commands/stats.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +198 -306
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +95 -82
- package/dist/manifest.js +198 -306
- package/dist/manifest.js.map +1 -1
- package/dist/rest/handlers/build-order-handler.d.ts +5 -13
- package/dist/rest/handlers/build-order-handler.js +14 -17
- package/dist/rest/handlers/build-order-handler.js.map +1 -1
- package/dist/rest/handlers/builds-handler.js +10 -6
- package/dist/rest/handlers/builds-handler.js.map +1 -1
- package/dist/rest/handlers/coupling-handler.d.ts +8 -0
- package/dist/rest/handlers/coupling-handler.js +28 -0
- package/dist/rest/handlers/coupling-handler.js.map +1 -0
- package/dist/rest/handlers/cycles-handler.js +16 -12
- package/dist/rest/handlers/cycles-handler.js.map +1 -1
- package/dist/rest/handlers/dependencies-handler.js +12 -8
- package/dist/rest/handlers/dependencies-handler.js.map +1 -1
- package/dist/rest/handlers/graph-handler.js +77 -73
- package/dist/rest/handlers/graph-handler.js.map +1 -1
- package/dist/rest/handlers/health-handler.d.ts +4 -11
- package/dist/rest/handlers/health-handler.js +35 -9
- package/dist/rest/handlers/health-handler.js.map +1 -1
- package/dist/rest/handlers/history-handler.d.ts +7 -0
- package/dist/rest/handlers/history-handler.js +21 -0
- package/dist/rest/handlers/history-handler.js.map +1 -0
- package/dist/rest/handlers/knip-handler.d.ts +8 -0
- package/dist/rest/handlers/knip-handler.js +31 -0
- package/dist/rest/handlers/knip-handler.js.map +1 -0
- package/dist/rest/handlers/layers-handler.d.ts +8 -0
- package/dist/rest/handlers/layers-handler.js +28 -0
- package/dist/rest/handlers/layers-handler.js.map +1 -0
- package/dist/rest/handlers/snapshot-handler.d.ts +7 -0
- package/dist/rest/handlers/snapshot-handler.js +67 -0
- package/dist/rest/handlers/snapshot-handler.js.map +1 -0
- package/dist/rest/handlers/stale-handler.js +13 -9
- package/dist/rest/handlers/stale-handler.js.map +1 -1
- package/dist/rest/handlers/stats-handler.d.ts +4 -9
- package/dist/rest/handlers/stats-handler.js +22 -18
- package/dist/rest/handlers/stats-handler.js.map +1 -1
- package/dist/rest/handlers/tests-handler.js +12 -8
- package/dist/rest/handlers/tests-handler.js.map +1 -1
- package/dist/rest/handlers/types-handler.js +10 -6
- package/dist/rest/handlers/types-handler.js.map +1 -1
- package/dist/widgets/117.js +24 -1
- package/dist/widgets/117.js.map +1 -1
- package/dist/widgets/370.js +1 -1
- package/dist/widgets/370.js.map +1 -1
- package/dist/widgets/62.js +97 -0
- package/dist/widgets/62.js.map +1 -0
- package/dist/widgets/__federation_expose_QualityOverview.js +1 -1
- package/dist/widgets/__federation_expose_QualityOverview.js.map +1 -1
- package/dist/widgets/mf-manifest.json +27 -27
- package/dist/widgets/mf-stats.json +27 -27
- package/dist/widgets/remoteEntry.js +2 -2
- package/dist/widgets/remoteEntry.js.map +1 -1
- package/package.json +9 -8
- package/dist/widgets/418.js +0 -39
- package/dist/widgets/418.js.map +0 -1
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
-
import {
|
|
2
|
+
import { CLIInput } from '@kb-labs/sdk';
|
|
3
|
+
import { BuildOrderFlags } from './flags.js';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
script?: boolean;
|
|
8
|
-
json?: boolean;
|
|
9
|
-
argv?: string[];
|
|
10
|
-
};
|
|
11
|
-
type BuildOrderInput = BuildOrderFlags & {
|
|
12
|
-
argv?: string[];
|
|
13
|
-
};
|
|
14
|
-
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, BuildOrderInput, TopologicalSortResult>;
|
|
5
|
+
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CLIInput<BuildOrderFlags>, {
|
|
6
|
+
exitCode: number;
|
|
7
|
+
}>;
|
|
15
8
|
|
|
16
9
|
export { _default as default };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { defineCommand } from '@kb-labs/sdk';
|
|
2
|
-
import {
|
|
1
|
+
import { defineCommand, useConfig } from '@kb-labs/sdk';
|
|
2
|
+
import { analyzeBuildOrder } from '@kb-labs/quality-core';
|
|
3
|
+
import { defaultQualityConfig } from '@kb-labs/quality-contracts';
|
|
3
4
|
|
|
4
5
|
// src/cli/commands/build-order.ts
|
|
5
6
|
var build_order_default = defineCommand({
|
|
@@ -7,106 +8,53 @@ var build_order_default = defineCommand({
|
|
|
7
8
|
description: "Calculate build order using topological sort",
|
|
8
9
|
handler: {
|
|
9
10
|
async execute(ctx, input) {
|
|
10
|
-
const {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
if (result.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
function outputBuildOrder(result, flags, ui) {
|
|
32
|
-
if (flags.json) {
|
|
33
|
-
ui?.json?.(result);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (flags.script) {
|
|
37
|
-
ui?.write?.("#!/bin/bash");
|
|
38
|
-
ui?.write?.("# Generated build script");
|
|
39
|
-
ui?.write?.("set -e");
|
|
40
|
-
ui?.write?.("");
|
|
41
|
-
for (let i = 0; i < result.layers.length; i++) {
|
|
42
|
-
const layer = result.layers[i];
|
|
43
|
-
if (!layer) {
|
|
44
|
-
continue;
|
|
11
|
+
const { flags } = input;
|
|
12
|
+
const config = await useConfig();
|
|
13
|
+
const cfg = config ?? defaultQualityConfig;
|
|
14
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
15
|
+
const result = analyzeBuildOrder({
|
|
16
|
+
rootDir: cwd,
|
|
17
|
+
layerMap: cfg.layers,
|
|
18
|
+
filterPackage: flags.package
|
|
19
|
+
});
|
|
20
|
+
if (result.hasCircular) {
|
|
21
|
+
if (flags.json) {
|
|
22
|
+
ctx.ui?.json?.({ ...result, ok: false });
|
|
23
|
+
} else {
|
|
24
|
+
ctx.ui?.error?.(`Circular dependencies detected \u2014 build order cannot be fully determined`, {
|
|
25
|
+
sections: result.circular.map((cycle, i) => ({
|
|
26
|
+
header: `Cycle ${i + 1}`,
|
|
27
|
+
items: cycle
|
|
28
|
+
}))
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return { exitCode: 1 };
|
|
45
32
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
if (flags.json) {
|
|
34
|
+
ctx.ui?.json?.(result);
|
|
35
|
+
return { exitCode: 0 };
|
|
49
36
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
for (let i = 0; i < result.layers.length; i++) {
|
|
58
|
-
const layer = result.layers[i];
|
|
59
|
-
if (!layer) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
layerItems.push(`Layer ${i + 1}: ${layer.length} packages (can build in parallel)`);
|
|
63
|
-
for (const pkg of layer) {
|
|
64
|
-
layerItems.push(` \u2022 ${pkg}`);
|
|
37
|
+
if (flags.script) {
|
|
38
|
+
for (let i = 0; i < result.layers.length; i++) {
|
|
39
|
+
const layer = result.layers[i];
|
|
40
|
+
if (!layer) continue;
|
|
41
|
+
ctx.ui?.success?.(`# Layer ${i + 1}`, { sections: [{ header: "", items: layer.map((p) => `pnpm --filter "${p}" run build`) }] });
|
|
42
|
+
}
|
|
43
|
+
return { exitCode: 0 };
|
|
65
44
|
}
|
|
66
|
-
|
|
45
|
+
const sections = flags.layers ? result.layers.map((layer, i) => ({
|
|
46
|
+
header: `Layer ${i + 1} \u2014 ${layer.length} package(s) (parallel)`,
|
|
47
|
+
items: layer
|
|
48
|
+
})) : [{ header: "Build order", items: result.sorted.map((p, i) => `${i + 1}. ${p}`) }];
|
|
49
|
+
sections.push({
|
|
50
|
+
header: "Summary",
|
|
51
|
+
items: [`${result.packageCount} packages in ${result.layerCount} layers`]
|
|
52
|
+
});
|
|
53
|
+
ctx.ui?.success?.(flags.package ? `Build order for ${flags.package}` : "Monorepo build order", { sections });
|
|
54
|
+
return { exitCode: 0 };
|
|
67
55
|
}
|
|
68
|
-
sections.push({ header: "Build Layers", items: layerItems });
|
|
69
|
-
} else {
|
|
70
|
-
const orderItems = result.sorted.map((pkg, idx) => `${idx + 1}. ${pkg}`);
|
|
71
|
-
sections.push({ header: "Build Order", items: orderItems });
|
|
72
56
|
}
|
|
73
|
-
|
|
74
|
-
header: "Summary",
|
|
75
|
-
items: [
|
|
76
|
-
`Total packages: ${result.sorted.length}`,
|
|
77
|
-
`Build layers: ${result.layers.length}`,
|
|
78
|
-
`Circular dependencies: ${result.circular.length}`
|
|
79
|
-
]
|
|
80
|
-
});
|
|
81
|
-
const title = flags.package ? `\u{1F4E6} Build Order for ${flags.package}` : "\u{1F4E6} Monorepo Build Order";
|
|
82
|
-
ui?.success?.("Build order calculated successfully", {
|
|
83
|
-
title,
|
|
84
|
-
sections
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
function outputCircularDependencies(cycles, ui) {
|
|
88
|
-
const sections = [];
|
|
89
|
-
for (let i = 0; i < cycles.length; i++) {
|
|
90
|
-
const cycle = cycles[i];
|
|
91
|
-
if (!cycle) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
sections.push({
|
|
95
|
-
header: `Cycle ${i + 1}`,
|
|
96
|
-
items: cycle.map((pkg, idx) => {
|
|
97
|
-
if (idx === cycle.length - 1) {
|
|
98
|
-
const firstPkg = cycle[0];
|
|
99
|
-
return ` ${pkg} \u2192 ${firstPkg ?? "?"} (circular!)`;
|
|
100
|
-
}
|
|
101
|
-
return ` ${pkg} \u2192`;
|
|
102
|
-
})
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
ui?.error?.("Circular dependencies detected", {
|
|
106
|
-
title: "\u26A0\uFE0F Circular Dependencies",
|
|
107
|
-
sections
|
|
108
|
-
});
|
|
109
|
-
}
|
|
57
|
+
});
|
|
110
58
|
|
|
111
59
|
export { build_order_default as default };
|
|
112
60
|
//# sourceMappingURL=build-order.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/build-order.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/build-order.ts"],"names":[],"mappings":"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","file":"build-order.js","sourcesContent":["/**\n * quality:build-order - Calculate build order and dependency layers\n *\n * Uses topological sort to determine correct build order.\n * Shows build layers where each layer can build in parallel.\n */\n\nimport { defineCommand, useConfig, type CLIInput, type PluginContextV3 } from '@kb-labs/sdk';\nimport { analyzeBuildOrder } from '@kb-labs/quality-core';\nimport { defaultQualityConfig, type QualityPluginConfig } from '@kb-labs/quality-contracts';\nimport type { BuildOrderFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<BuildOrderFlags>, { exitCode: number }>({\n id: 'quality:build-order',\n description: 'Calculate build order using topological sort',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<BuildOrderFlags>): Promise<{ exitCode: number }> {\n const { flags } = input;\n const config = await useConfig<QualityPluginConfig>();\n const cfg = config ?? defaultQualityConfig;\n const cwd = ctx.cwd ?? process.cwd();\n\n const result = analyzeBuildOrder({\n rootDir: cwd,\n layerMap: cfg.layers,\n filterPackage: flags.package,\n });\n\n if (result.hasCircular) {\n if (flags.json) {\n ctx.ui?.json?.({ ...result, ok: false });\n } else {\n ctx.ui?.error?.(`Circular dependencies detected — build order cannot be fully determined`, {\n sections: result.circular.map((cycle, i) => ({\n header: `Cycle ${i + 1}`,\n items: cycle,\n })),\n });\n }\n return { exitCode: 1 };\n }\n\n if (flags.json) {\n ctx.ui?.json?.(result);\n return { exitCode: 0 };\n }\n\n if (flags.script) {\n for (let i = 0; i < result.layers.length; i++) {\n const layer = result.layers[i];\n if (!layer) continue;\n ctx.ui?.success?.(`# Layer ${i + 1}`, { sections: [{ header: '', items: layer.map(p => `pnpm --filter \"${p}\" run build`) }] });\n }\n return { exitCode: 0 };\n }\n\n const sections = flags.layers\n ? result.layers.map((layer, i) => ({\n header: `Layer ${i + 1} — ${layer.length} package(s) (parallel)`,\n items: layer,\n }))\n : [{ header: 'Build order', items: result.sorted.map((p, i) => `${i + 1}. ${p}`) }];\n\n sections.push({\n header: 'Summary',\n items: [`${result.packageCount} packages in ${result.layerCount} layers`],\n });\n\n ctx.ui?.success?.(flags.package ? `Build order for ${flags.package}` : 'Monorepo build order', { sections });\n return { exitCode: 0 };\n },\n },\n});\n\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
+
import { CLIInput } from '@kb-labs/sdk';
|
|
3
|
+
import { CheckLayersFlags } from './flags.js';
|
|
4
|
+
|
|
5
|
+
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CLIInput<CheckLayersFlags>, {
|
|
6
|
+
exitCode: number;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
export { _default as default };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { defineCommand, useConfig } from '@kb-labs/sdk';
|
|
2
|
+
import { analyzeLayering } from '@kb-labs/quality-core';
|
|
3
|
+
|
|
4
|
+
// src/cli/commands/check-layers.ts
|
|
5
|
+
var check_layers_default = defineCommand({
|
|
6
|
+
id: "quality:check-layers",
|
|
7
|
+
description: "Detect layering violations (lower layer importing higher layer)",
|
|
8
|
+
handler: {
|
|
9
|
+
async execute(ctx, input) {
|
|
10
|
+
const { flags } = input;
|
|
11
|
+
const config = await useConfig();
|
|
12
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
13
|
+
const report = await analyzeLayering({
|
|
14
|
+
rootDir: cwd,
|
|
15
|
+
layerMap: config?.layers
|
|
16
|
+
});
|
|
17
|
+
const filtered = flags.package ? {
|
|
18
|
+
...report,
|
|
19
|
+
violations: report.violations.filter((v) => v.fromPackage.includes(flags.package)),
|
|
20
|
+
totalViolations: report.violations.filter((v) => v.fromPackage.includes(flags.package)).length
|
|
21
|
+
} : report;
|
|
22
|
+
if (flags.json) {
|
|
23
|
+
ctx.ui?.json?.(filtered);
|
|
24
|
+
return { exitCode: filtered.totalViolations > 0 ? 1 : 0 };
|
|
25
|
+
}
|
|
26
|
+
if (filtered.totalViolations === 0) {
|
|
27
|
+
ctx.ui?.success?.("No layering violations found");
|
|
28
|
+
return { exitCode: 0 };
|
|
29
|
+
}
|
|
30
|
+
const byPackage = /* @__PURE__ */ new Map();
|
|
31
|
+
for (const v of filtered.violations) {
|
|
32
|
+
const list = byPackage.get(v.fromPackage) ?? [];
|
|
33
|
+
list.push(v);
|
|
34
|
+
byPackage.set(v.fromPackage, list);
|
|
35
|
+
}
|
|
36
|
+
const sections = [...byPackage.entries()].map(([pkg, vs]) => ({
|
|
37
|
+
header: `${pkg} (Layer ${vs[0]?.fromLayer})`,
|
|
38
|
+
items: vs.map((v) => `\u2192 ${v.toPackage} (Layer ${v.toLayer}) ${v.importSpecifier}`)
|
|
39
|
+
}));
|
|
40
|
+
ctx.ui?.error?.(`${filtered.totalViolations} layering violation(s) in ${filtered.affectedPackages.length} package(s)`, { sections });
|
|
41
|
+
return { exitCode: 1 };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export { check_layers_default as default };
|
|
47
|
+
//# sourceMappingURL=check-layers.js.map
|
|
48
|
+
//# sourceMappingURL=check-layers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/check-layers.ts"],"names":[],"mappings":";;;;AAUA,IAAO,uBAAQ,aAAA,CAAyE;AAAA,EACtF,EAAA,EAAI,sBAAA;AAAA,EACJ,WAAA,EAAa,iEAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAAkE;AACpG,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA+B;AACpD,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,MAAA,GAAS,MAAM,eAAA,CAAgB;AAAA,QACnC,OAAA,EAAS,GAAA;AAAA,QACT,UAAU,MAAA,EAAQ;AAAA,OACnB,CAAA;AAED,MAAA,MAAM,QAAA,GAAW,MAAM,OAAA,GACnB;AAAA,QACE,GAAG,MAAA;AAAA,QACH,UAAA,EAAY,MAAA,CAAO,UAAA,CAAW,MAAA,CAAO,CAAC,CAAA,KAAyB,CAAA,CAAE,WAAA,CAAY,QAAA,CAAS,KAAA,CAAM,OAAQ,CAAC,CAAA;AAAA,QACrG,eAAA,EAAiB,MAAA,CAAO,UAAA,CAAW,MAAA,CAAO,CAAC,CAAA,KAAyB,CAAA,CAAE,WAAA,CAAY,QAAA,CAAS,KAAA,CAAM,OAAQ,CAAC,CAAA,CAAE;AAAA,OAC9G,GACA,MAAA;AAEJ,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,OAAO,QAAQ,CAAA;AACvB,QAAA,OAAO,EAAE,QAAA,EAAU,QAAA,CAAS,eAAA,GAAkB,CAAA,GAAI,IAAI,CAAA,EAAE;AAAA,MAC1D;AAEA,MAAA,IAAI,QAAA,CAAS,oBAAoB,CAAA,EAAG;AAClC,QAAA,GAAA,CAAI,EAAA,EAAI,UAAU,8BAA8B,CAAA;AAChD,QAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,MACvB;AAEA,MAAA,MAAM,SAAA,uBAAgB,GAAA,EAAwC;AAC9D,MAAA,KAAA,MAAW,CAAA,IAAK,SAAS,UAAA,EAAY;AACnC,QAAA,MAAM,OAAO,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,WAAW,KAAK,EAAC;AAC9C,QAAA,IAAA,CAAK,KAAK,CAAC,CAAA;AACX,QAAA,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,WAAA,EAAa,IAAI,CAAA;AAAA,MACnC;AAEA,MAAA,MAAM,QAAA,GAAW,CAAC,GAAG,SAAA,CAAU,OAAA,EAAS,CAAA,CAAE,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,EAAE,CAAA,MAAO;AAAA,QAC5D,QAAQ,CAAA,EAAG,GAAG,WAAW,EAAA,CAAG,CAAC,GAAG,SAAS,CAAA,CAAA,CAAA;AAAA,QACzC,KAAA,EAAO,EAAA,CAAG,GAAA,CAAI,CAAC,MAAyB,CAAA,OAAA,EAAK,CAAA,CAAE,SAAS,CAAA,QAAA,EAAW,CAAA,CAAE,OAAO,CAAA,GAAA,EAAM,CAAA,CAAE,eAAe,CAAA,CAAE;AAAA,OACvG,CAAE,CAAA;AAEF,MAAA,GAAA,CAAI,EAAA,EAAI,KAAA,GAAQ,CAAA,EAAG,QAAA,CAAS,eAAe,CAAA,0BAAA,EAA6B,QAAA,CAAS,gBAAA,CAAiB,MAAM,CAAA,WAAA,CAAA,EAAe,EAAE,QAAA,EAAU,CAAA;AACnI,MAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,IACvB;AAAA;AAEJ,CAAC","file":"check-layers.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n} from '@kb-labs/sdk';\nimport { analyzeLayering } from '@kb-labs/quality-core';\nimport { type QualityPluginConfig, type LayeringViolation } from '@kb-labs/quality-contracts';\nimport type { CheckLayersFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<CheckLayersFlags>, { exitCode: number }>({\n id: 'quality:check-layers',\n description: 'Detect layering violations (lower layer importing higher layer)',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<CheckLayersFlags>): Promise<{ exitCode: number }> {\n const { flags } = input;\n const config = await useConfig<QualityPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const report = await analyzeLayering({\n rootDir: cwd,\n layerMap: config?.layers,\n });\n\n const filtered = flags.package\n ? {\n ...report,\n violations: report.violations.filter((v: LayeringViolation) => v.fromPackage.includes(flags.package!)),\n totalViolations: report.violations.filter((v: LayeringViolation) => v.fromPackage.includes(flags.package!)).length,\n }\n : report;\n\n if (flags.json) {\n ctx.ui?.json?.(filtered);\n return { exitCode: filtered.totalViolations > 0 ? 1 : 0 };\n }\n\n if (filtered.totalViolations === 0) {\n ctx.ui?.success?.('No layering violations found');\n return { exitCode: 0 };\n }\n\n const byPackage = new Map<string, typeof filtered.violations>();\n for (const v of filtered.violations) {\n const list = byPackage.get(v.fromPackage) ?? [];\n list.push(v);\n byPackage.set(v.fromPackage, list);\n }\n\n const sections = [...byPackage.entries()].map(([pkg, vs]) => ({\n header: `${pkg} (Layer ${vs[0]?.fromLayer})`,\n items: vs.map((v: LayeringViolation) => `→ ${v.toPackage} (Layer ${v.toLayer}) ${v.importSpecifier}`),\n }));\n\n ctx.ui?.error?.(`${filtered.totalViolations} layering violation(s) in ${filtered.affectedPackages.length} package(s)`, { sections });\n return { exitCode: 1 };\n },\n },\n});\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
+
import { CLIInput } from '@kb-labs/sdk';
|
|
3
|
+
import { ContextFlags } from './flags.js';
|
|
4
|
+
|
|
5
|
+
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CLIInput<ContextFlags>, {
|
|
6
|
+
exitCode: number;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
export { _default as default };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { defineCommand, useConfig } from '@kb-labs/sdk';
|
|
2
|
+
import { scanWorkspace, buildPackageMap } from '@kb-labs/quality-core';
|
|
3
|
+
import { defaultQualityConfig } from '@kb-labs/quality-contracts';
|
|
4
|
+
|
|
5
|
+
// src/cli/commands/context.ts
|
|
6
|
+
var LAYER_NAMES = {
|
|
7
|
+
0: "core",
|
|
8
|
+
1: "sdk / shared",
|
|
9
|
+
2: "cli / adapters",
|
|
10
|
+
3: "plugins",
|
|
11
|
+
4: "studio"
|
|
12
|
+
};
|
|
13
|
+
var context_default = defineCommand({
|
|
14
|
+
id: "quality:context",
|
|
15
|
+
description: "Package context for agents: layer, dependents, dependencies, coupling",
|
|
16
|
+
handler: {
|
|
17
|
+
async execute(ctx, input) {
|
|
18
|
+
const { flags } = input;
|
|
19
|
+
const config = await useConfig();
|
|
20
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
21
|
+
const layerMap = config?.layers ?? defaultQualityConfig.layers;
|
|
22
|
+
const packages = scanWorkspace(cwd, layerMap);
|
|
23
|
+
const packageMap = buildPackageMap(packages);
|
|
24
|
+
const allNames = new Set(packageMap.keys());
|
|
25
|
+
const pkgName = flags.package;
|
|
26
|
+
if (!pkgName) {
|
|
27
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
28
|
+
for (const p of packages) {
|
|
29
|
+
const list = byLayer.get(p.layer) ?? [];
|
|
30
|
+
list.push(p.name);
|
|
31
|
+
byLayer.set(p.layer, list);
|
|
32
|
+
}
|
|
33
|
+
if (flags.json) {
|
|
34
|
+
const summary = Object.fromEntries(
|
|
35
|
+
[...byLayer.entries()].map(([l, names]) => [LAYER_NAMES[l] ?? `layer-${l}`, names])
|
|
36
|
+
);
|
|
37
|
+
ctx.ui?.json?.({ totalPackages: packages.length, byLayer: summary });
|
|
38
|
+
return { exitCode: 0 };
|
|
39
|
+
}
|
|
40
|
+
const sections = [...byLayer.entries()].sort(([a], [b]) => a - b).map(([layer, names]) => ({
|
|
41
|
+
header: `Layer ${layer}: ${LAYER_NAMES[layer] ?? "unknown"} (${names.length})`,
|
|
42
|
+
items: names
|
|
43
|
+
}));
|
|
44
|
+
ctx.ui?.success?.(`Workspace: ${packages.length} packages`, { sections });
|
|
45
|
+
return { exitCode: 0 };
|
|
46
|
+
}
|
|
47
|
+
const pkg = packageMap.get(pkgName);
|
|
48
|
+
if (!pkg) {
|
|
49
|
+
ctx.ui?.error?.(`Package not found: ${pkgName}`);
|
|
50
|
+
return { exitCode: 1 };
|
|
51
|
+
}
|
|
52
|
+
const wsDeps = pkg.deps.filter((d) => allNames.has(d));
|
|
53
|
+
const wsDevDeps = pkg.devDeps.filter((d) => allNames.has(d));
|
|
54
|
+
const dependents = packages.filter((p) => p.name !== pkgName && (p.deps.includes(pkgName) || p.devDeps.includes(pkgName))).map((p) => p.name);
|
|
55
|
+
const ce = wsDeps.length + wsDevDeps.length;
|
|
56
|
+
const ca = dependents.length;
|
|
57
|
+
const instability = ca + ce === 0 ? 0 : Math.round(ce / (ca + ce) * 100) / 100;
|
|
58
|
+
const context = {
|
|
59
|
+
name: pkgName,
|
|
60
|
+
layer: pkg.layer,
|
|
61
|
+
layerName: LAYER_NAMES[pkg.layer] ?? "unknown",
|
|
62
|
+
dir: pkg.dir.replace(cwd + "/", ""),
|
|
63
|
+
coupling: { afferent: ca, efferent: ce, instability },
|
|
64
|
+
dependents,
|
|
65
|
+
dependencies: wsDeps,
|
|
66
|
+
devDependencies: wsDevDeps
|
|
67
|
+
};
|
|
68
|
+
if (flags.json) {
|
|
69
|
+
ctx.ui?.json?.(context);
|
|
70
|
+
return { exitCode: 0 };
|
|
71
|
+
}
|
|
72
|
+
ctx.ui?.success?.(`Context: ${pkgName}`, {
|
|
73
|
+
sections: [
|
|
74
|
+
{
|
|
75
|
+
header: "Package info",
|
|
76
|
+
items: [
|
|
77
|
+
`Layer: ${pkg.layer} (${LAYER_NAMES[pkg.layer] ?? "unknown"})`,
|
|
78
|
+
`Dir: ${context.dir}`,
|
|
79
|
+
`Instability: ${instability} Ca: ${ca} Ce: ${ce}`
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
...dependents.length > 0 ? [{ header: `Dependents (${ca}) \u2014 packages that import this`, items: dependents }] : [{ header: "Dependents", items: ["none \u2014 this package is a leaf"] }],
|
|
83
|
+
...wsDeps.length > 0 ? [{ header: `Dependencies (${wsDeps.length}) \u2014 workspace packages this imports`, items: wsDeps }] : [],
|
|
84
|
+
...wsDevDeps.length > 0 ? [{ header: `Dev dependencies (${wsDevDeps.length})`, items: wsDevDeps }] : []
|
|
85
|
+
]
|
|
86
|
+
});
|
|
87
|
+
return { exitCode: 0 };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export { context_default as default };
|
|
93
|
+
//# sourceMappingURL=context.js.map
|
|
94
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/context.ts"],"names":[],"mappings":"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","file":"context.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n} from '@kb-labs/sdk';\nimport { scanWorkspace, buildPackageMap } from '@kb-labs/quality-core';\nimport { defaultQualityConfig, type QualityPluginConfig } from '@kb-labs/quality-contracts';\nimport type { ContextFlags } from './flags.js';\n\nconst LAYER_NAMES: Record<number, string> = {\n 0: 'core',\n 1: 'sdk / shared',\n 2: 'cli / adapters',\n 3: 'plugins',\n 4: 'studio',\n};\n\nexport default defineCommand<unknown, CLIInput<ContextFlags>, { exitCode: number }>({\n id: 'quality:context',\n description: 'Package context for agents: layer, dependents, dependencies, coupling',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<ContextFlags>): Promise<{ exitCode: number }> {\n const { flags } = input;\n const config = await useConfig<QualityPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n const layerMap = config?.layers ?? defaultQualityConfig.layers;\n\n const packages = scanWorkspace(cwd, layerMap);\n const packageMap = buildPackageMap(packages);\n const allNames = new Set(packageMap.keys());\n\n const pkgName = flags.package;\n\n if (!pkgName) {\n // No package specified — show workspace summary\n const byLayer = new Map<number, string[]>();\n for (const p of packages) {\n const list = byLayer.get(p.layer) ?? [];\n list.push(p.name);\n byLayer.set(p.layer, list);\n }\n\n if (flags.json) {\n const summary = Object.fromEntries(\n [...byLayer.entries()].map(([l, names]) => [LAYER_NAMES[l] ?? `layer-${l}`, names])\n );\n ctx.ui?.json?.({ totalPackages: packages.length, byLayer: summary });\n return { exitCode: 0 };\n }\n\n const sections = [...byLayer.entries()]\n .sort(([a], [b]) => a - b)\n .map(([layer, names]) => ({\n header: `Layer ${layer}: ${LAYER_NAMES[layer] ?? 'unknown'} (${names.length})`,\n items: names,\n }));\n\n ctx.ui?.success?.(`Workspace: ${packages.length} packages`, { sections });\n return { exitCode: 0 };\n }\n\n const pkg = packageMap.get(pkgName);\n if (!pkg) {\n ctx.ui?.error?.(`Package not found: ${pkgName}`);\n return { exitCode: 1 };\n }\n\n // Workspace deps (efferent)\n const wsDeps = pkg.deps.filter(d => allNames.has(d));\n const wsDevDeps = pkg.devDeps.filter(d => allNames.has(d));\n\n // Who depends on this package (afferent) — scan all packages\n const dependents = packages\n .filter(p => p.name !== pkgName && (p.deps.includes(pkgName) || p.devDeps.includes(pkgName)))\n .map(p => p.name);\n\n const ce = wsDeps.length + wsDevDeps.length;\n const ca = dependents.length;\n const instability = ca + ce === 0 ? 0 : Math.round((ce / (ca + ce)) * 100) / 100;\n\n const context = {\n name: pkgName,\n layer: pkg.layer,\n layerName: LAYER_NAMES[pkg.layer] ?? 'unknown',\n dir: pkg.dir.replace(cwd + '/', ''),\n coupling: { afferent: ca, efferent: ce, instability },\n dependents,\n dependencies: wsDeps,\n devDependencies: wsDevDeps,\n };\n\n if (flags.json) {\n ctx.ui?.json?.(context);\n return { exitCode: 0 };\n }\n\n ctx.ui?.success?.(`Context: ${pkgName}`, {\n sections: [\n {\n header: 'Package info',\n items: [\n `Layer: ${pkg.layer} (${LAYER_NAMES[pkg.layer] ?? 'unknown'})`,\n `Dir: ${context.dir}`,\n `Instability: ${instability} Ca: ${ca} Ce: ${ce}`,\n ],\n },\n ...(dependents.length > 0\n ? [{ header: `Dependents (${ca}) — packages that import this`, items: dependents }]\n : [{ header: 'Dependents', items: ['none — this package is a leaf'] }]),\n ...(wsDeps.length > 0\n ? [{ header: `Dependencies (${wsDeps.length}) — workspace packages this imports`, items: wsDeps }]\n : []),\n ...(wsDevDeps.length > 0\n ? [{ header: `Dev dependencies (${wsDevDeps.length})`, items: wsDevDeps }]\n : []),\n ],\n });\n\n return { exitCode: 0 };\n },\n },\n});\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
+
import { CLIInput } from '@kb-labs/sdk';
|
|
3
|
+
import { CouplingFlags } from './flags.js';
|
|
4
|
+
|
|
5
|
+
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CLIInput<CouplingFlags>, {
|
|
6
|
+
exitCode: number;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
export { _default as default };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineCommand, useConfig } from '@kb-labs/sdk';
|
|
2
|
+
import { analyzeCoupling } from '@kb-labs/quality-core';
|
|
3
|
+
|
|
4
|
+
// src/cli/commands/coupling.ts
|
|
5
|
+
var coupling_default = defineCommand({
|
|
6
|
+
id: "quality:coupling",
|
|
7
|
+
description: "Show coupling metrics per package (afferent/efferent/instability)",
|
|
8
|
+
handler: {
|
|
9
|
+
async execute(ctx, input) {
|
|
10
|
+
const { flags } = input;
|
|
11
|
+
const config = await useConfig();
|
|
12
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
13
|
+
const report = analyzeCoupling({ rootDir: cwd, layerMap: config?.layers, topN: flags.top });
|
|
14
|
+
if (flags.json) {
|
|
15
|
+
ctx.ui?.json?.(report);
|
|
16
|
+
return { exitCode: 0 };
|
|
17
|
+
}
|
|
18
|
+
const sorted = flags.sort === "coupled" ? [...report.packages].sort((a, b) => b.afferent + b.efferent - (a.afferent + a.efferent)) : [...report.packages].sort((a, b) => b.instability - a.instability);
|
|
19
|
+
const top = sorted.slice(0, flags.top ?? 10);
|
|
20
|
+
const threshold = config?.thresholds?.instability ?? 0.85;
|
|
21
|
+
const items = top.map((p) => {
|
|
22
|
+
const warn = p.instability > threshold ? " \u26A0 unstable" : "";
|
|
23
|
+
return `${p.name} Ca: ${p.afferent} Ce: ${p.efferent} I: ${p.instability.toFixed(2)}${warn}`;
|
|
24
|
+
});
|
|
25
|
+
ctx.ui?.success?.(`Coupling \u2014 avg instability: ${report.avgInstability.toFixed(2)}`, {
|
|
26
|
+
sections: [{ header: `Top ${top.length} by instability`, items }]
|
|
27
|
+
});
|
|
28
|
+
return { exitCode: 0 };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export { coupling_default as default };
|
|
34
|
+
//# sourceMappingURL=coupling.js.map
|
|
35
|
+
//# sourceMappingURL=coupling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/coupling.ts"],"names":[],"mappings":";;;;AAUA,IAAO,mBAAQ,aAAA,CAAsE;AAAA,EACnF,EAAA,EAAI,kBAAA;AAAA,EACJ,WAAA,EAAa,mEAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAA+D;AACjG,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAA+B;AACpD,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAEnC,MAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,EAAE,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,KAAA,CAAM,GAAA,EAAK,CAAA;AAE1F,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,GAAA,CAAI,EAAA,EAAI,OAAO,MAAM,CAAA;AACrB,QAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,MACvB;AAEA,MAAA,MAAM,MAAA,GACJ,KAAA,CAAM,IAAA,KAAS,SAAA,GACX,CAAC,GAAG,MAAA,CAAO,QAAQ,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA,CAAE,QAAA,IAAY,CAAA,CAAE,QAAA,GAAW,CAAA,CAAE,QAAA,CAAS,CAAA,GACvF,CAAC,GAAG,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,WAAA,GAAc,EAAE,WAAW,CAAA;AAEvE,MAAA,MAAM,MAAM,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,KAAA,CAAM,OAAO,EAAE,CAAA;AAC3C,MAAA,MAAM,SAAA,GAAY,MAAA,EAAQ,UAAA,EAAY,WAAA,IAAe,IAAA;AAErD,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,CAAI,CAAA,CAAA,KAAK;AACzB,QAAA,MAAM,IAAA,GAAO,CAAA,CAAE,WAAA,GAAc,SAAA,GAAY,kBAAA,GAAgB,EAAA;AACzD,QAAA,OAAO,GAAG,CAAA,CAAE,IAAI,CAAA,MAAA,EAAS,CAAA,CAAE,QAAQ,CAAA,MAAA,EAAS,CAAA,CAAE,QAAQ,CAAA,KAAA,EAAQ,EAAE,WAAA,CAAY,OAAA,CAAQ,CAAC,CAAC,GAAG,IAAI,CAAA,CAAA;AAAA,MAC/F,CAAC,CAAA;AAED,MAAA,GAAA,CAAI,EAAA,EAAI,UAAU,CAAA,iCAAA,EAA+B,MAAA,CAAO,eAAe,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,EAAI;AAAA,QACnF,QAAA,EAAU,CAAC,EAAE,MAAA,EAAQ,OAAO,GAAA,CAAI,MAAM,CAAA,eAAA,CAAA,EAAmB,KAAA,EAAO;AAAA,OACjE,CAAA;AAED,MAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,IACvB;AAAA;AAEJ,CAAC","file":"coupling.js","sourcesContent":["import {\n defineCommand,\n useConfig,\n type CLIInput,\n type PluginContextV3,\n} from '@kb-labs/sdk';\nimport { analyzeCoupling } from '@kb-labs/quality-core';\nimport { type QualityPluginConfig } from '@kb-labs/quality-contracts';\nimport type { CouplingFlags } from './flags.js';\n\nexport default defineCommand<unknown, CLIInput<CouplingFlags>, { exitCode: number }>({\n id: 'quality:coupling',\n description: 'Show coupling metrics per package (afferent/efferent/instability)',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<CouplingFlags>): Promise<{ exitCode: number }> {\n const { flags } = input;\n const config = await useConfig<QualityPluginConfig>();\n const cwd = ctx.cwd ?? process.cwd();\n\n const report = analyzeCoupling({ rootDir: cwd, layerMap: config?.layers, topN: flags.top });\n\n if (flags.json) {\n ctx.ui?.json?.(report);\n return { exitCode: 0 };\n }\n\n const sorted =\n flags.sort === 'coupled'\n ? [...report.packages].sort((a, b) => b.afferent + b.efferent - (a.afferent + a.efferent))\n : [...report.packages].sort((a, b) => b.instability - a.instability);\n\n const top = sorted.slice(0, flags.top ?? 10);\n const threshold = config?.thresholds?.instability ?? 0.85;\n\n const items = top.map(p => {\n const warn = p.instability > threshold ? ' ⚠ unstable' : '';\n return `${p.name} Ca: ${p.afferent} Ce: ${p.efferent} I: ${p.instability.toFixed(2)}${warn}`;\n });\n\n ctx.ui?.success?.(`Coupling — avg instability: ${report.avgInstability.toFixed(2)}`, {\n sections: [{ header: `Top ${top.length} by instability`, items }],\n });\n\n return { exitCode: 0 };\n },\n },\n});\n"]}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
-
import {
|
|
2
|
+
import { CLIInput } from '@kb-labs/sdk';
|
|
3
3
|
import { DeadCodeFlags } from './flags.js';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, DeadCodeInput, DeadCodeResult | DeadCodeRemovalResult | DeadCodeBackupManifest[]>;
|
|
5
|
+
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CLIInput<DeadCodeFlags>, {
|
|
6
|
+
exitCode: number;
|
|
7
|
+
}>;
|
|
9
8
|
|
|
10
9
|
export { _default as default };
|