@rainy-updates/cli 0.5.3 → 0.5.6

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 (44) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +8 -0
  3. package/dist/bin/cli.js +38 -2
  4. package/dist/commands/audit/runner.js +43 -26
  5. package/dist/commands/dashboard/parser.d.ts +2 -0
  6. package/dist/commands/dashboard/parser.js +59 -0
  7. package/dist/commands/dashboard/runner.d.ts +2 -0
  8. package/dist/commands/dashboard/runner.js +47 -0
  9. package/dist/commands/doctor/parser.js +6 -0
  10. package/dist/commands/ga/parser.d.ts +2 -0
  11. package/dist/commands/ga/parser.js +50 -0
  12. package/dist/commands/ga/runner.d.ts +2 -0
  13. package/dist/commands/ga/runner.js +129 -0
  14. package/dist/commands/resolve/runner.js +7 -3
  15. package/dist/commands/review/parser.js +6 -0
  16. package/dist/commands/review/runner.js +4 -3
  17. package/dist/core/analysis-bundle.d.ts +4 -0
  18. package/dist/core/analysis-bundle.js +241 -0
  19. package/dist/core/artifacts.d.ts +3 -0
  20. package/dist/core/artifacts.js +48 -0
  21. package/dist/core/check.js +6 -1
  22. package/dist/core/options.d.ts +7 -1
  23. package/dist/core/options.js +14 -0
  24. package/dist/core/review-model.js +51 -177
  25. package/dist/core/summary.js +13 -0
  26. package/dist/output/format.js +15 -0
  27. package/dist/output/github.js +8 -0
  28. package/dist/output/sarif.js +12 -0
  29. package/dist/types/index.d.ts +92 -0
  30. package/dist/ui/dashboard/DashboardTUI.d.ts +6 -0
  31. package/dist/ui/dashboard/DashboardTUI.js +34 -0
  32. package/dist/ui/dashboard/components/DetailPanel.d.ts +4 -0
  33. package/dist/ui/dashboard/components/DetailPanel.js +30 -0
  34. package/dist/ui/dashboard/components/Footer.d.ts +4 -0
  35. package/dist/ui/dashboard/components/Footer.js +9 -0
  36. package/dist/ui/dashboard/components/Header.d.ts +4 -0
  37. package/dist/ui/dashboard/components/Header.js +12 -0
  38. package/dist/ui/dashboard/components/Sidebar.d.ts +4 -0
  39. package/dist/ui/dashboard/components/Sidebar.js +23 -0
  40. package/dist/ui/dashboard/store.d.ts +34 -0
  41. package/dist/ui/dashboard/store.js +148 -0
  42. package/dist/ui/tui.d.ts +2 -2
  43. package/dist/ui/tui.js +310 -79
  44. package/package.json +1 -1
@@ -1,82 +1,51 @@
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";
7
4
  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 Promise.all([
16
- runSilenced(() => import("../commands/audit/runner.js").then((mod) => mod.runAudit(toAuditOptions(options)))),
17
- runSilenced(() => import("../commands/resolve/runner.js").then((mod) => mod.runResolve(toResolveOptions(options)))),
18
- runSilenced(() => import("../commands/health/runner.js").then((mod) => mod.runHealth(toHealthOptions(options)))),
19
- runSilenced(() => import("../commands/licenses/runner.js").then((mod) => mod.runLicenses(toLicenseOptions(options)))),
20
- runSilenced(() => 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));
5
+ const includeChangelog = ("showChangelog" in options && options.showChangelog === true) ||
6
+ ("includeChangelog" in options && options.includeChangelog === true) ||
7
+ options.interactive === true;
8
+ const analysis = await buildAnalysisBundle(options, { includeChangelog });
9
+ const items = analysis.items.filter((item) => matchesReviewFilters(item, options));
10
+ const checkResult = analysis.check;
57
11
  const summary = createReviewSummary(checkResult.summary, items, [
58
12
  ...checkResult.errors,
59
- ...auditResult.errors,
60
- ...resolveResult.errors,
61
- ...healthResult.errors,
62
- ...licenseResult.errors,
63
- ...unusedResult.errors,
13
+ ...analysis.audit.errors,
14
+ ...analysis.resolve.errors,
15
+ ...analysis.health.errors,
16
+ ...analysis.licenses.errors,
17
+ ...analysis.unused.errors,
64
18
  ], [
65
19
  ...checkResult.warnings,
66
- ...auditResult.warnings,
67
- ...resolveResult.warnings,
68
- ...healthResult.warnings,
69
- ...licenseResult.warnings,
70
- ...unusedResult.warnings,
71
- ], options.interactive === true);
20
+ ...analysis.audit.warnings,
21
+ ...analysis.resolve.warnings,
22
+ ...analysis.health.warnings,
23
+ ...analysis.licenses.warnings,
24
+ ...analysis.unused.warnings,
25
+ ], options.interactive === true, analysis.degradedSources);
72
26
  return {
73
27
  projectPath: checkResult.projectPath,
74
28
  target: checkResult.target,
75
29
  summary,
30
+ analysis,
76
31
  items,
77
32
  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],
33
+ errors: [
34
+ ...checkResult.errors,
35
+ ...analysis.audit.errors,
36
+ ...analysis.resolve.errors,
37
+ ...analysis.health.errors,
38
+ ...analysis.licenses.errors,
39
+ ...analysis.unused.errors,
40
+ ],
41
+ warnings: [
42
+ ...checkResult.warnings,
43
+ ...analysis.audit.warnings,
44
+ ...analysis.resolve.warnings,
45
+ ...analysis.health.warnings,
46
+ ...analysis.licenses.warnings,
47
+ ...analysis.unused.warnings,
48
+ ],
80
49
  };
81
50
  }
82
51
  export function createDoctorResult(review) {
@@ -115,6 +84,7 @@ export function renderReviewResult(review) {
115
84
  item.update.licenseStatus && item.update.licenseStatus !== "allowed"
116
85
  ? `license=${item.update.licenseStatus}`
117
86
  : undefined,
87
+ item.update.policyAction ? `policy=${item.update.policyAction}` : undefined,
118
88
  ].filter(Boolean);
119
89
  lines.push(`- ${path.basename(item.update.packagePath)} :: ${item.update.name} ${item.update.fromRange} -> ${item.update.toRange} (${notes.join(", ")})`);
120
90
  if (item.update.riskReasons && item.update.riskReasons.length > 0) {
@@ -123,6 +93,9 @@ export function renderReviewResult(review) {
123
93
  if (item.update.recommendedAction) {
124
94
  lines.push(` action: ${item.update.recommendedAction}`);
125
95
  }
96
+ if (item.update.releaseNotesSummary) {
97
+ lines.push(` notes: ${item.update.releaseNotesSummary.title} - ${item.update.releaseNotesSummary.excerpt}`);
98
+ }
126
99
  if (item.update.homepage) {
127
100
  lines.push(` homepage: ${item.update.homepage}`);
128
101
  }
@@ -157,36 +130,6 @@ export function renderDoctorResult(result, verdictOnly = false) {
157
130
  }
158
131
  return lines.join("\n");
159
132
  }
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
133
  function matchesReviewFilters(item, options) {
191
134
  if ("securityOnly" in options && options.securityOnly && item.advisories.length === 0) {
192
135
  return false;
@@ -208,7 +151,7 @@ function riskMatches(current, threshold) {
208
151
  };
209
152
  return order[current ?? "low"] >= order[threshold];
210
153
  }
211
- function createReviewSummary(base, items, errors, warnings, interactiveSession) {
154
+ function createReviewSummary(base, items, errors, warnings, interactiveSession, degradedSources) {
212
155
  const summary = finalizeSummary(createSummary({
213
156
  scannedPackages: base.scannedPackages,
214
157
  totalDependencies: base.totalDependencies,
@@ -242,6 +185,17 @@ function createReviewSummary(base, items, errors, warnings, interactiveSession)
242
185
  summary.riskPackages = items.filter((item) => item.update.riskLevel === "critical" || item.update.riskLevel === "high").length;
243
186
  summary.peerConflictPackages = items.filter((item) => item.update.peerConflictSeverity && item.update.peerConflictSeverity !== "none").length;
244
187
  summary.licenseViolationPackages = items.filter((item) => item.update.licenseStatus === "denied").length;
188
+ summary.policyActionCounts = {
189
+ allow: items.filter((item) => item.update.policyAction === "allow").length,
190
+ review: items.filter((item) => item.update.policyAction === "review").length,
191
+ block: items.filter((item) => item.update.policyAction === "block").length,
192
+ monitor: items.filter((item) => item.update.policyAction === "monitor").length,
193
+ };
194
+ summary.blockedPackages = items.filter((item) => item.update.decisionState === "blocked").length;
195
+ summary.reviewPackages = items.filter((item) => item.update.decisionState === "review").length;
196
+ summary.monitorPackages = items.filter((item) => item.update.policyAction === "monitor").length;
197
+ summary.decisionPackages = items.length;
198
+ summary.degradedSources = degradedSources;
245
199
  summary.verdict = deriveVerdict(items, errors);
246
200
  return summary;
247
201
  }
@@ -290,83 +244,3 @@ function recommendCommand(review, verdict) {
290
244
  return "rup review --interactive";
291
245
  return "rup check";
292
246
  }
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,
371
- };
372
- }
@@ -50,6 +50,19 @@ 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,
53
66
  };
54
67
  }
55
68
  export function finalizeSummary(summary) {
@@ -62,6 +62,14 @@ 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"}`,
65
73
  ].join("\n");
66
74
  }
67
75
  const lines = [];
@@ -89,12 +97,16 @@ export function renderResult(result, format, display = {}) {
89
97
  display.showHomepage && update.homepage ? update.homepage : undefined,
90
98
  update.riskLevel ? `risk=${update.riskLevel}` : undefined,
91
99
  typeof update.riskScore === "number" ? `score=${update.riskScore}` : undefined,
100
+ update.policyAction ? `policy=${update.policyAction}` : undefined,
92
101
  ]
93
102
  .filter(Boolean)
94
103
  .join(", ")})`);
95
104
  if (update.recommendedAction) {
96
105
  lines.push(` action: ${update.recommendedAction}`);
97
106
  }
107
+ if (update.releaseNotesSummary) {
108
+ lines.push(` notes: ${update.releaseNotesSummary.title} — ${update.releaseNotesSummary.excerpt}`);
109
+ }
98
110
  }
99
111
  }
100
112
  if (result.errors.length > 0) {
@@ -118,6 +130,9 @@ export function renderResult(result, format, display = {}) {
118
130
  if (result.summary.verdict) {
119
131
  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
132
  }
133
+ if (result.summary.runId) {
134
+ 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}`);
135
+ }
121
136
  lines.push(`Contract v${result.summary.contractVersion}, failReason=${result.summary.failReason}, duration=${result.summary.durationMs.total}ms`);
122
137
  if (result.summary.fixPrApplied) {
123
138
  lines.push(`Fix PR: applied on branch ${result.summary.fixBranchName ?? "unknown"} (${result.summary.fixCommitSha ?? "no-commit"})`);
@@ -26,6 +26,14 @@ 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"}`,
29
37
  `fix_pr_applied=${result.summary.fixPrApplied === true ? "1" : "0"}`,
30
38
  `fix_pr_branches_created=${result.summary.fixPrBranchesCreated}`,
31
39
  `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,13 @@ 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,
92
104
  },
93
105
  },
94
106
  ],
@@ -7,6 +7,8 @@ export type Verdict = "safe" | "review" | "blocked" | "actionable";
7
7
  export type RiskLevel = "critical" | "high" | "medium" | "low";
8
8
  export type RiskCategory = "known-vulnerability" | "behavioral-risk" | "operational-health";
9
9
  export type MaintainerChurnStatus = "unknown" | "stable" | "elevated-change";
10
+ export type PolicyAction = "allow" | "review" | "block" | "monitor";
11
+ export type DecisionState = "safe" | "review" | "blocked" | "actionable";
10
12
  export type OutputFormat = "table" | "json" | "minimal" | "github" | "metrics";
11
13
  export type FailOnLevel = "none" | "patch" | "minor" | "major" | "any";
12
14
  export type LogLevel = "error" | "warn" | "info" | "debug";
@@ -108,6 +110,40 @@ export interface PackageUpdate {
108
110
  peerConflictSeverity?: "none" | PeerConflictSeverity;
109
111
  licenseStatus?: "allowed" | "review" | "denied";
110
112
  healthStatus?: "healthy" | HealthFlag;
113
+ policyAction?: PolicyAction;
114
+ decisionState?: DecisionState;
115
+ releaseNotesSummary?: ReleaseNotesSummary;
116
+ engineStatus?: EngineStatus;
117
+ workspaceGroup?: string;
118
+ groupKey?: string;
119
+ selectedByDefault?: boolean;
120
+ blockedReason?: string;
121
+ monitorReason?: string;
122
+ }
123
+ export interface ReleaseNotesSummary {
124
+ source: "github-release" | "changelog-file" | "none";
125
+ title: string;
126
+ excerpt: string;
127
+ }
128
+ export interface EngineStatus {
129
+ state: "compatible" | "review" | "blocked" | "unknown";
130
+ required?: string;
131
+ current?: string;
132
+ reason?: string;
133
+ }
134
+ export interface ArtifactManifest {
135
+ runId: string;
136
+ createdAt: string;
137
+ command: string;
138
+ projectPath: string;
139
+ ciProfile: CiProfile;
140
+ artifactManifestPath: string;
141
+ outputs: {
142
+ jsonFile?: string;
143
+ githubOutputFile?: string;
144
+ sarifFile?: string;
145
+ prReportFile?: string;
146
+ };
111
147
  }
112
148
  export interface Summary {
113
149
  contractVersion: "2";
@@ -155,6 +191,19 @@ export interface Summary {
155
191
  peerConflictPackages?: number;
156
192
  licenseViolationPackages?: number;
157
193
  privateRegistryPackages?: number;
194
+ runId?: string;
195
+ artifactManifest?: string;
196
+ policyActionCounts?: Record<PolicyAction, number>;
197
+ blockedPackages?: number;
198
+ reviewPackages?: number;
199
+ monitorPackages?: number;
200
+ decisionPackages?: number;
201
+ releaseVolatilityPackages?: number;
202
+ engineConflictPackages?: number;
203
+ degradedSources?: string[];
204
+ cacheBackend?: "sqlite" | "file";
205
+ binaryRecommended?: boolean;
206
+ gaReady?: boolean;
158
207
  }
159
208
  export interface CheckResult {
160
209
  projectPath: string;
@@ -208,6 +257,7 @@ export interface AuditOptions {
208
257
  jsonFile?: string;
209
258
  concurrency: number;
210
259
  registryTimeoutMs: number;
260
+ silent?: boolean;
211
261
  }
212
262
  export interface CveAdvisory {
213
263
  cveId: string;
@@ -324,6 +374,7 @@ export interface ResolveOptions {
324
374
  concurrency: number;
325
375
  registryTimeoutMs: number;
326
376
  cacheTtlSeconds: number;
377
+ silent?: boolean;
327
378
  }
328
379
  export interface ResolveResult {
329
380
  conflicts: PeerConflict[];
@@ -367,6 +418,7 @@ export interface ReviewResult {
367
418
  projectPath: string;
368
419
  target: TargetLevel;
369
420
  summary: Summary;
421
+ analysis: AnalysisBundle;
370
422
  items: ReviewItem[];
371
423
  updates: PackageUpdate[];
372
424
  errors: string[];
@@ -377,9 +429,11 @@ export interface ReviewOptions extends CheckOptions {
377
429
  risk?: RiskLevel;
378
430
  diff?: TargetLevel;
379
431
  applySelected: boolean;
432
+ showChangelog?: boolean;
380
433
  }
381
434
  export interface DoctorOptions extends CheckOptions {
382
435
  verdictOnly: boolean;
436
+ includeChangelog?: boolean;
383
437
  }
384
438
  export interface DoctorResult {
385
439
  verdict: Verdict;
@@ -388,6 +442,24 @@ export interface DoctorResult {
388
442
  primaryFindings: string[];
389
443
  recommendedCommand: string;
390
444
  }
445
+ export interface AnalysisBundle {
446
+ check: CheckResult;
447
+ audit: AuditResult;
448
+ resolve: ResolveResult;
449
+ health: HealthResult;
450
+ licenses: LicenseResult;
451
+ unused: UnusedResult;
452
+ items: ReviewItem[];
453
+ degradedSources: string[];
454
+ }
455
+ export interface DashboardOptions extends CheckOptions {
456
+ view?: "dependencies" | "security" | "health";
457
+ }
458
+ export interface DashboardResult {
459
+ completed: boolean;
460
+ errors: string[];
461
+ warnings: string[];
462
+ }
391
463
  export type UnusedKind = "declared-not-imported" | "imported-not-declared";
392
464
  export interface UnusedDependency {
393
465
  name: string;
@@ -495,3 +567,23 @@ export interface SnapshotResult {
495
567
  errors: string[];
496
568
  warnings: string[];
497
569
  }
570
+ export interface GaOptions {
571
+ cwd: string;
572
+ workspace: boolean;
573
+ jsonFile?: string;
574
+ }
575
+ export interface GaCheck {
576
+ name: "package-manager" | "workspace-discovery" | "lockfile" | "cache-backend" | "dist-build" | "benchmark-gates" | "docs-contract";
577
+ status: "pass" | "warn" | "fail";
578
+ detail: string;
579
+ }
580
+ export interface GaResult {
581
+ ready: boolean;
582
+ projectPath: string;
583
+ packageManager: "npm" | "pnpm" | "unknown";
584
+ workspacePackages: number;
585
+ cacheBackend: "sqlite" | "file";
586
+ checks: GaCheck[];
587
+ warnings: string[];
588
+ errors: string[];
589
+ }
@@ -0,0 +1,6 @@
1
+ import type { DashboardOptions, CheckResult } from "../../types/index.js";
2
+ export interface DashboardTUIProps {
3
+ options: DashboardOptions;
4
+ initialResult: CheckResult;
5
+ }
6
+ export declare function DashboardTUI({ options, initialResult }: DashboardTUIProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, useInput, useApp } from "ink";
3
+ import { initStore, dashboardActions } from "./store.js";
4
+ import { Header } from "./components/Header.js";
5
+ import { Sidebar } from "./components/Sidebar.js";
6
+ import { DetailPanel } from "./components/DetailPanel.js";
7
+ import { Footer } from "./components/Footer.js";
8
+ export function DashboardTUI({ options, initialResult }) {
9
+ const { exit } = useApp();
10
+ // Initialize the singleton store synchronously before rendering children
11
+ // so that components can access it on the first render pass natively.
12
+ initStore(options, initialResult);
13
+ // Handle global keyboard input (doesn't trigger React re-renders unless store state affects this component)
14
+ // Our static layout theoretically will not re-render off this hook alone.
15
+ useInput((input, key) => {
16
+ if (key.upArrow) {
17
+ dashboardActions.moveCursorUp();
18
+ }
19
+ if (key.downArrow) {
20
+ dashboardActions.moveCursorDown();
21
+ }
22
+ if (key.return) {
23
+ dashboardActions.setShouldApply(true);
24
+ exit();
25
+ }
26
+ if (input === "r") {
27
+ void dashboardActions.runResolveAction();
28
+ }
29
+ if (input === "a") {
30
+ void dashboardActions.runAuditAction();
31
+ }
32
+ });
33
+ return (_jsxs(Box, { flexDirection: "column", minHeight: 25, children: [_jsx(Header, {}), _jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsx(Sidebar, {}), _jsx(DetailPanel, {})] }), _jsx(Footer, {})] }));
34
+ }
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare function DetailPanelComponent(): import("react/jsx-runtime").JSX.Element;
3
+ export declare const DetailPanel: React.MemoExoticComponent<typeof DetailPanelComponent>;
4
+ export {};
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Box, Text } from "ink";
4
+ import { useDashboardStore } from "../store.js";
5
+ function getRiskColor(risk) {
6
+ switch (risk) {
7
+ case "critical":
8
+ return "red";
9
+ case "high":
10
+ return "red";
11
+ case "medium":
12
+ return "yellow";
13
+ case "low":
14
+ return "blue";
15
+ default:
16
+ return "gray";
17
+ }
18
+ }
19
+ function DetailPanelComponent() {
20
+ const selectedIndex = useDashboardStore((s) => s.selectedIndex);
21
+ const updates = useDashboardStore((s) => s.updates);
22
+ // Fallback if list is empty
23
+ const update = updates[selectedIndex];
24
+ if (!update) {
25
+ return (_jsx(Box, { width: "50%", height: 22, padding: 1, borderStyle: "single", borderColor: "gray", children: _jsx(Text, { dimColor: true, children: "No package selected." }) }));
26
+ }
27
+ const { name, publishedAt, publishAgeDays, riskLevel, homepage, advisoryCount, peerConflictSeverity, } = update;
28
+ return (_jsxs(Box, { width: "50%", height: 22, flexDirection: "column", paddingX: 2, borderStyle: "single", borderColor: "gray", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: name }) }), _jsxs(Box, { marginBottom: 2, flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Risk: " }), _jsx(Text, { color: getRiskColor(riskLevel), children: riskLevel || "unknown" })] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Advisories: " }), _jsx(Text, { color: advisoryCount ? "red" : "green", children: advisoryCount || 0 })] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Peer Conflicts: " }), _jsx(Text, { color: peerConflictSeverity ? "red" : "green", children: peerConflictSeverity || "none" })] })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: "gray", children: homepage || "No homepage provided" }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Published ", publishAgeDays, " days ago", " ", publishedAt ? `(${publishedAt})` : ""] }) })] }));
29
+ }
30
+ export const DetailPanel = React.memo(DetailPanelComponent);
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare function FooterComponent(): import("react/jsx-runtime").JSX.Element;
3
+ export declare const Footer: React.MemoExoticComponent<typeof FooterComponent>;
4
+ export {};
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Box, Text } from "ink";
4
+ import { useDashboardStore } from "../store.js";
5
+ function FooterComponent() {
6
+ const modal = useDashboardStore((s) => s.modal);
7
+ return (_jsxs(Box, { width: "100%", paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: [_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [_jsx(Text, { bold: true, color: "white", children: "\u2191\u2193" }), " ", "Navigate |", _jsxs(Text, { bold: true, color: "white", children: [" ", "Enter"] }), " ", "Apply |", _jsxs(Text, { bold: true, color: "white", children: [" ", "r"] }), "esolve |", _jsxs(Text, { bold: true, color: "white", children: [" ", "a"] }), "udit |", _jsxs(Text, { bold: true, color: "white", children: [" ", "i"] }), "gnore"] }) }), _jsx(Box, { children: _jsx(Text, { color: "yellow", children: modal !== "none" ? `Status: ${modal}...` : "Status: Idle" }) })] }));
8
+ }
9
+ export const Footer = React.memo(FooterComponent);
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare function HeaderComponent(): import("react/jsx-runtime").JSX.Element;
3
+ export declare const Header: React.MemoExoticComponent<typeof HeaderComponent>;
4
+ export {};
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Box, Text } from "ink";
4
+ import { useDashboardStore } from "../store.js";
5
+ function HeaderComponent() {
6
+ const scanned = useDashboardStore((s) => s.summary.scannedPackages);
7
+ const totalUpdates = useDashboardStore((s) => s.updates.length);
8
+ const view = useDashboardStore((s) => s.view);
9
+ return (_jsxs(Box, { width: "100%", paddingX: 1, borderStyle: "single", borderColor: "blue", flexDirection: "row", justifyContent: "space-between", children: [_jsx(Box, { children: _jsx(Text, { color: "cyan", bold: true, children: "\uD83C\uDF27\uFE0F Rainy Updates Dashboard" }) }), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: view === "dependencies" ? "green" : "gray", children: "[Dependencies]" }), " ", _jsx(Text, { color: view === "security" ? "green" : "gray", children: "[Security]" }), " ", _jsx(Text, { color: view === "health" ? "green" : "gray", children: "[Health]" })] }) }), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["Packages: ", scanned, " | Found: ", totalUpdates] }) })] }));
10
+ }
11
+ // Memoize to ensure Header only renders when deeply affected (which is rare)
12
+ export const Header = React.memo(HeaderComponent);
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare function SidebarComponent(): import("react/jsx-runtime").JSX.Element;
3
+ export declare const Sidebar: React.MemoExoticComponent<typeof SidebarComponent>;
4
+ export {};