dep-brain 0.5.2 → 0.6.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/README.md +24 -7
- package/depbrain.output.schema.json +19 -7
- package/dist/checks/duplicate.js +13 -1
- package/dist/checks/outdated.js +13 -1
- package/dist/checks/risk.js +20 -1
- package/dist/checks/unused.js +22 -2
- package/dist/core/analyzer.d.ts +13 -1
- package/dist/core/analyzer.js +29 -5
- package/dist/core/types.d.ts +4 -0
- package/dist/reporters/console.js +11 -7
- package/dist/reporters/markdown.js +11 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/dep-brain)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
`dep-brain` is a CLI and library for
|
|
7
|
+
`dep-brain` is a CLI and library for explainable dependency intelligence in JavaScript and TypeScript projects.
|
|
8
8
|
|
|
9
9
|
## Vision
|
|
10
10
|
|
|
11
|
-
`
|
|
11
|
+
`dep-brain` aims to become a dependency decision engine:
|
|
12
|
+
|
|
13
|
+
- Explain why a dependency matters
|
|
14
|
+
- Evaluate how safe, risky, or necessary it is
|
|
15
|
+
- Recommend what to do next
|
|
16
|
+
- Enforce decisions in CI workflows
|
|
12
17
|
|
|
13
18
|
## What It Does
|
|
14
19
|
|
|
@@ -19,6 +24,12 @@
|
|
|
19
24
|
- Generate a simple project health score
|
|
20
25
|
- Output reports in human-readable or JSON format
|
|
21
26
|
|
|
27
|
+
The long-term goal is not just to list problems, but to answer:
|
|
28
|
+
|
|
29
|
+
- Why is this dependency here?
|
|
30
|
+
- Can I remove it safely?
|
|
31
|
+
- What should I fix first?
|
|
32
|
+
|
|
22
33
|
## Current MVP Features
|
|
23
34
|
|
|
24
35
|
- Duplicate dependency detection with lockfile instance tracking
|
|
@@ -225,14 +236,20 @@ src/
|
|
|
225
236
|
`-- config.ts
|
|
226
237
|
```
|
|
227
238
|
|
|
228
|
-
##
|
|
239
|
+
## Product Direction
|
|
240
|
+
|
|
241
|
+
`dep-brain` is currently in the `v0.5.x` foundation stage. The next roadmap is:
|
|
242
|
+
|
|
243
|
+
- `v0.6`: explainability and confidence scoring
|
|
244
|
+
- `v0.7`: safe removal guidance and actionable recommendations
|
|
245
|
+
- `v0.8`: supply-chain trust and risk intelligence
|
|
246
|
+
- `v0.9`: deeper monorepo and ownership intelligence
|
|
247
|
+
- `v1.0`: stable CI, ecosystem exports, and production readiness
|
|
229
248
|
|
|
230
|
-
|
|
231
|
-
- Improve monorepo and workspace support
|
|
232
|
-
- Strengthen risk scoring and suggestions
|
|
233
|
-
- Add CI and GitHub Action support in later releases
|
|
249
|
+
The project should optimize for trust, clarity, and actionability over flashy UI, generic graphs, or simply adding more checks.
|
|
234
250
|
|
|
235
251
|
## Repository Notes
|
|
236
252
|
|
|
237
253
|
- Project brief: [docs/project-brief.md](./docs/project-brief.md)
|
|
254
|
+
- Product roadmap: [docs/product-roadmap.md](./docs/product-roadmap.md)
|
|
238
255
|
- Implementation history: [docs/implementation-log.md](./docs/implementation-log.md)
|
|
@@ -44,11 +44,14 @@
|
|
|
44
44
|
"type": "array",
|
|
45
45
|
"items": {
|
|
46
46
|
"type": "object",
|
|
47
|
-
"required": ["name", "versions", "instances"],
|
|
47
|
+
"required": ["name", "versions", "instances", "confidence", "reasonCodes", "explanation"],
|
|
48
48
|
"additionalProperties": false,
|
|
49
49
|
"properties": {
|
|
50
50
|
"name": { "type": "string" },
|
|
51
51
|
"versions": { "type": "array", "items": { "type": "string" } },
|
|
52
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
53
|
+
"reasonCodes": { "type": "array", "items": { "type": "string" } },
|
|
54
|
+
"explanation": { "type": "array", "items": { "type": "string" } },
|
|
52
55
|
"instances": {
|
|
53
56
|
"type": "array",
|
|
54
57
|
"items": {
|
|
@@ -68,12 +71,15 @@
|
|
|
68
71
|
"type": "array",
|
|
69
72
|
"items": {
|
|
70
73
|
"type": "object",
|
|
71
|
-
"required": ["name", "section"],
|
|
74
|
+
"required": ["name", "section", "confidence", "reasonCodes", "explanation"],
|
|
72
75
|
"additionalProperties": false,
|
|
73
76
|
"properties": {
|
|
74
77
|
"name": { "type": "string" },
|
|
75
78
|
"section": { "type": "string", "enum": ["dependencies", "devDependencies"] },
|
|
76
|
-
"package": { "type": "string" }
|
|
79
|
+
"package": { "type": "string" },
|
|
80
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
81
|
+
"reasonCodes": { "type": "array", "items": { "type": "string" } },
|
|
82
|
+
"explanation": { "type": "array", "items": { "type": "string" } }
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
85
|
},
|
|
@@ -81,14 +87,17 @@
|
|
|
81
87
|
"type": "array",
|
|
82
88
|
"items": {
|
|
83
89
|
"type": "object",
|
|
84
|
-
"required": ["name", "current", "latest", "updateType"],
|
|
90
|
+
"required": ["name", "current", "latest", "updateType", "confidence", "reasonCodes", "explanation"],
|
|
85
91
|
"additionalProperties": false,
|
|
86
92
|
"properties": {
|
|
87
93
|
"name": { "type": "string" },
|
|
88
94
|
"current": { "type": "string" },
|
|
89
95
|
"latest": { "type": "string" },
|
|
90
96
|
"updateType": { "type": "string" },
|
|
91
|
-
"package": { "type": "string" }
|
|
97
|
+
"package": { "type": "string" },
|
|
98
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
99
|
+
"reasonCodes": { "type": "array", "items": { "type": "string" } },
|
|
100
|
+
"explanation": { "type": "array", "items": { "type": "string" } }
|
|
92
101
|
}
|
|
93
102
|
}
|
|
94
103
|
},
|
|
@@ -96,12 +105,15 @@
|
|
|
96
105
|
"type": "array",
|
|
97
106
|
"items": {
|
|
98
107
|
"type": "object",
|
|
99
|
-
"required": ["name", "reasons"],
|
|
108
|
+
"required": ["name", "reasons", "confidence", "reasonCodes", "explanation"],
|
|
100
109
|
"additionalProperties": false,
|
|
101
110
|
"properties": {
|
|
102
111
|
"name": { "type": "string" },
|
|
103
112
|
"reasons": { "type": "array", "items": { "type": "string" } },
|
|
104
|
-
"package": { "type": "string" }
|
|
113
|
+
"package": { "type": "string" },
|
|
114
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
115
|
+
"reasonCodes": { "type": "array", "items": { "type": "string" } },
|
|
116
|
+
"explanation": { "type": "array", "items": { "type": "string" } }
|
|
105
117
|
}
|
|
106
118
|
}
|
|
107
119
|
},
|
package/dist/checks/duplicate.js
CHANGED
|
@@ -3,7 +3,16 @@ export async function findDuplicateDependencies(graph) {
|
|
|
3
3
|
.map(([name, instances]) => ({
|
|
4
4
|
name,
|
|
5
5
|
versions: Array.from(new Set(instances.map((instance) => instance.version))).sort(),
|
|
6
|
-
instances
|
|
6
|
+
instances,
|
|
7
|
+
confidence: 0.98,
|
|
8
|
+
reasonCodes: [
|
|
9
|
+
"multiple_lockfile_versions",
|
|
10
|
+
"multiple_installation_paths"
|
|
11
|
+
],
|
|
12
|
+
explanation: [
|
|
13
|
+
`Multiple versions of ${name} were found in the lockfile.`,
|
|
14
|
+
"The package is installed from more than one dependency path."
|
|
15
|
+
]
|
|
7
16
|
}))
|
|
8
17
|
.filter((dependency) => dependency.versions.length > 1)
|
|
9
18
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
@@ -17,6 +26,9 @@ export async function runDuplicateCheck(graph) {
|
|
|
17
26
|
id: `duplicate:${item.name}`,
|
|
18
27
|
message: `${item.name} has ${item.versions.length} versions`,
|
|
19
28
|
severity: "warning",
|
|
29
|
+
confidence: item.confidence,
|
|
30
|
+
reasonCodes: item.reasonCodes,
|
|
31
|
+
explanation: item.explanation,
|
|
20
32
|
meta: {
|
|
21
33
|
name: item.name,
|
|
22
34
|
versions: item.versions,
|
package/dist/checks/outdated.js
CHANGED
|
@@ -15,7 +15,16 @@ export async function findOutdatedDependencies(graph, options = {}) {
|
|
|
15
15
|
name,
|
|
16
16
|
current,
|
|
17
17
|
latest,
|
|
18
|
-
updateType: classifyUpdateType(normalized, latest)
|
|
18
|
+
updateType: classifyUpdateType(normalized, latest),
|
|
19
|
+
confidence: 0.97,
|
|
20
|
+
reasonCodes: [
|
|
21
|
+
"latest_registry_version_newer",
|
|
22
|
+
`update_type_${classifyUpdateType(normalized, latest)}`
|
|
23
|
+
],
|
|
24
|
+
explanation: [
|
|
25
|
+
"The npm registry reports a newer published version than the one declared in this project.",
|
|
26
|
+
`The change is classified as a ${classifyUpdateType(normalized, latest)} update.`
|
|
27
|
+
]
|
|
19
28
|
};
|
|
20
29
|
}));
|
|
21
30
|
return results
|
|
@@ -31,6 +40,9 @@ export async function runOutdatedCheck(graph) {
|
|
|
31
40
|
id: `outdated:${item.name}`,
|
|
32
41
|
message: `${item.name} ${item.current} -> ${item.latest}`,
|
|
33
42
|
severity: item.updateType === "major" ? "critical" : "warning",
|
|
43
|
+
confidence: item.confidence,
|
|
44
|
+
reasonCodes: item.reasonCodes,
|
|
45
|
+
explanation: item.explanation,
|
|
34
46
|
meta: {
|
|
35
47
|
name: item.name,
|
|
36
48
|
current: item.current,
|
package/dist/checks/risk.js
CHANGED
|
@@ -23,7 +23,13 @@ export async function findRiskDependencies(graph) {
|
|
|
23
23
|
if (reasons.length === 0) {
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
name,
|
|
28
|
+
reasons,
|
|
29
|
+
confidence: calculateRiskConfidence(reasons),
|
|
30
|
+
reasonCodes: reasons.map(toRiskReasonCode),
|
|
31
|
+
explanation: reasons
|
|
32
|
+
};
|
|
27
33
|
}));
|
|
28
34
|
return results
|
|
29
35
|
.filter((item) => item !== null)
|
|
@@ -38,6 +44,9 @@ export async function runRiskCheck(graph) {
|
|
|
38
44
|
id: `risk:${item.name}`,
|
|
39
45
|
message: `${item.name}: ${item.reasons.join("; ")}`,
|
|
40
46
|
severity: "warning",
|
|
47
|
+
confidence: item.confidence,
|
|
48
|
+
reasonCodes: item.reasonCodes,
|
|
49
|
+
explanation: item.explanation,
|
|
41
50
|
meta: {
|
|
42
51
|
name: item.name,
|
|
43
52
|
reasons: item.reasons
|
|
@@ -45,3 +54,13 @@ export async function runRiskCheck(graph) {
|
|
|
45
54
|
}))
|
|
46
55
|
};
|
|
47
56
|
}
|
|
57
|
+
function calculateRiskConfidence(reasons) {
|
|
58
|
+
return Math.min(0.99, 0.55 + reasons.length * 0.12);
|
|
59
|
+
}
|
|
60
|
+
function toRiskReasonCode(reason) {
|
|
61
|
+
const normalized = reason
|
|
62
|
+
.toLowerCase()
|
|
63
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
64
|
+
.replace(/^_+|_+$/g, "");
|
|
65
|
+
return normalized || "risk_signal_detected";
|
|
66
|
+
}
|
package/dist/checks/unused.js
CHANGED
|
@@ -33,11 +33,11 @@ export async function findUnusedDependencies(rootDir, graph, fileEntries, option
|
|
|
33
33
|
}
|
|
34
34
|
const unusedDependencies = Object.keys(graph.dependencies)
|
|
35
35
|
.filter((name) => !runtimeUsed.has(name))
|
|
36
|
-
.map((name) => (
|
|
36
|
+
.map((name) => buildUnusedDependency(name, "dependencies"));
|
|
37
37
|
const unusedDevDependencies = Object.keys(graph.devDependencies)
|
|
38
38
|
.filter((name) => !devUsed.has(name) && !runtimeUsed.has(name))
|
|
39
39
|
.filter((name) => !isImplicitlyUsedDevDependency(name, hasTypeScriptSources, options.hasTypeScriptConfig))
|
|
40
|
-
.map((name) => (
|
|
40
|
+
.map((name) => buildUnusedDependency(name, "devDependencies"));
|
|
41
41
|
return [...unusedDependencies, ...unusedDevDependencies].sort((left, right) => left.name.localeCompare(right.name));
|
|
42
42
|
}
|
|
43
43
|
export async function runUnusedCheck(context) {
|
|
@@ -49,6 +49,9 @@ export async function runUnusedCheck(context) {
|
|
|
49
49
|
id: `unused:${item.section}:${item.name}`,
|
|
50
50
|
message: `${item.name} appears unused`,
|
|
51
51
|
severity: "warning",
|
|
52
|
+
confidence: item.confidence,
|
|
53
|
+
reasonCodes: item.reasonCodes,
|
|
54
|
+
explanation: item.explanation,
|
|
52
55
|
meta: {
|
|
53
56
|
name: item.name,
|
|
54
57
|
section: item.section
|
|
@@ -127,3 +130,20 @@ function isImplicitlyUsedDevDependency(name, hasTypeScriptSources, hasTypeScript
|
|
|
127
130
|
}
|
|
128
131
|
return false;
|
|
129
132
|
}
|
|
133
|
+
function buildUnusedDependency(name, section) {
|
|
134
|
+
return {
|
|
135
|
+
name,
|
|
136
|
+
section,
|
|
137
|
+
confidence: section === "dependencies" ? 0.9 : 0.82,
|
|
138
|
+
reasonCodes: [
|
|
139
|
+
"no_source_import_found",
|
|
140
|
+
"no_config_reference_found",
|
|
141
|
+
"no_script_reference_found"
|
|
142
|
+
],
|
|
143
|
+
explanation: [
|
|
144
|
+
"No import or require usage was found in scanned source files.",
|
|
145
|
+
"No matching reference was found in recognized config files.",
|
|
146
|
+
"No matching binary or package reference was found in package scripts."
|
|
147
|
+
]
|
|
148
|
+
};
|
|
149
|
+
}
|
package/dist/core/analyzer.d.ts
CHANGED
|
@@ -12,11 +12,17 @@ export interface DuplicateDependency {
|
|
|
12
12
|
name: string;
|
|
13
13
|
versions: string[];
|
|
14
14
|
instances: DuplicateInstance[];
|
|
15
|
+
confidence: number;
|
|
16
|
+
reasonCodes: string[];
|
|
17
|
+
explanation: string[];
|
|
15
18
|
}
|
|
16
19
|
export interface UnusedDependency {
|
|
17
20
|
name: string;
|
|
18
21
|
section: "dependencies" | "devDependencies";
|
|
19
22
|
package?: string;
|
|
23
|
+
confidence: number;
|
|
24
|
+
reasonCodes: string[];
|
|
25
|
+
explanation: string[];
|
|
20
26
|
}
|
|
21
27
|
export interface OutdatedDependency {
|
|
22
28
|
name: string;
|
|
@@ -24,11 +30,17 @@ export interface OutdatedDependency {
|
|
|
24
30
|
latest: string;
|
|
25
31
|
updateType: "major" | "minor" | "patch" | "unknown";
|
|
26
32
|
package?: string;
|
|
33
|
+
confidence: number;
|
|
34
|
+
reasonCodes: string[];
|
|
35
|
+
explanation: string[];
|
|
27
36
|
}
|
|
28
37
|
export interface RiskDependency {
|
|
29
38
|
name: string;
|
|
30
39
|
reasons: string[];
|
|
31
40
|
package?: string;
|
|
41
|
+
confidence: number;
|
|
42
|
+
reasonCodes: string[];
|
|
43
|
+
explanation: string[];
|
|
32
44
|
}
|
|
33
45
|
export interface AnalysisResult {
|
|
34
46
|
outputVersion: string;
|
|
@@ -60,7 +72,7 @@ export interface PackageAnalysisResult {
|
|
|
60
72
|
risks: RiskDependency[];
|
|
61
73
|
suggestions: string[];
|
|
62
74
|
}
|
|
63
|
-
export declare const OUTPUT_VERSION = "1.
|
|
75
|
+
export declare const OUTPUT_VERSION = "1.1";
|
|
64
76
|
export interface ScoreBreakdown {
|
|
65
77
|
baseScore: number;
|
|
66
78
|
duplicates: number;
|
package/dist/core/analyzer.js
CHANGED
|
@@ -8,7 +8,7 @@ import { findWorkspacePackages } from "../utils/workspaces.js";
|
|
|
8
8
|
import { buildDependencyGraph } from "./graph-builder.js";
|
|
9
9
|
import { calculateHealthScore } from "./scorer.js";
|
|
10
10
|
import { buildAnalysisContext } from "./context.js";
|
|
11
|
-
export const OUTPUT_VERSION = "1.
|
|
11
|
+
export const OUTPUT_VERSION = "1.1";
|
|
12
12
|
export async function analyzeProject(options = {}) {
|
|
13
13
|
const rootDir = path.resolve(options.rootDir ?? process.cwd());
|
|
14
14
|
const loadedConfig = await loadDepBrainConfig(rootDir, options.configPath);
|
|
@@ -264,13 +264,19 @@ function mapDuplicateIssues(issues) {
|
|
|
264
264
|
versions: Array.isArray(issue.meta?.versions) ? issue.meta?.versions : [],
|
|
265
265
|
instances: Array.isArray(issue.meta?.instances)
|
|
266
266
|
? issue.meta?.instances
|
|
267
|
-
: []
|
|
267
|
+
: [],
|
|
268
|
+
confidence: normalizeConfidence(issue.confidence),
|
|
269
|
+
reasonCodes: normalizeStringArray(issue.reasonCodes),
|
|
270
|
+
explanation: normalizeStringArray(issue.explanation)
|
|
268
271
|
}));
|
|
269
272
|
}
|
|
270
273
|
function mapUnusedIssues(issues) {
|
|
271
274
|
return issues.map((issue) => ({
|
|
272
275
|
name: String(issue.meta?.name ?? issue.package ?? "unknown"),
|
|
273
|
-
section: issue.meta?.section === "devDependencies" ? "devDependencies" : "dependencies"
|
|
276
|
+
section: issue.meta?.section === "devDependencies" ? "devDependencies" : "dependencies",
|
|
277
|
+
confidence: normalizeConfidence(issue.confidence),
|
|
278
|
+
reasonCodes: normalizeStringArray(issue.reasonCodes),
|
|
279
|
+
explanation: normalizeStringArray(issue.explanation)
|
|
274
280
|
}));
|
|
275
281
|
}
|
|
276
282
|
function mapOutdatedIssues(issues) {
|
|
@@ -280,15 +286,33 @@ function mapOutdatedIssues(issues) {
|
|
|
280
286
|
latest: String(issue.meta?.latest ?? ""),
|
|
281
287
|
updateType: issue.meta?.updateType === "major" || issue.meta?.updateType === "minor" || issue.meta?.updateType === "patch"
|
|
282
288
|
? issue.meta.updateType
|
|
283
|
-
: "unknown"
|
|
289
|
+
: "unknown",
|
|
290
|
+
confidence: normalizeConfidence(issue.confidence),
|
|
291
|
+
reasonCodes: normalizeStringArray(issue.reasonCodes),
|
|
292
|
+
explanation: normalizeStringArray(issue.explanation)
|
|
284
293
|
}));
|
|
285
294
|
}
|
|
286
295
|
function mapRiskIssues(issues) {
|
|
287
296
|
return issues.map((issue) => ({
|
|
288
297
|
name: String(issue.meta?.name ?? issue.package ?? "unknown"),
|
|
289
|
-
reasons: Array.isArray(issue.meta?.reasons) ? issue.meta?.reasons : []
|
|
298
|
+
reasons: Array.isArray(issue.meta?.reasons) ? issue.meta?.reasons : [],
|
|
299
|
+
confidence: normalizeConfidence(issue.confidence),
|
|
300
|
+
reasonCodes: normalizeStringArray(issue.reasonCodes),
|
|
301
|
+
explanation: normalizeStringArray(issue.explanation)
|
|
290
302
|
}));
|
|
291
303
|
}
|
|
304
|
+
function normalizeConfidence(value) {
|
|
305
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
306
|
+
return 0.5;
|
|
307
|
+
}
|
|
308
|
+
return Math.min(0.99, Math.max(0, Number(value.toFixed(2))));
|
|
309
|
+
}
|
|
310
|
+
function normalizeStringArray(value) {
|
|
311
|
+
if (!Array.isArray(value)) {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
return value.filter((entry) => typeof entry === "string");
|
|
315
|
+
}
|
|
292
316
|
function buildScoreBreakdown(counts, config) {
|
|
293
317
|
return {
|
|
294
318
|
baseScore: 100,
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export type IssueSeverity = "info" | "warning" | "critical";
|
|
2
|
+
export type ReasonCode = string;
|
|
2
3
|
export type Issue = {
|
|
3
4
|
id: string;
|
|
4
5
|
message: string;
|
|
5
6
|
package?: string;
|
|
6
7
|
severity: IssueSeverity;
|
|
8
|
+
confidence?: number;
|
|
9
|
+
reasonCodes?: ReasonCode[];
|
|
10
|
+
explanation?: string[];
|
|
7
11
|
meta?: Record<string, unknown>;
|
|
8
12
|
};
|
|
9
13
|
export type CheckResult = {
|
|
@@ -16,16 +16,16 @@ export function renderConsoleReport(result) {
|
|
|
16
16
|
lines.push(`- ${pkg.name}: ${pkg.score}/100, D:${pkg.duplicates.length} U:${pkg.unused.length} O:${pkg.outdated.length} R:${pkg.risks.length}`);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
appendSection(lines, "Duplicate dependencies", result.duplicates.map((item) => `${item.name}: ${item.versions.join(", ")}
|
|
20
|
-
appendSection(lines, "Unused dependencies", result.unused.map((item) => item.package
|
|
19
|
+
appendSection(lines, "Duplicate dependencies", result.duplicates.map((item) => formatEntry(`${item.name}: ${item.versions.join(", ")}`, item.confidence, item.explanation)));
|
|
20
|
+
appendSection(lines, "Unused dependencies", result.unused.map((item) => formatEntry(item.package
|
|
21
21
|
? `${item.name} (${item.section}) [${item.package}]`
|
|
22
|
-
: `${item.name} (${item.section})
|
|
23
|
-
appendSection(lines, "Outdated dependencies", result.outdated.map((item) => item.package
|
|
22
|
+
: `${item.name} (${item.section})`, item.confidence, item.explanation)));
|
|
23
|
+
appendSection(lines, "Outdated dependencies", result.outdated.map((item) => formatEntry(item.package
|
|
24
24
|
? `${item.name}: ${item.current} -> ${item.latest} [${item.updateType}] [${item.package}]`
|
|
25
|
-
: `${item.name}: ${item.current} -> ${item.latest} [${item.updateType}]
|
|
26
|
-
appendSection(lines, "Risky dependencies", result.risks.map((item) => item.package
|
|
25
|
+
: `${item.name}: ${item.current} -> ${item.latest} [${item.updateType}]`, item.confidence, item.explanation)));
|
|
26
|
+
appendSection(lines, "Risky dependencies", result.risks.map((item) => formatEntry(item.package
|
|
27
27
|
? `${item.name}: ${item.reasons.join("; ")} [${item.package}]`
|
|
28
|
-
: `${item.name}: ${item.reasons.join("; ")}
|
|
28
|
+
: `${item.name}: ${item.reasons.join("; ")}`, item.confidence, item.explanation)));
|
|
29
29
|
appendSection(lines, "Policy reasons", result.policy.reasons);
|
|
30
30
|
if (result.suggestions.length > 0) {
|
|
31
31
|
lines.push("");
|
|
@@ -50,3 +50,7 @@ function appendSection(lines, title, entries) {
|
|
|
50
50
|
lines.push(`- ${entry}`);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
function formatEntry(label, confidence, explanation) {
|
|
54
|
+
const reasonSummary = explanation.length > 0 ? ` | why: ${explanation.join("; ")}` : "";
|
|
55
|
+
return `${label} | confidence ${Math.round(confidence * 100)}%${reasonSummary}`;
|
|
56
|
+
}
|
|
@@ -20,16 +20,16 @@ export function renderMarkdownReport(result) {
|
|
|
20
20
|
lines.push(`- Outdated: ${result.outdated.length}`);
|
|
21
21
|
lines.push(`- Risks: ${result.risks.length}`);
|
|
22
22
|
lines.push("");
|
|
23
|
-
appendSection(lines, "Duplicate dependencies", result.duplicates.map((item) => `${item.name}: ${item.versions.join(", ")}
|
|
24
|
-
appendSection(lines, "Unused dependencies", result.unused.map((item) => item.package
|
|
23
|
+
appendSection(lines, "Duplicate dependencies", result.duplicates.map((item) => formatEntry(`${item.name}: ${item.versions.join(", ")}`, item.confidence, item.explanation)));
|
|
24
|
+
appendSection(lines, "Unused dependencies", result.unused.map((item) => formatEntry(item.package
|
|
25
25
|
? `${item.name} (${item.section}) [${item.package}]`
|
|
26
|
-
: `${item.name} (${item.section})
|
|
27
|
-
appendSection(lines, "Outdated dependencies", result.outdated.map((item) => item.package
|
|
26
|
+
: `${item.name} (${item.section})`, item.confidence, item.explanation)));
|
|
27
|
+
appendSection(lines, "Outdated dependencies", result.outdated.map((item) => formatEntry(item.package
|
|
28
28
|
? `${item.name}: ${item.current} -> ${item.latest} [${item.updateType}] [${item.package}]`
|
|
29
|
-
: `${item.name}: ${item.current} -> ${item.latest} [${item.updateType}]
|
|
30
|
-
appendSection(lines, "Risky dependencies", result.risks.map((item) => item.package
|
|
29
|
+
: `${item.name}: ${item.current} -> ${item.latest} [${item.updateType}]`, item.confidence, item.explanation)));
|
|
30
|
+
appendSection(lines, "Risky dependencies", result.risks.map((item) => formatEntry(item.package
|
|
31
31
|
? `${item.name}: ${item.reasons.join("; ")} [${item.package}]`
|
|
32
|
-
: `${item.name}: ${item.reasons.join("; ")}
|
|
32
|
+
: `${item.name}: ${item.reasons.join("; ")}`, item.confidence, item.explanation)));
|
|
33
33
|
appendSection(lines, "Policy reasons", result.policy.reasons);
|
|
34
34
|
if (result.suggestions.length > 0) {
|
|
35
35
|
lines.push("## Suggestions");
|
|
@@ -50,3 +50,7 @@ function appendSection(lines, title, items) {
|
|
|
50
50
|
}
|
|
51
51
|
lines.push("");
|
|
52
52
|
}
|
|
53
|
+
function formatEntry(label, confidence, explanation) {
|
|
54
|
+
const reasonSummary = explanation.length > 0 ? ` | why: ${explanation.join("; ")}` : "";
|
|
55
|
+
return `${label} | confidence ${Math.round(confidence * 100)}%${reasonSummary}`;
|
|
56
|
+
}
|