@rainy-updates/cli 0.5.6 → 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.
@@ -0,0 +1,150 @@
1
+ import process from "node:process";
2
+ import { check } from "../core/check.js";
3
+ import { upgrade } from "../core/upgrade.js";
4
+ import { warmCache } from "../core/warm-cache.js";
5
+ import { runCi } from "../core/ci.js";
6
+ import { initCiWorkflow } from "../core/init-ci.js";
7
+ import { diffBaseline, saveBaseline } from "../core/baseline.js";
8
+ export async function handleDirectCommand(parsed) {
9
+ if (parsed.command === "init-ci") {
10
+ const workflow = await initCiWorkflow(parsed.options.cwd, parsed.options.force, {
11
+ mode: parsed.options.mode,
12
+ schedule: parsed.options.schedule,
13
+ });
14
+ process.stdout.write(workflow.created
15
+ ? `Created CI workflow at ${workflow.path}\n`
16
+ : `CI workflow already exists at ${workflow.path}. Use --force to overwrite.\n`);
17
+ return true;
18
+ }
19
+ if (parsed.command === "baseline") {
20
+ if (parsed.options.action === "save") {
21
+ const saved = await saveBaseline(parsed.options);
22
+ process.stdout.write(`Saved baseline at ${saved.filePath} (${saved.entries} entries)\n`);
23
+ return true;
24
+ }
25
+ const diff = await diffBaseline(parsed.options);
26
+ const changes = diff.added.length + diff.removed.length + diff.changed.length;
27
+ if (changes === 0) {
28
+ process.stdout.write(`No baseline drift detected (${diff.filePath}).\n`);
29
+ return true;
30
+ }
31
+ process.stdout.write(`Baseline drift detected (${diff.filePath}).\n`);
32
+ if (diff.added.length > 0)
33
+ process.stdout.write(`Added: ${diff.added.length}\n`);
34
+ if (diff.removed.length > 0)
35
+ process.stdout.write(`Removed: ${diff.removed.length}\n`);
36
+ if (diff.changed.length > 0)
37
+ process.stdout.write(`Changed: ${diff.changed.length}\n`);
38
+ process.exitCode = 1;
39
+ return true;
40
+ }
41
+ if (parsed.command === "bisect") {
42
+ const { runBisect } = await import("../commands/bisect/runner.js");
43
+ const result = await runBisect(parsed.options);
44
+ process.exitCode = result.breakingVersion ? 1 : 0;
45
+ return true;
46
+ }
47
+ if (parsed.command === "audit") {
48
+ const { runAudit } = await import("../commands/audit/runner.js");
49
+ const result = await runAudit(parsed.options);
50
+ process.exitCode = result.advisories.length > 0 ? 1 : 0;
51
+ return true;
52
+ }
53
+ if (parsed.command === "health") {
54
+ const { runHealth } = await import("../commands/health/runner.js");
55
+ const result = await runHealth(parsed.options);
56
+ process.exitCode = result.totalFlagged > 0 ? 1 : 0;
57
+ return true;
58
+ }
59
+ if (parsed.command === "unused") {
60
+ const { runUnused } = await import("../commands/unused/runner.js");
61
+ const result = await runUnused(parsed.options);
62
+ process.exitCode = result.totalUnused > 0 || result.totalMissing > 0 ? 1 : 0;
63
+ return true;
64
+ }
65
+ if (parsed.command === "resolve") {
66
+ const { runResolve } = await import("../commands/resolve/runner.js");
67
+ const result = await runResolve(parsed.options);
68
+ process.exitCode = result.errorConflicts > 0 ? 1 : 0;
69
+ return true;
70
+ }
71
+ if (parsed.command === "licenses") {
72
+ const { runLicenses } = await import("../commands/licenses/runner.js");
73
+ const result = await runLicenses(parsed.options);
74
+ process.exitCode = result.totalViolations > 0 ? 1 : 0;
75
+ return true;
76
+ }
77
+ if (parsed.command === "snapshot") {
78
+ const { runSnapshot } = await import("../commands/snapshot/runner.js");
79
+ const result = await runSnapshot(parsed.options);
80
+ process.exitCode = result.errors.length > 0 ? 1 : 0;
81
+ return true;
82
+ }
83
+ if (parsed.command === "review") {
84
+ const { runReview } = await import("../commands/review/runner.js");
85
+ const result = await runReview(parsed.options);
86
+ process.exitCode =
87
+ result.summary.verdict === "blocked" ||
88
+ result.summary.verdict === "actionable" ||
89
+ result.summary.verdict === "review"
90
+ ? 1
91
+ : 0;
92
+ return true;
93
+ }
94
+ if (parsed.command === "doctor") {
95
+ const { runDoctor } = await import("../commands/doctor/runner.js");
96
+ const result = await runDoctor(parsed.options);
97
+ process.exitCode = result.verdict === "safe" ? 0 : 1;
98
+ return true;
99
+ }
100
+ if (parsed.command === "dashboard") {
101
+ const { runDashboard } = await import("../commands/dashboard/runner.js");
102
+ const result = await runDashboard(parsed.options);
103
+ process.exitCode = result.errors.length > 0 ? 1 : 0;
104
+ return true;
105
+ }
106
+ if (parsed.command === "ga") {
107
+ const { runGa } = await import("../commands/ga/runner.js");
108
+ const result = await runGa(parsed.options);
109
+ process.exitCode = result.ready ? 0 : 1;
110
+ return true;
111
+ }
112
+ if (parsed.options.interactive &&
113
+ (parsed.command === "check" ||
114
+ parsed.command === "upgrade" ||
115
+ parsed.command === "ci")) {
116
+ const { runReview } = await import("../commands/review/runner.js");
117
+ const result = await runReview({
118
+ ...parsed.options,
119
+ securityOnly: false,
120
+ risk: undefined,
121
+ diff: undefined,
122
+ applySelected: parsed.command === "upgrade",
123
+ });
124
+ process.exitCode =
125
+ result.summary.verdict === "safe" && result.updates.length === 0 ? 0 : 1;
126
+ return true;
127
+ }
128
+ return false;
129
+ }
130
+ export async function runPrimaryCommand(parsed) {
131
+ if (parsed.command === "upgrade") {
132
+ return upgrade(parsed.options);
133
+ }
134
+ if (parsed.command === "warm-cache") {
135
+ return warmCache(parsed.options);
136
+ }
137
+ if (parsed.command === "ci") {
138
+ return runCi(parsed.options);
139
+ }
140
+ if (parsed.options.fixPr) {
141
+ const upgradeOptions = {
142
+ ...parsed.options,
143
+ install: false,
144
+ packageManager: "auto",
145
+ sync: false,
146
+ };
147
+ return upgrade(upgradeOptions);
148
+ }
149
+ return check(parsed.options);
150
+ }
@@ -0,0 +1 @@
1
+ export declare function renderHelp(command?: string): string;
@@ -0,0 +1,284 @@
1
+ export function renderHelp(command) {
2
+ const isCommand = command && !command.startsWith("-");
3
+ if (isCommand && command === "check") {
4
+ return `rainy-updates check [options]
5
+
6
+ Detect candidate dependency updates. This is the first step in the flow:
7
+ check detects
8
+ doctor summarizes
9
+ review decides
10
+ upgrade applies
11
+
12
+ Options:
13
+ --workspace
14
+ --target patch|minor|major|latest
15
+ --filter <pattern>
16
+ --reject <pattern>
17
+ --dep-kinds deps,dev,optional,peer
18
+ --concurrency <n>
19
+ --registry-timeout-ms <n>
20
+ --registry-retries <n>
21
+ --cache-ttl <seconds>
22
+ --stream
23
+ --policy-file <path>
24
+ --offline
25
+ --fix-pr
26
+ --fix-branch <name>
27
+ --fix-commit-message <text>
28
+ --fix-dry-run
29
+ --fix-pr-no-checkout
30
+ --fix-pr-batch-size <n>
31
+ --no-pr-report
32
+ --json-file <path>
33
+ --github-output <path>
34
+ --sarif-file <path>
35
+ --pr-report-file <path>
36
+ --fail-on none|patch|minor|major|any
37
+ --max-updates <n>
38
+ --group-by none|name|scope|kind|risk
39
+ --group-max <n>
40
+ --cooldown-days <n>
41
+ --pr-limit <n>
42
+ --only-changed
43
+ --interactive
44
+ --show-impact
45
+ --show-links
46
+ --show-homepage
47
+ --lockfile-mode preserve|update|error
48
+ --log-level error|warn|info|debug
49
+ --ci`;
50
+ }
51
+ if (isCommand && command === "warm-cache") {
52
+ return `rainy-updates warm-cache [options]
53
+
54
+ Pre-warm local metadata cache for faster CI checks.
55
+
56
+ Options:
57
+ --workspace
58
+ --target patch|minor|major|latest
59
+ --filter <pattern>
60
+ --reject <pattern>
61
+ --dep-kinds deps,dev,optional,peer
62
+ --concurrency <n>
63
+ --registry-timeout-ms <n>
64
+ --registry-retries <n>
65
+ --cache-ttl <seconds>
66
+ --offline
67
+ --stream
68
+ --json-file <path>
69
+ --github-output <path>
70
+ --sarif-file <path>
71
+ --pr-report-file <path>`;
72
+ }
73
+ if (isCommand && command === "upgrade") {
74
+ return `rainy-updates upgrade [options]
75
+
76
+ Apply an approved change set to package.json manifests.
77
+
78
+ Options:
79
+ --workspace
80
+ --sync
81
+ --install
82
+ --pm auto|npm|pnpm
83
+ --target patch|minor|major|latest
84
+ --policy-file <path>
85
+ --concurrency <n>
86
+ --registry-timeout-ms <n>
87
+ --registry-retries <n>
88
+ --fix-pr
89
+ --fix-branch <name>
90
+ --fix-commit-message <text>
91
+ --fix-dry-run
92
+ --fix-pr-no-checkout
93
+ --fix-pr-batch-size <n>
94
+ --interactive
95
+ --lockfile-mode preserve|update|error
96
+ --no-pr-report
97
+ --json-file <path>
98
+ --pr-report-file <path>`;
99
+ }
100
+ if (isCommand && command === "ci") {
101
+ return `rainy-updates ci [options]
102
+
103
+ Run CI-oriented automation around the same lifecycle:
104
+ check detects
105
+ doctor summarizes
106
+ review decides
107
+ upgrade applies
108
+
109
+ Options:
110
+ --workspace
111
+ --mode minimal|strict|enterprise
112
+ --group-by none|name|scope|kind|risk
113
+ --group-max <n>
114
+ --cooldown-days <n>
115
+ --pr-limit <n>
116
+ --only-changed
117
+ --offline
118
+ --concurrency <n>
119
+ --registry-timeout-ms <n>
120
+ --registry-retries <n>
121
+ --stream
122
+ --fix-pr
123
+ --fix-branch <name>
124
+ --fix-commit-message <text>
125
+ --fix-dry-run
126
+ --fix-pr-no-checkout
127
+ --fix-pr-batch-size <n>
128
+ --no-pr-report
129
+ --json-file <path>
130
+ --github-output <path>
131
+ --sarif-file <path>
132
+ --pr-report-file <path>
133
+ --fail-on none|patch|minor|major|any
134
+ --max-updates <n>
135
+ --lockfile-mode preserve|update|error
136
+ --log-level error|warn|info|debug
137
+ --ci`;
138
+ }
139
+ if (isCommand && command === "init-ci") {
140
+ return `rainy-updates init-ci [options]
141
+
142
+ Create a GitHub Actions workflow template at:
143
+ .github/workflows/rainy-updates.yml
144
+
145
+ Options:
146
+ --force
147
+ --mode minimal|strict|enterprise
148
+ --schedule weekly|daily|off`;
149
+ }
150
+ if (isCommand && command === "baseline") {
151
+ return `rainy-updates baseline [options]
152
+
153
+ Save or compare dependency baseline snapshots.
154
+
155
+ Options:
156
+ --save
157
+ --check
158
+ --file <path>
159
+ --workspace
160
+ --dep-kinds deps,dev,optional,peer
161
+ --ci`;
162
+ }
163
+ if (isCommand && command === "audit") {
164
+ return `rainy-updates audit [options]
165
+
166
+ Scan dependencies for CVEs using OSV.dev and GitHub Advisory Database.
167
+
168
+ Options:
169
+ --workspace
170
+ --severity critical|high|medium|low
171
+ --summary
172
+ --report table|summary|json
173
+ --source auto|osv|github|all
174
+ --fix
175
+ --dry-run
176
+ --commit
177
+ --pm auto|npm|pnpm|bun|yarn
178
+ --json-file <path>
179
+ --concurrency <n>
180
+ --registry-timeout-ms <n>`;
181
+ }
182
+ if (isCommand && command === "review") {
183
+ return `rainy-updates review [options]
184
+
185
+ Review is the decision center of Rainy Updates.
186
+ Use it to inspect risk, security, peer, license, and policy context before applying changes.
187
+
188
+ Options:
189
+ --workspace
190
+ --interactive
191
+ --security-only
192
+ --risk critical|high|medium|low
193
+ --diff patch|minor|major|latest
194
+ --apply-selected
195
+ --show-changelog
196
+ --policy-file <path>
197
+ --json-file <path>
198
+ --concurrency <n>
199
+ --registry-timeout-ms <n>
200
+ --registry-retries <n>`;
201
+ }
202
+ if (isCommand && command === "doctor") {
203
+ return `rainy-updates doctor [options]
204
+
205
+ Produce a fast summary verdict and point the operator to review when action is needed.
206
+
207
+ Options:
208
+ --workspace
209
+ --verdict-only
210
+ --include-changelog
211
+ --json-file <path>`;
212
+ }
213
+ if (isCommand && command === "ga") {
214
+ return `rainy-updates ga [options]
215
+
216
+ Audit release and CI readiness for Rainy Updates.
217
+
218
+ Options:
219
+ --workspace
220
+ --json-file <path>
221
+ --cwd <path>`;
222
+ }
223
+ return `rainy-updates (rup / rainy-up) <command> [options]
224
+
225
+ Commands:
226
+ check Detect candidate updates
227
+ doctor Summarize what matters
228
+ review Decide what to do
229
+ upgrade Apply the approved change set
230
+ dashboard Open the interactive DevOps dashboard (Ink TUI)
231
+ ci Run CI-focused orchestration
232
+ warm-cache Warm local cache for fast/offline checks
233
+ init-ci Scaffold GitHub Actions workflow
234
+ baseline Save/check dependency baseline snapshots
235
+ audit Scan dependencies for CVEs (OSV.dev + GitHub)
236
+ health Detect stale/deprecated/unmaintained packages
237
+ bisect Find which version of a dep introduced a failure
238
+ unused Detect unused or missing npm dependencies
239
+ resolve Check peer dependency conflicts (pure-TS, no subprocess)
240
+ licenses Scan dependency licenses and generate SPDX SBOM
241
+ snapshot Save, list, restore, and diff dependency state snapshots
242
+ ga Audit GA and CI readiness for this checkout
243
+
244
+ Global options:
245
+ --cwd <path>
246
+ --workspace
247
+ --target patch|minor|major|latest
248
+ --format table|json|minimal|github|metrics
249
+ --json-file <path>
250
+ --github-output <path>
251
+ --sarif-file <path>
252
+ --pr-report-file <path>
253
+ --policy-file <path>
254
+ --fail-on none|patch|minor|major|any
255
+ --max-updates <n>
256
+ --group-by none|name|scope|kind|risk
257
+ --group-max <n>
258
+ --cooldown-days <n>
259
+ --pr-limit <n>
260
+ --only-changed
261
+ --interactive
262
+ --show-impact
263
+ --show-links
264
+ --show-homepage
265
+ --mode minimal|strict|enterprise
266
+ --fix-pr
267
+ --fix-branch <name>
268
+ --fix-commit-message <text>
269
+ --fix-dry-run
270
+ --fix-pr-no-checkout
271
+ --fix-pr-batch-size <n>
272
+ --no-pr-report
273
+ --log-level error|warn|info|debug
274
+ --concurrency <n>
275
+ --registry-timeout-ms <n>
276
+ --registry-retries <n>
277
+ --cache-ttl <seconds>
278
+ --offline
279
+ --stream
280
+ --lockfile-mode preserve|update|error
281
+ --ci
282
+ --help, -h
283
+ --version, -v`;
284
+ }
@@ -43,6 +43,7 @@ export function parseDoctorArgs(args) {
43
43
  showHomepage: true,
44
44
  verdictOnly: false,
45
45
  includeChangelog: false,
46
+ agentReport: false,
46
47
  };
47
48
  for (let i = 0; i < args.length; i += 1) {
48
49
  const current = args[i];
@@ -66,6 +67,10 @@ export function parseDoctorArgs(args) {
66
67
  options.includeChangelog = true;
67
68
  continue;
68
69
  }
70
+ if (current === "--agent-report") {
71
+ options.agentReport = true;
72
+ continue;
73
+ }
69
74
  if (current === "--json-file" && next) {
70
75
  options.jsonFile = path.resolve(options.cwd, next);
71
76
  i += 1;
@@ -92,6 +97,7 @@ Usage:
92
97
  Options:
93
98
  --verdict-only Print the 3-line quick verdict without counts
94
99
  --include-changelog Include release note summaries in the aggregated review data
100
+ --agent-report Print a prompt-ready remediation report for coding agents
95
101
  --workspace Scan all workspace packages
96
102
  --json-file <path> Write JSON doctor report to file
97
103
  --cwd <path>
@@ -1,11 +1,14 @@
1
1
  import process from "node:process";
2
- import { buildReviewResult, createDoctorResult, renderDoctorResult } from "../../core/review-model.js";
2
+ import { buildReviewResult, createDoctorResult, renderDoctorAgentReport, renderDoctorResult, } from "../../core/review-model.js";
3
3
  import { stableStringify } from "../../utils/stable-json.js";
4
4
  import { writeFileAtomic } from "../../utils/io.js";
5
5
  export async function runDoctor(options) {
6
6
  const review = await buildReviewResult(options);
7
7
  const doctor = createDoctorResult(review);
8
- process.stdout.write(renderDoctorResult(doctor, options.verdictOnly) + "\n");
8
+ const output = options.agentReport
9
+ ? renderDoctorAgentReport(doctor)
10
+ : renderDoctorResult(doctor, options.verdictOnly);
11
+ process.stdout.write(output + "\n");
9
12
  if (options.jsonFile) {
10
13
  await writeFileAtomic(options.jsonFile, stableStringify(doctor, 2) + "\n");
11
14
  }
@@ -0,0 +1,6 @@
1
+ import type { AuditOptions, CheckOptions, HealthOptions, LicenseOptions, ResolveOptions, UnusedOptions } from "../../types/index.js";
2
+ export declare function toAuditOptions(options: CheckOptions): AuditOptions;
3
+ export declare function toResolveOptions(options: CheckOptions): ResolveOptions;
4
+ export declare function toHealthOptions(options: CheckOptions): HealthOptions;
5
+ export declare function toLicenseOptions(options: CheckOptions): LicenseOptions;
6
+ export declare function toUnusedOptions(options: CheckOptions): UnusedOptions;
@@ -0,0 +1,69 @@
1
+ export function toAuditOptions(options) {
2
+ return {
3
+ cwd: options.cwd,
4
+ workspace: options.workspace,
5
+ severity: undefined,
6
+ fix: false,
7
+ dryRun: true,
8
+ commit: false,
9
+ packageManager: "auto",
10
+ reportFormat: "json",
11
+ sourceMode: "auto",
12
+ jsonFile: undefined,
13
+ concurrency: options.concurrency,
14
+ registryTimeoutMs: options.registryTimeoutMs,
15
+ silent: true,
16
+ };
17
+ }
18
+ export function toResolveOptions(options) {
19
+ return {
20
+ cwd: options.cwd,
21
+ workspace: options.workspace,
22
+ afterUpdate: true,
23
+ safe: false,
24
+ jsonFile: undefined,
25
+ concurrency: options.concurrency,
26
+ registryTimeoutMs: options.registryTimeoutMs,
27
+ cacheTtlSeconds: options.cacheTtlSeconds,
28
+ silent: true,
29
+ };
30
+ }
31
+ export function toHealthOptions(options) {
32
+ return {
33
+ cwd: options.cwd,
34
+ workspace: options.workspace,
35
+ staleDays: 365,
36
+ includeDeprecated: true,
37
+ includeAlternatives: false,
38
+ reportFormat: "json",
39
+ jsonFile: undefined,
40
+ concurrency: options.concurrency,
41
+ registryTimeoutMs: options.registryTimeoutMs,
42
+ };
43
+ }
44
+ export function toLicenseOptions(options) {
45
+ return {
46
+ cwd: options.cwd,
47
+ workspace: options.workspace,
48
+ allow: undefined,
49
+ deny: undefined,
50
+ sbomFile: undefined,
51
+ jsonFile: undefined,
52
+ diffMode: false,
53
+ concurrency: options.concurrency,
54
+ registryTimeoutMs: options.registryTimeoutMs,
55
+ cacheTtlSeconds: options.cacheTtlSeconds,
56
+ };
57
+ }
58
+ export function toUnusedOptions(options) {
59
+ return {
60
+ cwd: options.cwd,
61
+ workspace: options.workspace,
62
+ srcDirs: ["src", "."],
63
+ includeDevDependencies: true,
64
+ fix: false,
65
+ dryRun: true,
66
+ jsonFile: undefined,
67
+ concurrency: options.concurrency,
68
+ };
69
+ }
@@ -0,0 +1,4 @@
1
+ import type { AnalysisBundle, AuditResult, PackageUpdate, ResolveResult, ReviewItem, UnusedResult } from "../../types/index.js";
2
+ export declare function buildReviewItems(updates: PackageUpdate[], auditResult: AuditResult, resolveResult: ResolveResult, healthResult: AnalysisBundle["health"], licenseResult: AnalysisBundle["licenses"], unusedResult: UnusedResult, config: {
3
+ includeChangelog?: boolean;
4
+ }): Promise<ReviewItem[]>;
@@ -0,0 +1,128 @@
1
+ import { fetchChangelog } from "../../commands/changelog/fetcher.js";
2
+ import { applyRiskAssessments } from "../../risk/index.js";
3
+ import { applyImpactScores } from "../impact.js";
4
+ export async function buildReviewItems(updates, auditResult, resolveResult, healthResult, licenseResult, unusedResult, config) {
5
+ const advisoryPackages = new Set(auditResult.packages.map((pkg) => pkg.packageName));
6
+ const impactedUpdates = applyImpactScores(updates, {
7
+ advisoryPackages,
8
+ workspaceDependentCount: (name) => updates.filter((item) => item.name === name).length,
9
+ });
10
+ const healthByName = new Map(healthResult.metrics.map((metric) => [metric.name, metric]));
11
+ const advisoriesByName = new Map();
12
+ const conflictsByName = new Map();
13
+ const licenseByName = new Map(licenseResult.packages.map((pkg) => [pkg.name, pkg]));
14
+ const licenseViolationNames = new Set(licenseResult.violations.map((pkg) => pkg.name));
15
+ const unusedByName = new Map();
16
+ for (const advisory of auditResult.advisories) {
17
+ const list = advisoriesByName.get(advisory.packageName) ?? [];
18
+ list.push(advisory);
19
+ advisoriesByName.set(advisory.packageName, list);
20
+ }
21
+ for (const conflict of resolveResult.conflicts) {
22
+ const list = conflictsByName.get(conflict.requester) ?? [];
23
+ list.push(conflict);
24
+ conflictsByName.set(conflict.requester, list);
25
+ const peerList = conflictsByName.get(conflict.peer) ?? [];
26
+ peerList.push(conflict);
27
+ conflictsByName.set(conflict.peer, peerList);
28
+ }
29
+ for (const issue of [...unusedResult.unused, ...unusedResult.missing]) {
30
+ const list = unusedByName.get(issue.name) ?? [];
31
+ list.push(issue);
32
+ unusedByName.set(issue.name, list);
33
+ }
34
+ const enrichedUpdates = await maybeAttachReleaseNotes(impactedUpdates, Boolean(config.includeChangelog));
35
+ return applyRiskAssessments(enrichedUpdates.map((update) => enrichUpdate(update, advisoriesByName, conflictsByName, healthByName, licenseByName, licenseViolationNames, unusedByName)), {
36
+ knownPackageNames: new Set(updates.map((item) => item.name)),
37
+ }).map((item) => ({
38
+ ...item,
39
+ update: {
40
+ ...item.update,
41
+ policyAction: derivePolicyAction(item),
42
+ decisionState: deriveDecisionState(item),
43
+ selectedByDefault: deriveDecisionState(item) !== "blocked",
44
+ blockedReason: deriveDecisionState(item) === "blocked"
45
+ ? item.update.recommendedAction
46
+ : undefined,
47
+ monitorReason: item.update.healthStatus === "stale" ? "Package health should be monitored." : undefined,
48
+ },
49
+ }));
50
+ }
51
+ function enrichUpdate(update, advisoriesByName, conflictsByName, healthByName, licenseByName, licenseViolationNames, unusedByName) {
52
+ const advisories = advisoriesByName.get(update.name) ?? [];
53
+ const peerConflicts = conflictsByName.get(update.name) ?? [];
54
+ const health = healthByName.get(update.name);
55
+ const license = licenseByName.get(update.name);
56
+ const unusedIssues = unusedByName.get(update.name) ?? [];
57
+ return {
58
+ update: {
59
+ ...update,
60
+ advisoryCount: advisories.length,
61
+ peerConflictSeverity: peerConflicts.some((item) => item.severity === "error")
62
+ ? "error"
63
+ : peerConflicts.length > 0
64
+ ? "warning"
65
+ : "none",
66
+ licenseStatus: licenseViolationNames.has(update.name)
67
+ ? "denied"
68
+ : license
69
+ ? "allowed"
70
+ : "review",
71
+ healthStatus: health?.flags[0] ?? "healthy",
72
+ },
73
+ advisories,
74
+ health,
75
+ peerConflicts,
76
+ license,
77
+ unusedIssues,
78
+ selected: true,
79
+ };
80
+ }
81
+ function derivePolicyAction(item) {
82
+ if (item.update.peerConflictSeverity === "error" || item.update.licenseStatus === "denied") {
83
+ return "block";
84
+ }
85
+ if ((item.update.advisoryCount ?? 0) > 0 || item.update.riskLevel === "critical") {
86
+ return "review";
87
+ }
88
+ if (item.update.healthStatus === "stale" || item.update.healthStatus === "archived") {
89
+ return "monitor";
90
+ }
91
+ return "allow";
92
+ }
93
+ function deriveDecisionState(item) {
94
+ if (item.update.peerConflictSeverity === "error" || item.update.licenseStatus === "denied") {
95
+ return "blocked";
96
+ }
97
+ if ((item.update.advisoryCount ?? 0) > 0 || item.update.riskLevel === "critical") {
98
+ return "actionable";
99
+ }
100
+ if (item.update.riskLevel === "high" || item.update.diffType === "major") {
101
+ return "review";
102
+ }
103
+ return "safe";
104
+ }
105
+ async function maybeAttachReleaseNotes(updates, includeChangelog) {
106
+ if (!includeChangelog || updates.length === 0) {
107
+ return updates;
108
+ }
109
+ return Promise.all(updates.map(async (update) => ({
110
+ ...update,
111
+ releaseNotesSummary: summarizeChangelog(await fetchChangelog(update.name, update.repository)),
112
+ })));
113
+ }
114
+ function summarizeChangelog(content) {
115
+ if (!content)
116
+ return undefined;
117
+ const lines = content
118
+ .split(/\r?\n/)
119
+ .map((line) => line.trim())
120
+ .filter(Boolean);
121
+ const title = lines.find((line) => line.startsWith("#"))?.replace(/^#+\s*/, "") ?? "Release notes";
122
+ const excerpt = lines.find((line) => !line.startsWith("#")) ?? "No summary available.";
123
+ return {
124
+ source: content.includes("# Release") ? "github-release" : "changelog-file",
125
+ title,
126
+ excerpt: excerpt.slice(0, 240),
127
+ };
128
+ }
@@ -0,0 +1 @@
1
+ export declare function runSilenced<T>(fn: () => Promise<T>): Promise<T>;