@rainy-updates/cli 0.5.4 → 0.5.7

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 (66) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +5 -0
  3. package/dist/bin/cli.js +16 -438
  4. package/dist/bin/dispatch.d.ts +16 -0
  5. package/dist/bin/dispatch.js +150 -0
  6. package/dist/bin/help.d.ts +1 -0
  7. package/dist/bin/help.js +284 -0
  8. package/dist/commands/audit/runner.js +43 -26
  9. package/dist/commands/dashboard/parser.d.ts +2 -0
  10. package/dist/commands/dashboard/parser.js +59 -0
  11. package/dist/commands/dashboard/runner.d.ts +2 -0
  12. package/dist/commands/dashboard/runner.js +47 -0
  13. package/dist/commands/doctor/parser.js +12 -0
  14. package/dist/commands/doctor/runner.js +5 -2
  15. package/dist/commands/ga/parser.d.ts +2 -0
  16. package/dist/commands/ga/parser.js +50 -0
  17. package/dist/commands/ga/runner.d.ts +2 -0
  18. package/dist/commands/ga/runner.js +129 -0
  19. package/dist/commands/resolve/runner.js +7 -3
  20. package/dist/commands/review/parser.js +6 -0
  21. package/dist/commands/review/runner.js +4 -3
  22. package/dist/core/analysis/options.d.ts +6 -0
  23. package/dist/core/analysis/options.js +69 -0
  24. package/dist/core/analysis/review-items.d.ts +4 -0
  25. package/dist/core/analysis/review-items.js +128 -0
  26. package/dist/core/analysis/run-silenced.d.ts +1 -0
  27. package/dist/core/analysis/run-silenced.js +14 -0
  28. package/dist/core/analysis-bundle.d.ts +4 -0
  29. package/dist/core/analysis-bundle.js +33 -0
  30. package/dist/core/artifacts.d.ts +3 -0
  31. package/dist/core/artifacts.js +48 -0
  32. package/dist/core/check.js +6 -1
  33. package/dist/core/doctor/findings.d.ts +2 -0
  34. package/dist/core/doctor/findings.js +166 -0
  35. package/dist/core/doctor/render.d.ts +3 -0
  36. package/dist/core/doctor/render.js +44 -0
  37. package/dist/core/doctor/result.d.ts +2 -0
  38. package/dist/core/doctor/result.js +55 -0
  39. package/dist/core/doctor/score.d.ts +5 -0
  40. package/dist/core/doctor/score.js +28 -0
  41. package/dist/core/options.d.ts +7 -1
  42. package/dist/core/options.js +14 -0
  43. package/dist/core/review-model.d.ts +3 -3
  44. package/dist/core/review-model.js +55 -245
  45. package/dist/core/review-verdict.d.ts +2 -0
  46. package/dist/core/review-verdict.js +14 -0
  47. package/dist/core/summary.js +19 -0
  48. package/dist/output/format.js +22 -0
  49. package/dist/output/github.js +12 -0
  50. package/dist/output/sarif.js +16 -0
  51. package/dist/types/index.d.ts +120 -0
  52. package/dist/ui/dashboard/DashboardTUI.d.ts +6 -0
  53. package/dist/ui/dashboard/DashboardTUI.js +34 -0
  54. package/dist/ui/dashboard/components/DetailPanel.d.ts +4 -0
  55. package/dist/ui/dashboard/components/DetailPanel.js +30 -0
  56. package/dist/ui/dashboard/components/Footer.d.ts +4 -0
  57. package/dist/ui/dashboard/components/Footer.js +9 -0
  58. package/dist/ui/dashboard/components/Header.d.ts +4 -0
  59. package/dist/ui/dashboard/components/Header.js +12 -0
  60. package/dist/ui/dashboard/components/Sidebar.d.ts +4 -0
  61. package/dist/ui/dashboard/components/Sidebar.js +23 -0
  62. package/dist/ui/dashboard/store.d.ts +34 -0
  63. package/dist/ui/dashboard/store.js +148 -0
  64. package/dist/ui/tui.d.ts +2 -2
  65. package/dist/ui/tui.js +310 -79
  66. package/package.json +1 -1
@@ -1,93 +1,54 @@
1
1
  import path from "node:path";
2
- import process from "node:process";
3
- import { check } from "./check.js";
4
2
  import { createSummary, finalizeSummary } from "./summary.js";
5
- import { applyImpactScores } from "./impact.js";
6
- import { applyRiskAssessments } from "../risk/index.js";
3
+ import { buildAnalysisBundle } from "./analysis-bundle.js";
4
+ export { createDoctorResult } from "./doctor/result.js";
5
+ export { renderDoctorAgentReport, renderDoctorResult, } from "./doctor/render.js";
6
+ import { deriveReviewVerdict } from "./review-verdict.js";
7
7
  export async function buildReviewResult(options) {
8
- const baseCheckOptions = {
9
- ...options,
10
- interactive: false,
11
- showImpact: true,
12
- showHomepage: true,
13
- };
14
- const checkResult = await check(baseCheckOptions);
15
- const [auditResult, resolveResult, healthResult, licenseResult, unusedResult] = await runSilenced(() => Promise.all([
16
- import("../commands/audit/runner.js").then((mod) => mod.runAudit(toAuditOptions(options))),
17
- import("../commands/resolve/runner.js").then((mod) => mod.runResolve(toResolveOptions(options))),
18
- import("../commands/health/runner.js").then((mod) => mod.runHealth(toHealthOptions(options))),
19
- import("../commands/licenses/runner.js").then((mod) => mod.runLicenses(toLicenseOptions(options))),
20
- import("../commands/unused/runner.js").then((mod) => mod.runUnused(toUnusedOptions(options))),
21
- ]));
22
- const advisoryPackages = new Set(auditResult.packages.map((pkg) => pkg.packageName));
23
- const impactedUpdates = applyImpactScores(checkResult.updates, {
24
- advisoryPackages,
25
- workspaceDependentCount: (name) => checkResult.updates.filter((item) => item.name === name).length,
26
- });
27
- const healthByName = new Map(healthResult.metrics.map((metric) => [metric.name, metric]));
28
- const advisoriesByName = new Map();
29
- const conflictsByName = new Map();
30
- const licenseByName = new Map(licenseResult.packages.map((pkg) => [pkg.name, pkg]));
31
- const licenseViolationNames = new Set(licenseResult.violations.map((pkg) => pkg.name));
32
- const unusedByName = new Map();
33
- for (const advisory of auditResult.advisories) {
34
- const list = advisoriesByName.get(advisory.packageName) ?? [];
35
- list.push(advisory);
36
- advisoriesByName.set(advisory.packageName, list);
37
- }
38
- for (const conflict of resolveResult.conflicts) {
39
- const list = conflictsByName.get(conflict.requester) ?? [];
40
- list.push(conflict);
41
- conflictsByName.set(conflict.requester, list);
42
- const peerList = conflictsByName.get(conflict.peer) ?? [];
43
- peerList.push(conflict);
44
- conflictsByName.set(conflict.peer, peerList);
45
- }
46
- for (const issue of [...unusedResult.unused, ...unusedResult.missing]) {
47
- const list = unusedByName.get(issue.name) ?? [];
48
- list.push(issue);
49
- unusedByName.set(issue.name, list);
50
- }
51
- const baseItems = impactedUpdates
52
- .map((update) => enrichUpdate(update, advisoriesByName, conflictsByName, healthByName, licenseByName, licenseViolationNames, unusedByName))
53
- .filter((item) => matchesReviewFilters(item, options));
54
- const items = applyRiskAssessments(baseItems, {
55
- knownPackageNames: new Set(checkResult.updates.map((item) => item.name)),
56
- }).filter((item) => matchesReviewFilters(item, options));
8
+ const includeChangelog = ("showChangelog" in options && options.showChangelog === true) ||
9
+ ("includeChangelog" in options && options.includeChangelog === true) ||
10
+ options.interactive === true;
11
+ const analysis = await buildAnalysisBundle(options, { includeChangelog });
12
+ const items = analysis.items.filter((item) => matchesReviewFilters(item, options));
13
+ const checkResult = analysis.check;
57
14
  const summary = createReviewSummary(checkResult.summary, items, [
58
15
  ...checkResult.errors,
59
- ...auditResult.errors,
60
- ...resolveResult.errors,
61
- ...healthResult.errors,
62
- ...licenseResult.errors,
63
- ...unusedResult.errors,
16
+ ...analysis.audit.errors,
17
+ ...analysis.resolve.errors,
18
+ ...analysis.health.errors,
19
+ ...analysis.licenses.errors,
20
+ ...analysis.unused.errors,
64
21
  ], [
65
22
  ...checkResult.warnings,
66
- ...auditResult.warnings,
67
- ...resolveResult.warnings,
68
- ...healthResult.warnings,
69
- ...licenseResult.warnings,
70
- ...unusedResult.warnings,
71
- ], options.interactive === true);
23
+ ...analysis.audit.warnings,
24
+ ...analysis.resolve.warnings,
25
+ ...analysis.health.warnings,
26
+ ...analysis.licenses.warnings,
27
+ ...analysis.unused.warnings,
28
+ ], options.interactive === true, analysis.degradedSources);
72
29
  return {
73
30
  projectPath: checkResult.projectPath,
74
31
  target: checkResult.target,
75
32
  summary,
33
+ analysis,
76
34
  items,
77
35
  updates: items.map((item) => item.update),
78
- errors: [...checkResult.errors, ...auditResult.errors, ...resolveResult.errors, ...healthResult.errors, ...licenseResult.errors, ...unusedResult.errors],
79
- warnings: [...checkResult.warnings, ...auditResult.warnings, ...resolveResult.warnings, ...healthResult.warnings, ...licenseResult.warnings, ...unusedResult.warnings],
80
- };
81
- }
82
- export function createDoctorResult(review) {
83
- const verdict = review.summary.verdict ?? deriveVerdict(review.items, review.errors);
84
- const primaryFindings = buildPrimaryFindings(review);
85
- return {
86
- verdict,
87
- summary: review.summary,
88
- review,
89
- primaryFindings,
90
- recommendedCommand: recommendCommand(review, verdict),
36
+ errors: [
37
+ ...checkResult.errors,
38
+ ...analysis.audit.errors,
39
+ ...analysis.resolve.errors,
40
+ ...analysis.health.errors,
41
+ ...analysis.licenses.errors,
42
+ ...analysis.unused.errors,
43
+ ],
44
+ warnings: [
45
+ ...checkResult.warnings,
46
+ ...analysis.audit.warnings,
47
+ ...analysis.resolve.warnings,
48
+ ...analysis.health.warnings,
49
+ ...analysis.licenses.warnings,
50
+ ...analysis.unused.warnings,
51
+ ],
91
52
  };
92
53
  }
93
54
  export function renderReviewResult(review) {
@@ -115,6 +76,7 @@ export function renderReviewResult(review) {
115
76
  item.update.licenseStatus && item.update.licenseStatus !== "allowed"
116
77
  ? `license=${item.update.licenseStatus}`
117
78
  : undefined,
79
+ item.update.policyAction ? `policy=${item.update.policyAction}` : undefined,
118
80
  ].filter(Boolean);
119
81
  lines.push(`- ${path.basename(item.update.packagePath)} :: ${item.update.name} ${item.update.fromRange} -> ${item.update.toRange} (${notes.join(", ")})`);
120
82
  if (item.update.riskReasons && item.update.riskReasons.length > 0) {
@@ -123,6 +85,9 @@ export function renderReviewResult(review) {
123
85
  if (item.update.recommendedAction) {
124
86
  lines.push(` action: ${item.update.recommendedAction}`);
125
87
  }
88
+ if (item.update.releaseNotesSummary) {
89
+ lines.push(` notes: ${item.update.releaseNotesSummary.title} - ${item.update.releaseNotesSummary.excerpt}`);
90
+ }
126
91
  if (item.update.homepage) {
127
92
  lines.push(` homepage: ${item.update.homepage}`);
128
93
  }
@@ -146,47 +111,6 @@ export function renderReviewResult(review) {
146
111
  lines.push(`Summary: ${review.summary.updatesFound} updates, riskPackages=${review.summary.riskPackages ?? 0}, securityPackages=${review.summary.securityPackages ?? 0}, peerConflictPackages=${review.summary.peerConflictPackages ?? 0}`);
147
112
  return lines.join("\n");
148
113
  }
149
- export function renderDoctorResult(result, verdictOnly = false) {
150
- const lines = [
151
- `State: ${result.verdict}`,
152
- `PrimaryRisk: ${result.primaryFindings[0] ?? "No blocking findings."}`,
153
- `NextAction: ${result.recommendedCommand}`,
154
- ];
155
- if (!verdictOnly) {
156
- lines.push(`Counts: updates=${result.summary.updatesFound}, security=${result.summary.securityPackages ?? 0}, risk=${result.summary.riskPackages ?? 0}, peer=${result.summary.peerConflictPackages ?? 0}, license=${result.summary.licenseViolationPackages ?? 0}`);
157
- }
158
- return lines.join("\n");
159
- }
160
- function enrichUpdate(update, advisoriesByName, conflictsByName, healthByName, licenseByName, licenseViolationNames, unusedByName) {
161
- const advisories = advisoriesByName.get(update.name) ?? [];
162
- const peerConflicts = conflictsByName.get(update.name) ?? [];
163
- const health = healthByName.get(update.name);
164
- const license = licenseByName.get(update.name);
165
- const unusedIssues = unusedByName.get(update.name) ?? [];
166
- return {
167
- update: {
168
- ...update,
169
- advisoryCount: advisories.length,
170
- peerConflictSeverity: peerConflicts.some((item) => item.severity === "error")
171
- ? "error"
172
- : peerConflicts.length > 0
173
- ? "warning"
174
- : "none",
175
- licenseStatus: licenseViolationNames.has(update.name)
176
- ? "denied"
177
- : license
178
- ? "allowed"
179
- : "review",
180
- healthStatus: health?.flags[0] ?? "healthy",
181
- },
182
- advisories,
183
- health,
184
- peerConflicts,
185
- license,
186
- unusedIssues,
187
- selected: true,
188
- };
189
- }
190
114
  function matchesReviewFilters(item, options) {
191
115
  if ("securityOnly" in options && options.securityOnly && item.advisories.length === 0) {
192
116
  return false;
@@ -208,7 +132,7 @@ function riskMatches(current, threshold) {
208
132
  };
209
133
  return order[current ?? "low"] >= order[threshold];
210
134
  }
211
- function createReviewSummary(base, items, errors, warnings, interactiveSession) {
135
+ function createReviewSummary(base, items, errors, warnings, interactiveSession, degradedSources) {
212
136
  const summary = finalizeSummary(createSummary({
213
137
  scannedPackages: base.scannedPackages,
214
138
  totalDependencies: base.totalDependencies,
@@ -242,131 +166,17 @@ function createReviewSummary(base, items, errors, warnings, interactiveSession)
242
166
  summary.riskPackages = items.filter((item) => item.update.riskLevel === "critical" || item.update.riskLevel === "high").length;
243
167
  summary.peerConflictPackages = items.filter((item) => item.update.peerConflictSeverity && item.update.peerConflictSeverity !== "none").length;
244
168
  summary.licenseViolationPackages = items.filter((item) => item.update.licenseStatus === "denied").length;
245
- summary.verdict = deriveVerdict(items, errors);
246
- return summary;
247
- }
248
- function deriveVerdict(items, errors) {
249
- if (items.some((item) => item.update.peerConflictSeverity === "error" ||
250
- item.update.licenseStatus === "denied")) {
251
- return "blocked";
252
- }
253
- if (items.some((item) => item.advisories.length > 0 || item.update.riskLevel === "critical")) {
254
- return "actionable";
255
- }
256
- if (errors.length > 0 ||
257
- items.some((item) => item.update.riskLevel === "high" || item.update.diffType === "major")) {
258
- return "review";
259
- }
260
- return "safe";
261
- }
262
- function buildPrimaryFindings(review) {
263
- const findings = [];
264
- if ((review.summary.peerConflictPackages ?? 0) > 0) {
265
- findings.push(`${review.summary.peerConflictPackages} package(s) have peer conflicts.`);
266
- }
267
- if ((review.summary.licenseViolationPackages ?? 0) > 0) {
268
- findings.push(`${review.summary.licenseViolationPackages} package(s) violate license policy.`);
269
- }
270
- if ((review.summary.securityPackages ?? 0) > 0) {
271
- findings.push(`${review.summary.securityPackages} package(s) have security advisories.`);
272
- }
273
- if ((review.summary.riskPackages ?? 0) > 0) {
274
- findings.push(`${review.summary.riskPackages} package(s) are high risk.`);
275
- }
276
- if (review.errors.length > 0) {
277
- findings.push(`${review.errors.length} execution error(s) need review before treating the result as clean.`);
278
- }
279
- if (findings.length === 0) {
280
- findings.push("No blocking findings; remaining updates are low-risk.");
281
- }
282
- return findings;
283
- }
284
- function recommendCommand(review, verdict) {
285
- if (verdict === "blocked")
286
- return "rup review --interactive";
287
- if ((review.summary.securityPackages ?? 0) > 0)
288
- return "rup review --security-only";
289
- if (review.errors.length > 0 || review.items.length > 0)
290
- return "rup review --interactive";
291
- return "rup check";
292
- }
293
- async function runSilenced(fn) {
294
- const stdoutWrite = process.stdout.write.bind(process.stdout);
295
- const stderrWrite = process.stderr.write.bind(process.stderr);
296
- process.stdout.write = (() => true);
297
- process.stderr.write = (() => true);
298
- try {
299
- return await fn();
300
- }
301
- finally {
302
- process.stdout.write = stdoutWrite;
303
- process.stderr.write = stderrWrite;
304
- }
305
- }
306
- function toAuditOptions(options) {
307
- return {
308
- cwd: options.cwd,
309
- workspace: options.workspace,
310
- severity: undefined,
311
- fix: false,
312
- dryRun: true,
313
- commit: false,
314
- packageManager: "auto",
315
- reportFormat: "json",
316
- sourceMode: "auto",
317
- jsonFile: undefined,
318
- concurrency: options.concurrency,
319
- registryTimeoutMs: options.registryTimeoutMs,
320
- };
321
- }
322
- function toResolveOptions(options) {
323
- return {
324
- cwd: options.cwd,
325
- workspace: options.workspace,
326
- afterUpdate: true,
327
- safe: false,
328
- jsonFile: undefined,
329
- concurrency: options.concurrency,
330
- registryTimeoutMs: options.registryTimeoutMs,
331
- cacheTtlSeconds: options.cacheTtlSeconds,
332
- };
333
- }
334
- function toHealthOptions(options) {
335
- return {
336
- cwd: options.cwd,
337
- workspace: options.workspace,
338
- staleDays: 365,
339
- includeDeprecated: true,
340
- includeAlternatives: false,
341
- reportFormat: "json",
342
- jsonFile: undefined,
343
- concurrency: options.concurrency,
344
- registryTimeoutMs: options.registryTimeoutMs,
345
- };
346
- }
347
- function toLicenseOptions(options) {
348
- return {
349
- cwd: options.cwd,
350
- workspace: options.workspace,
351
- allow: undefined,
352
- deny: undefined,
353
- sbomFile: undefined,
354
- jsonFile: undefined,
355
- diffMode: false,
356
- concurrency: options.concurrency,
357
- registryTimeoutMs: options.registryTimeoutMs,
358
- cacheTtlSeconds: options.cacheTtlSeconds,
359
- };
360
- }
361
- function toUnusedOptions(options) {
362
- return {
363
- cwd: options.cwd,
364
- workspace: options.workspace,
365
- srcDirs: ["src", "."],
366
- includeDevDependencies: true,
367
- fix: false,
368
- dryRun: true,
369
- jsonFile: undefined,
370
- concurrency: options.concurrency,
169
+ summary.policyActionCounts = {
170
+ allow: items.filter((item) => item.update.policyAction === "allow").length,
171
+ review: items.filter((item) => item.update.policyAction === "review").length,
172
+ block: items.filter((item) => item.update.policyAction === "block").length,
173
+ monitor: items.filter((item) => item.update.policyAction === "monitor").length,
371
174
  };
175
+ summary.blockedPackages = items.filter((item) => item.update.decisionState === "blocked").length;
176
+ summary.reviewPackages = items.filter((item) => item.update.decisionState === "review").length;
177
+ summary.monitorPackages = items.filter((item) => item.update.policyAction === "monitor").length;
178
+ summary.decisionPackages = items.length;
179
+ summary.degradedSources = degradedSources;
180
+ summary.verdict = deriveReviewVerdict(items, errors);
181
+ return summary;
372
182
  }
@@ -0,0 +1,2 @@
1
+ import type { ReviewItem, Verdict } from "../types/index.js";
2
+ export declare function deriveReviewVerdict(items: ReviewItem[], errors: string[]): Verdict;
@@ -0,0 +1,14 @@
1
+ export function deriveReviewVerdict(items, errors) {
2
+ if (items.some((item) => item.update.peerConflictSeverity === "error" ||
3
+ item.update.licenseStatus === "denied")) {
4
+ return "blocked";
5
+ }
6
+ if (items.some((item) => item.advisories.length > 0 || item.update.riskLevel === "critical")) {
7
+ return "actionable";
8
+ }
9
+ if (errors.length > 0 ||
10
+ items.some((item) => item.update.riskLevel === "high" || item.update.diffType === "major")) {
11
+ return "review";
12
+ }
13
+ return "safe";
14
+ }
@@ -50,6 +50,25 @@ export function createSummary(input) {
50
50
  peerConflictPackages: 0,
51
51
  licenseViolationPackages: 0,
52
52
  privateRegistryPackages: 0,
53
+ runId: undefined,
54
+ artifactManifest: undefined,
55
+ policyActionCounts: undefined,
56
+ blockedPackages: 0,
57
+ reviewPackages: 0,
58
+ monitorPackages: 0,
59
+ decisionPackages: 0,
60
+ releaseVolatilityPackages: 0,
61
+ engineConflictPackages: 0,
62
+ degradedSources: [],
63
+ cacheBackend: undefined,
64
+ binaryRecommended: false,
65
+ gaReady: undefined,
66
+ dependencyHealthScore: undefined,
67
+ findingCountsByCategory: undefined,
68
+ findingCountsBySeverity: undefined,
69
+ primaryFindingCode: undefined,
70
+ primaryFindingCategory: undefined,
71
+ nextActionReason: undefined,
53
72
  };
54
73
  }
55
74
  export function finalizeSummary(summary) {
@@ -62,6 +62,18 @@ export function renderResult(result, format, display = {}) {
62
62
  `security_packages=${result.summary.securityPackages ?? 0}`,
63
63
  `peer_conflict_packages=${result.summary.peerConflictPackages ?? 0}`,
64
64
  `license_violation_packages=${result.summary.licenseViolationPackages ?? 0}`,
65
+ `run_id=${result.summary.runId ?? ""}`,
66
+ `artifact_manifest=${result.summary.artifactManifest ?? ""}`,
67
+ `blocked_packages=${result.summary.blockedPackages ?? 0}`,
68
+ `review_packages=${result.summary.reviewPackages ?? 0}`,
69
+ `monitor_packages=${result.summary.monitorPackages ?? 0}`,
70
+ `cache_backend=${result.summary.cacheBackend ?? ""}`,
71
+ `degraded_sources=${(result.summary.degradedSources ?? []).join(",")}`,
72
+ `ga_ready=${result.summary.gaReady === true ? "1" : "0"}`,
73
+ `dependency_health_score=${result.summary.dependencyHealthScore ?? ""}`,
74
+ `primary_finding_code=${result.summary.primaryFindingCode ?? ""}`,
75
+ `primary_finding_category=${result.summary.primaryFindingCategory ?? ""}`,
76
+ `next_action_reason=${result.summary.nextActionReason ?? ""}`,
65
77
  ].join("\n");
66
78
  }
67
79
  const lines = [];
@@ -89,12 +101,16 @@ export function renderResult(result, format, display = {}) {
89
101
  display.showHomepage && update.homepage ? update.homepage : undefined,
90
102
  update.riskLevel ? `risk=${update.riskLevel}` : undefined,
91
103
  typeof update.riskScore === "number" ? `score=${update.riskScore}` : undefined,
104
+ update.policyAction ? `policy=${update.policyAction}` : undefined,
92
105
  ]
93
106
  .filter(Boolean)
94
107
  .join(", ")})`);
95
108
  if (update.recommendedAction) {
96
109
  lines.push(` action: ${update.recommendedAction}`);
97
110
  }
111
+ if (update.releaseNotesSummary) {
112
+ lines.push(` notes: ${update.releaseNotesSummary.title} — ${update.releaseNotesSummary.excerpt}`);
113
+ }
98
114
  }
99
115
  }
100
116
  if (result.errors.length > 0) {
@@ -118,6 +134,12 @@ export function renderResult(result, format, display = {}) {
118
134
  if (result.summary.verdict) {
119
135
  lines.push(`Verdict=${result.summary.verdict}, riskPackages=${result.summary.riskPackages ?? 0}, securityPackages=${result.summary.securityPackages ?? 0}, peerConflictPackages=${result.summary.peerConflictPackages ?? 0}, licenseViolationPackages=${result.summary.licenseViolationPackages ?? 0}`);
120
136
  }
137
+ if (typeof result.summary.dependencyHealthScore === "number") {
138
+ lines.push(`DependencyHealthScore=${result.summary.dependencyHealthScore}, primaryFinding=${result.summary.primaryFindingCode ?? "none"}, category=${result.summary.primaryFindingCategory ?? "none"}`);
139
+ }
140
+ if (result.summary.runId) {
141
+ lines.push(`RunId=${result.summary.runId}, artifactManifest=${result.summary.artifactManifest ?? "none"}, blockedPackages=${result.summary.blockedPackages ?? 0}, reviewPackages=${result.summary.reviewPackages ?? 0}, monitorPackages=${result.summary.monitorPackages ?? 0}`);
142
+ }
121
143
  lines.push(`Contract v${result.summary.contractVersion}, failReason=${result.summary.failReason}, duration=${result.summary.durationMs.total}ms`);
122
144
  if (result.summary.fixPrApplied) {
123
145
  lines.push(`Fix PR: applied on branch ${result.summary.fixBranchName ?? "unknown"} (${result.summary.fixCommitSha ?? "no-commit"})`);
@@ -26,6 +26,18 @@ export async function writeGitHubOutput(filePath, result) {
26
26
  `security_packages=${result.summary.securityPackages ?? 0}`,
27
27
  `peer_conflict_packages=${result.summary.peerConflictPackages ?? 0}`,
28
28
  `license_violation_packages=${result.summary.licenseViolationPackages ?? 0}`,
29
+ `run_id=${result.summary.runId ?? ""}`,
30
+ `artifact_manifest=${result.summary.artifactManifest ?? ""}`,
31
+ `blocked_packages=${result.summary.blockedPackages ?? 0}`,
32
+ `review_packages=${result.summary.reviewPackages ?? 0}`,
33
+ `monitor_packages=${result.summary.monitorPackages ?? 0}`,
34
+ `cache_backend=${result.summary.cacheBackend ?? ""}`,
35
+ `degraded_sources=${(result.summary.degradedSources ?? []).join(",")}`,
36
+ `ga_ready=${result.summary.gaReady === true ? "1" : "0"}`,
37
+ `dependency_health_score=${result.summary.dependencyHealthScore ?? ""}`,
38
+ `primary_finding_code=${result.summary.primaryFindingCode ?? ""}`,
39
+ `primary_finding_category=${result.summary.primaryFindingCategory ?? ""}`,
40
+ `next_action_reason=${result.summary.nextActionReason ?? ""}`,
29
41
  `fix_pr_applied=${result.summary.fixPrApplied === true ? "1" : "0"}`,
30
42
  `fix_pr_branches_created=${result.summary.fixPrBranchesCreated}`,
31
43
  `fix_pr_branch=${result.summary.fixBranchName ?? ""}`,
@@ -39,6 +39,11 @@ export function createSarifReport(result) {
39
39
  advisoryCount: update.advisoryCount ?? 0,
40
40
  peerConflictSeverity: update.peerConflictSeverity ?? "none",
41
41
  licenseStatus: update.licenseStatus ?? "allowed",
42
+ policyAction: update.policyAction,
43
+ decisionState: update.decisionState,
44
+ releaseNotesSummary: update.releaseNotesSummary?.excerpt,
45
+ engineStatus: update.engineStatus?.state,
46
+ runId: result.summary.runId,
42
47
  },
43
48
  }));
44
49
  const errorResults = [...result.errors].sort((a, b) => a.localeCompare(b)).map((error) => ({
@@ -89,6 +94,17 @@ export function createSarifReport(result) {
89
94
  securityPackages: result.summary.securityPackages ?? 0,
90
95
  peerConflictPackages: result.summary.peerConflictPackages ?? 0,
91
96
  licenseViolationPackages: result.summary.licenseViolationPackages ?? 0,
97
+ runId: result.summary.runId,
98
+ artifactManifest: result.summary.artifactManifest,
99
+ blockedPackages: result.summary.blockedPackages ?? 0,
100
+ reviewPackages: result.summary.reviewPackages ?? 0,
101
+ monitorPackages: result.summary.monitorPackages ?? 0,
102
+ degradedSources: result.summary.degradedSources ?? [],
103
+ cacheBackend: result.summary.cacheBackend,
104
+ dependencyHealthScore: result.summary.dependencyHealthScore,
105
+ primaryFindingCode: result.summary.primaryFindingCode,
106
+ primaryFindingCategory: result.summary.primaryFindingCategory,
107
+ nextActionReason: result.summary.nextActionReason,
92
108
  },
93
109
  },
94
110
  ],
@@ -5,8 +5,13 @@ export type CiProfile = "minimal" | "strict" | "enterprise";
5
5
  export type LockfileMode = "preserve" | "update" | "error";
6
6
  export type Verdict = "safe" | "review" | "blocked" | "actionable";
7
7
  export type RiskLevel = "critical" | "high" | "medium" | "low";
8
+ export type DoctorFindingSeverity = "error" | "warning";
9
+ export type DoctorScoreLabel = "Strong" | "Needs Review" | "Action Needed" | "Blocked / Critical";
10
+ export type DoctorFindingCategory = "Security" | "Compatibility" | "Policy" | "Operational Health" | "Licensing" | "Unused / Cleanup" | "Release Risk" | "Registry / Execution" | "Workspace Integrity";
8
11
  export type RiskCategory = "known-vulnerability" | "behavioral-risk" | "operational-health";
9
12
  export type MaintainerChurnStatus = "unknown" | "stable" | "elevated-change";
13
+ export type PolicyAction = "allow" | "review" | "block" | "monitor";
14
+ export type DecisionState = "safe" | "review" | "blocked" | "actionable";
10
15
  export type OutputFormat = "table" | "json" | "minimal" | "github" | "metrics";
11
16
  export type FailOnLevel = "none" | "patch" | "minor" | "major" | "any";
12
17
  export type LogLevel = "error" | "warn" | "info" | "debug";
@@ -108,6 +113,40 @@ export interface PackageUpdate {
108
113
  peerConflictSeverity?: "none" | PeerConflictSeverity;
109
114
  licenseStatus?: "allowed" | "review" | "denied";
110
115
  healthStatus?: "healthy" | HealthFlag;
116
+ policyAction?: PolicyAction;
117
+ decisionState?: DecisionState;
118
+ releaseNotesSummary?: ReleaseNotesSummary;
119
+ engineStatus?: EngineStatus;
120
+ workspaceGroup?: string;
121
+ groupKey?: string;
122
+ selectedByDefault?: boolean;
123
+ blockedReason?: string;
124
+ monitorReason?: string;
125
+ }
126
+ export interface ReleaseNotesSummary {
127
+ source: "github-release" | "changelog-file" | "none";
128
+ title: string;
129
+ excerpt: string;
130
+ }
131
+ export interface EngineStatus {
132
+ state: "compatible" | "review" | "blocked" | "unknown";
133
+ required?: string;
134
+ current?: string;
135
+ reason?: string;
136
+ }
137
+ export interface ArtifactManifest {
138
+ runId: string;
139
+ createdAt: string;
140
+ command: string;
141
+ projectPath: string;
142
+ ciProfile: CiProfile;
143
+ artifactManifestPath: string;
144
+ outputs: {
145
+ jsonFile?: string;
146
+ githubOutputFile?: string;
147
+ sarifFile?: string;
148
+ prReportFile?: string;
149
+ };
111
150
  }
112
151
  export interface Summary {
113
152
  contractVersion: "2";
@@ -155,6 +194,25 @@ export interface Summary {
155
194
  peerConflictPackages?: number;
156
195
  licenseViolationPackages?: number;
157
196
  privateRegistryPackages?: number;
197
+ runId?: string;
198
+ artifactManifest?: string;
199
+ policyActionCounts?: Record<PolicyAction, number>;
200
+ blockedPackages?: number;
201
+ reviewPackages?: number;
202
+ monitorPackages?: number;
203
+ decisionPackages?: number;
204
+ releaseVolatilityPackages?: number;
205
+ engineConflictPackages?: number;
206
+ degradedSources?: string[];
207
+ cacheBackend?: "sqlite" | "file";
208
+ binaryRecommended?: boolean;
209
+ gaReady?: boolean;
210
+ dependencyHealthScore?: number;
211
+ findingCountsByCategory?: Partial<Record<DoctorFindingCategory, number>>;
212
+ findingCountsBySeverity?: Partial<Record<DoctorFindingSeverity, number>>;
213
+ primaryFindingCode?: string;
214
+ primaryFindingCategory?: DoctorFindingCategory;
215
+ nextActionReason?: string;
158
216
  }
159
217
  export interface CheckResult {
160
218
  projectPath: string;
@@ -208,6 +266,7 @@ export interface AuditOptions {
208
266
  jsonFile?: string;
209
267
  concurrency: number;
210
268
  registryTimeoutMs: number;
269
+ silent?: boolean;
211
270
  }
212
271
  export interface CveAdvisory {
213
272
  cveId: string;
@@ -324,6 +383,7 @@ export interface ResolveOptions {
324
383
  concurrency: number;
325
384
  registryTimeoutMs: number;
326
385
  cacheTtlSeconds: number;
386
+ silent?: boolean;
327
387
  }
328
388
  export interface ResolveResult {
329
389
  conflicts: PeerConflict[];
@@ -367,6 +427,7 @@ export interface ReviewResult {
367
427
  projectPath: string;
368
428
  target: TargetLevel;
369
429
  summary: Summary;
430
+ analysis: AnalysisBundle;
370
431
  items: ReviewItem[];
371
432
  updates: PackageUpdate[];
372
433
  errors: string[];
@@ -377,16 +438,55 @@ export interface ReviewOptions extends CheckOptions {
377
438
  risk?: RiskLevel;
378
439
  diff?: TargetLevel;
379
440
  applySelected: boolean;
441
+ showChangelog?: boolean;
380
442
  }
381
443
  export interface DoctorOptions extends CheckOptions {
382
444
  verdictOnly: boolean;
445
+ includeChangelog?: boolean;
446
+ agentReport?: boolean;
447
+ }
448
+ export interface DoctorFinding {
449
+ id: string;
450
+ code: string;
451
+ category: DoctorFindingCategory;
452
+ severity: DoctorFindingSeverity;
453
+ scope: "project" | "package";
454
+ packageName?: string;
455
+ workspace?: string;
456
+ summary: string;
457
+ details?: string;
458
+ help?: string;
459
+ recommendedAction?: string;
460
+ evidence?: string[];
383
461
  }
384
462
  export interface DoctorResult {
385
463
  verdict: Verdict;
464
+ score: number;
465
+ scoreLabel: DoctorScoreLabel;
386
466
  summary: Summary;
387
467
  review: ReviewResult;
468
+ findings: DoctorFinding[];
388
469
  primaryFindings: string[];
389
470
  recommendedCommand: string;
471
+ nextActionReason: string;
472
+ }
473
+ export interface AnalysisBundle {
474
+ check: CheckResult;
475
+ audit: AuditResult;
476
+ resolve: ResolveResult;
477
+ health: HealthResult;
478
+ licenses: LicenseResult;
479
+ unused: UnusedResult;
480
+ items: ReviewItem[];
481
+ degradedSources: string[];
482
+ }
483
+ export interface DashboardOptions extends CheckOptions {
484
+ view?: "dependencies" | "security" | "health";
485
+ }
486
+ export interface DashboardResult {
487
+ completed: boolean;
488
+ errors: string[];
489
+ warnings: string[];
390
490
  }
391
491
  export type UnusedKind = "declared-not-imported" | "imported-not-declared";
392
492
  export interface UnusedDependency {
@@ -495,3 +595,23 @@ export interface SnapshotResult {
495
595
  errors: string[];
496
596
  warnings: string[];
497
597
  }
598
+ export interface GaOptions {
599
+ cwd: string;
600
+ workspace: boolean;
601
+ jsonFile?: string;
602
+ }
603
+ export interface GaCheck {
604
+ name: "package-manager" | "workspace-discovery" | "lockfile" | "cache-backend" | "dist-build" | "benchmark-gates" | "docs-contract";
605
+ status: "pass" | "warn" | "fail";
606
+ detail: string;
607
+ }
608
+ export interface GaResult {
609
+ ready: boolean;
610
+ projectPath: string;
611
+ packageManager: "npm" | "pnpm" | "unknown";
612
+ workspacePackages: number;
613
+ cacheBackend: "sqlite" | "file";
614
+ checks: GaCheck[];
615
+ warnings: string[];
616
+ errors: string[];
617
+ }