@rainy-updates/cli 0.5.7 → 0.6.1

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 (105) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +90 -31
  3. package/dist/bin/cli.js +11 -126
  4. package/dist/bin/dispatch.js +35 -32
  5. package/dist/bin/help.js +79 -2
  6. package/dist/bin/main.d.ts +1 -0
  7. package/dist/bin/main.js +126 -0
  8. package/dist/cache/cache.js +13 -11
  9. package/dist/commands/audit/parser.js +38 -2
  10. package/dist/commands/audit/runner.js +41 -61
  11. package/dist/commands/audit/targets.js +13 -13
  12. package/dist/commands/bisect/oracle.js +31 -11
  13. package/dist/commands/bisect/parser.js +3 -3
  14. package/dist/commands/bisect/runner.js +16 -8
  15. package/dist/commands/changelog/fetcher.js +11 -5
  16. package/dist/commands/dashboard/parser.js +144 -1
  17. package/dist/commands/dashboard/runner.d.ts +2 -2
  18. package/dist/commands/dashboard/runner.js +67 -37
  19. package/dist/commands/doctor/parser.js +53 -4
  20. package/dist/commands/doctor/runner.js +2 -2
  21. package/dist/commands/ga/parser.js +43 -4
  22. package/dist/commands/ga/runner.js +22 -13
  23. package/dist/commands/health/parser.js +38 -2
  24. package/dist/commands/health/runner.js +5 -1
  25. package/dist/commands/hook/parser.d.ts +2 -0
  26. package/dist/commands/hook/parser.js +40 -0
  27. package/dist/commands/hook/runner.d.ts +2 -0
  28. package/dist/commands/hook/runner.js +174 -0
  29. package/dist/commands/licenses/parser.js +39 -0
  30. package/dist/commands/licenses/runner.js +9 -5
  31. package/dist/commands/resolve/graph/builder.js +5 -1
  32. package/dist/commands/resolve/parser.js +39 -0
  33. package/dist/commands/resolve/runner.js +14 -4
  34. package/dist/commands/review/parser.js +101 -4
  35. package/dist/commands/review/runner.js +31 -5
  36. package/dist/commands/snapshot/parser.js +39 -0
  37. package/dist/commands/snapshot/runner.js +21 -18
  38. package/dist/commands/snapshot/store.d.ts +0 -12
  39. package/dist/commands/snapshot/store.js +26 -38
  40. package/dist/commands/unused/parser.js +39 -0
  41. package/dist/commands/unused/runner.js +10 -8
  42. package/dist/commands/unused/scanner.d.ts +2 -1
  43. package/dist/commands/unused/scanner.js +65 -52
  44. package/dist/config/loader.d.ts +2 -2
  45. package/dist/config/loader.js +2 -5
  46. package/dist/config/policy.js +20 -11
  47. package/dist/core/analysis/run-silenced.js +0 -1
  48. package/dist/core/artifacts.js +6 -5
  49. package/dist/core/baseline.js +3 -5
  50. package/dist/core/check.js +7 -3
  51. package/dist/core/ci.js +52 -1
  52. package/dist/core/decision-plan.d.ts +14 -0
  53. package/dist/core/decision-plan.js +107 -0
  54. package/dist/core/doctor/result.js +8 -5
  55. package/dist/core/fix-pr-batch.js +38 -28
  56. package/dist/core/fix-pr.js +27 -24
  57. package/dist/core/init-ci.js +34 -28
  58. package/dist/core/options.d.ts +4 -1
  59. package/dist/core/options.js +152 -4
  60. package/dist/core/review-model.js +3 -0
  61. package/dist/core/summary.js +6 -0
  62. package/dist/core/upgrade.js +64 -2
  63. package/dist/core/verification.d.ts +2 -0
  64. package/dist/core/verification.js +108 -0
  65. package/dist/core/warm-cache.js +7 -3
  66. package/dist/generated/version.d.ts +1 -0
  67. package/dist/generated/version.js +2 -0
  68. package/dist/git/scope.d.ts +19 -0
  69. package/dist/git/scope.js +167 -0
  70. package/dist/index.d.ts +2 -1
  71. package/dist/index.js +1 -0
  72. package/dist/output/format.js +15 -0
  73. package/dist/output/github.js +6 -0
  74. package/dist/output/sarif.js +12 -18
  75. package/dist/parsers/package-json.js +2 -4
  76. package/dist/pm/detect.d.ts +40 -1
  77. package/dist/pm/detect.js +152 -9
  78. package/dist/pm/install.d.ts +3 -1
  79. package/dist/pm/install.js +18 -17
  80. package/dist/registry/npm.js +34 -76
  81. package/dist/rup +0 -0
  82. package/dist/types/index.d.ts +134 -5
  83. package/dist/ui/tui.d.ts +4 -1
  84. package/dist/ui/tui.js +156 -67
  85. package/dist/utils/io.js +5 -6
  86. package/dist/utils/lockfile.js +24 -19
  87. package/dist/utils/runtime-paths.d.ts +4 -0
  88. package/dist/utils/runtime-paths.js +35 -0
  89. package/dist/utils/runtime.d.ts +7 -0
  90. package/dist/utils/runtime.js +32 -0
  91. package/dist/workspace/discover.d.ts +7 -1
  92. package/dist/workspace/discover.js +67 -54
  93. package/package.json +24 -19
  94. package/dist/ui/dashboard/DashboardTUI.d.ts +0 -6
  95. package/dist/ui/dashboard/DashboardTUI.js +0 -34
  96. package/dist/ui/dashboard/components/DetailPanel.d.ts +0 -4
  97. package/dist/ui/dashboard/components/DetailPanel.js +0 -30
  98. package/dist/ui/dashboard/components/Footer.d.ts +0 -4
  99. package/dist/ui/dashboard/components/Footer.js +0 -9
  100. package/dist/ui/dashboard/components/Header.d.ts +0 -4
  101. package/dist/ui/dashboard/components/Header.js +0 -12
  102. package/dist/ui/dashboard/components/Sidebar.d.ts +0 -4
  103. package/dist/ui/dashboard/components/Sidebar.js +0 -23
  104. package/dist/ui/dashboard/store.d.ts +0 -34
  105. package/dist/ui/dashboard/store.js +0 -148
@@ -1,17 +1,17 @@
1
- import process from "node:process";
2
1
  import { check } from "../core/check.js";
3
2
  import { upgrade } from "../core/upgrade.js";
4
3
  import { warmCache } from "../core/warm-cache.js";
5
4
  import { runCi } from "../core/ci.js";
6
5
  import { initCiWorkflow } from "../core/init-ci.js";
7
6
  import { diffBaseline, saveBaseline } from "../core/baseline.js";
7
+ import { setRuntimeExitCode, writeStdout, } from "../utils/runtime.js";
8
8
  export async function handleDirectCommand(parsed) {
9
9
  if (parsed.command === "init-ci") {
10
10
  const workflow = await initCiWorkflow(parsed.options.cwd, parsed.options.force, {
11
11
  mode: parsed.options.mode,
12
12
  schedule: parsed.options.schedule,
13
13
  });
14
- process.stdout.write(workflow.created
14
+ writeStdout(workflow.created
15
15
  ? `Created CI workflow at ${workflow.path}\n`
16
16
  : `CI workflow already exists at ${workflow.path}. Use --force to overwrite.\n`);
17
17
  return true;
@@ -19,110 +19,113 @@ export async function handleDirectCommand(parsed) {
19
19
  if (parsed.command === "baseline") {
20
20
  if (parsed.options.action === "save") {
21
21
  const saved = await saveBaseline(parsed.options);
22
- process.stdout.write(`Saved baseline at ${saved.filePath} (${saved.entries} entries)\n`);
22
+ writeStdout(`Saved baseline at ${saved.filePath} (${saved.entries} entries)\n`);
23
23
  return true;
24
24
  }
25
25
  const diff = await diffBaseline(parsed.options);
26
26
  const changes = diff.added.length + diff.removed.length + diff.changed.length;
27
27
  if (changes === 0) {
28
- process.stdout.write(`No baseline drift detected (${diff.filePath}).\n`);
28
+ writeStdout(`No baseline drift detected (${diff.filePath}).\n`);
29
29
  return true;
30
30
  }
31
- process.stdout.write(`Baseline drift detected (${diff.filePath}).\n`);
31
+ writeStdout(`Baseline drift detected (${diff.filePath}).\n`);
32
32
  if (diff.added.length > 0)
33
- process.stdout.write(`Added: ${diff.added.length}\n`);
33
+ writeStdout(`Added: ${diff.added.length}\n`);
34
34
  if (diff.removed.length > 0)
35
- process.stdout.write(`Removed: ${diff.removed.length}\n`);
35
+ writeStdout(`Removed: ${diff.removed.length}\n`);
36
36
  if (diff.changed.length > 0)
37
- process.stdout.write(`Changed: ${diff.changed.length}\n`);
38
- process.exitCode = 1;
37
+ writeStdout(`Changed: ${diff.changed.length}\n`);
38
+ setRuntimeExitCode(1);
39
39
  return true;
40
40
  }
41
41
  if (parsed.command === "bisect") {
42
42
  const { runBisect } = await import("../commands/bisect/runner.js");
43
43
  const result = await runBisect(parsed.options);
44
- process.exitCode = result.breakingVersion ? 1 : 0;
44
+ setRuntimeExitCode(result.breakingVersion ? 1 : 0);
45
45
  return true;
46
46
  }
47
47
  if (parsed.command === "audit") {
48
48
  const { runAudit } = await import("../commands/audit/runner.js");
49
49
  const result = await runAudit(parsed.options);
50
- process.exitCode = result.advisories.length > 0 ? 1 : 0;
50
+ setRuntimeExitCode(result.advisories.length > 0 ? 1 : 0);
51
51
  return true;
52
52
  }
53
53
  if (parsed.command === "health") {
54
54
  const { runHealth } = await import("../commands/health/runner.js");
55
55
  const result = await runHealth(parsed.options);
56
- process.exitCode = result.totalFlagged > 0 ? 1 : 0;
56
+ setRuntimeExitCode(result.totalFlagged > 0 ? 1 : 0);
57
57
  return true;
58
58
  }
59
59
  if (parsed.command === "unused") {
60
60
  const { runUnused } = await import("../commands/unused/runner.js");
61
61
  const result = await runUnused(parsed.options);
62
- process.exitCode = result.totalUnused > 0 || result.totalMissing > 0 ? 1 : 0;
62
+ setRuntimeExitCode(result.totalUnused > 0 || result.totalMissing > 0 ? 1 : 0);
63
63
  return true;
64
64
  }
65
65
  if (parsed.command === "resolve") {
66
66
  const { runResolve } = await import("../commands/resolve/runner.js");
67
67
  const result = await runResolve(parsed.options);
68
- process.exitCode = result.errorConflicts > 0 ? 1 : 0;
68
+ setRuntimeExitCode(result.errorConflicts > 0 ? 1 : 0);
69
69
  return true;
70
70
  }
71
71
  if (parsed.command === "licenses") {
72
72
  const { runLicenses } = await import("../commands/licenses/runner.js");
73
73
  const result = await runLicenses(parsed.options);
74
- process.exitCode = result.totalViolations > 0 ? 1 : 0;
74
+ setRuntimeExitCode(result.totalViolations > 0 ? 1 : 0);
75
75
  return true;
76
76
  }
77
77
  if (parsed.command === "snapshot") {
78
78
  const { runSnapshot } = await import("../commands/snapshot/runner.js");
79
79
  const result = await runSnapshot(parsed.options);
80
- process.exitCode = result.errors.length > 0 ? 1 : 0;
80
+ setRuntimeExitCode(result.errors.length > 0 ? 1 : 0);
81
81
  return true;
82
82
  }
83
83
  if (parsed.command === "review") {
84
84
  const { runReview } = await import("../commands/review/runner.js");
85
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;
86
+ setRuntimeExitCode(result.summary.verdict === "blocked" ||
87
+ result.summary.verdict === "actionable" ||
88
+ result.summary.verdict === "review"
89
+ ? 1
90
+ : 0);
92
91
  return true;
93
92
  }
94
93
  if (parsed.command === "doctor") {
95
94
  const { runDoctor } = await import("../commands/doctor/runner.js");
96
95
  const result = await runDoctor(parsed.options);
97
- process.exitCode = result.verdict === "safe" ? 0 : 1;
96
+ setRuntimeExitCode(result.verdict === "safe" ? 0 : 1);
98
97
  return true;
99
98
  }
100
99
  if (parsed.command === "dashboard") {
101
100
  const { runDashboard } = await import("../commands/dashboard/runner.js");
102
101
  const result = await runDashboard(parsed.options);
103
- process.exitCode = result.errors.length > 0 ? 1 : 0;
102
+ setRuntimeExitCode(result.errors.length > 0 ? 1 : 0);
104
103
  return true;
105
104
  }
106
105
  if (parsed.command === "ga") {
107
106
  const { runGa } = await import("../commands/ga/runner.js");
108
107
  const result = await runGa(parsed.options);
109
- process.exitCode = result.ready ? 0 : 1;
108
+ setRuntimeExitCode(result.ready ? 0 : 1);
109
+ return true;
110
+ }
111
+ if (parsed.command === "hook") {
112
+ const { runHook } = await import("../commands/hook/runner.js");
113
+ const result = await runHook(parsed.options);
114
+ setRuntimeExitCode(result.errors.length > 0 ? 1 : 0);
110
115
  return true;
111
116
  }
112
117
  if (parsed.options.interactive &&
113
118
  (parsed.command === "check" ||
114
119
  parsed.command === "upgrade" ||
115
120
  parsed.command === "ci")) {
116
- const { runReview } = await import("../commands/review/runner.js");
117
- const result = await runReview({
121
+ const { runDashboard } = await import("../commands/dashboard/runner.js");
122
+ const result = await runDashboard({
118
123
  ...parsed.options,
119
- securityOnly: false,
120
- risk: undefined,
121
- diff: undefined,
124
+ mode: parsed.command === "upgrade" ? "upgrade" : "review",
125
+ focus: "all",
122
126
  applySelected: parsed.command === "upgrade",
123
127
  });
124
- process.exitCode =
125
- result.summary.verdict === "safe" && result.updates.length === 0 ? 0 : 1;
128
+ setRuntimeExitCode(result.errors.length > 0 ? 1 : 0);
126
129
  return true;
127
130
  }
128
131
  return false;
package/dist/bin/help.js CHANGED
@@ -40,7 +40,16 @@ Options:
40
40
  --cooldown-days <n>
41
41
  --pr-limit <n>
42
42
  --only-changed
43
+ --affected
44
+ --staged
45
+ --base <ref>
46
+ --head <ref>
47
+ --since <ref>
43
48
  --interactive
49
+ --plan-file <path>
50
+ --verify none|install|test|install,test
51
+ --test-command <cmd>
52
+ --verification-report-file <path>
44
53
  --show-impact
45
54
  --show-links
46
55
  --show-homepage
@@ -79,7 +88,7 @@ Options:
79
88
  --workspace
80
89
  --sync
81
90
  --install
82
- --pm auto|npm|pnpm
91
+ --pm auto|bun|npm|pnpm|yarn
83
92
  --target patch|minor|major|latest
84
93
  --policy-file <path>
85
94
  --concurrency <n>
@@ -92,6 +101,10 @@ Options:
92
101
  --fix-pr-no-checkout
93
102
  --fix-pr-batch-size <n>
94
103
  --interactive
104
+ --from-plan <path>
105
+ --verify none|install|test|install,test
106
+ --test-command <cmd>
107
+ --verification-report-file <path>
95
108
  --lockfile-mode preserve|update|error
96
109
  --no-pr-report
97
110
  --json-file <path>
@@ -109,11 +122,17 @@ Run CI-oriented automation around the same lifecycle:
109
122
  Options:
110
123
  --workspace
111
124
  --mode minimal|strict|enterprise
125
+ --gate check|doctor|review|upgrade
112
126
  --group-by none|name|scope|kind|risk
113
127
  --group-max <n>
114
128
  --cooldown-days <n>
115
129
  --pr-limit <n>
116
130
  --only-changed
131
+ --affected
132
+ --staged
133
+ --base <ref>
134
+ --head <ref>
135
+ --since <ref>
117
136
  --offline
118
137
  --concurrency <n>
119
138
  --registry-timeout-ms <n>
@@ -126,6 +145,10 @@ Options:
126
145
  --fix-pr-no-checkout
127
146
  --fix-pr-batch-size <n>
128
147
  --no-pr-report
148
+ --plan-file <path>
149
+ --verify none|install|test|install,test
150
+ --test-command <cmd>
151
+ --verification-report-file <path>
129
152
  --json-file <path>
130
153
  --github-output <path>
131
154
  --sarif-file <path>
@@ -167,6 +190,11 @@ Scan dependencies for CVEs using OSV.dev and GitHub Advisory Database.
167
190
 
168
191
  Options:
169
192
  --workspace
193
+ --affected
194
+ --staged
195
+ --base <ref>
196
+ --head <ref>
197
+ --since <ref>
170
198
  --severity critical|high|medium|low
171
199
  --summary
172
200
  --report table|summary|json
@@ -187,11 +215,18 @@ Use it to inspect risk, security, peer, license, and policy context before apply
187
215
 
188
216
  Options:
189
217
  --workspace
218
+ --only-changed
219
+ --affected
220
+ --staged
221
+ --base <ref>
222
+ --head <ref>
223
+ --since <ref>
190
224
  --interactive
191
225
  --security-only
192
226
  --risk critical|high|medium|low
193
227
  --diff patch|minor|major|latest
194
228
  --apply-selected
229
+ --plan-file <path>
195
230
  --show-changelog
196
231
  --policy-file <path>
197
232
  --json-file <path>
@@ -206,9 +241,37 @@ Produce a fast summary verdict and point the operator to review when action is n
206
241
 
207
242
  Options:
208
243
  --workspace
244
+ --only-changed
245
+ --affected
246
+ --staged
247
+ --base <ref>
248
+ --head <ref>
249
+ --since <ref>
209
250
  --verdict-only
210
251
  --include-changelog
211
252
  --json-file <path>`;
253
+ }
254
+ if (isCommand && command === "dashboard") {
255
+ return `rainy-updates dashboard [options]
256
+
257
+ Open the primary interactive dependency operations console.
258
+
259
+ Options:
260
+ --workspace
261
+ --only-changed
262
+ --affected
263
+ --staged
264
+ --base <ref>
265
+ --head <ref>
266
+ --since <ref>
267
+ --mode check|review|upgrade
268
+ --focus all|security|risk|major|blocked|workspace
269
+ --apply-selected
270
+ --plan-file <path>
271
+ --verify none|install|test|install,test
272
+ --test-command <cmd>
273
+ --verification-report-file <path>
274
+ --cwd <path>`;
212
275
  }
213
276
  if (isCommand && command === "ga") {
214
277
  return `rainy-updates ga [options]
@@ -218,6 +281,14 @@ Audit release and CI readiness for Rainy Updates.
218
281
  Options:
219
282
  --workspace
220
283
  --json-file <path>
284
+ --cwd <path>`;
285
+ }
286
+ if (isCommand && command === "hook") {
287
+ return `rainy-updates hook <install|uninstall|doctor> [options]
288
+
289
+ Install, remove, or inspect Rainy-managed git hooks.
290
+
291
+ Options:
221
292
  --cwd <path>`;
222
293
  }
223
294
  return `rainy-updates (rup / rainy-up) <command> [options]
@@ -227,7 +298,7 @@ Commands:
227
298
  doctor Summarize what matters
228
299
  review Decide what to do
229
300
  upgrade Apply the approved change set
230
- dashboard Open the interactive DevOps dashboard (Ink TUI)
301
+ dashboard Open the primary interactive dependency dashboard
231
302
  ci Run CI-focused orchestration
232
303
  warm-cache Warm local cache for fast/offline checks
233
304
  init-ci Scaffold GitHub Actions workflow
@@ -240,6 +311,7 @@ Commands:
240
311
  licenses Scan dependency licenses and generate SPDX SBOM
241
312
  snapshot Save, list, restore, and diff dependency state snapshots
242
313
  ga Audit GA and CI readiness for this checkout
314
+ hook Install or inspect Rainy-managed git hooks
243
315
 
244
316
  Global options:
245
317
  --cwd <path>
@@ -258,6 +330,11 @@ Global options:
258
330
  --cooldown-days <n>
259
331
  --pr-limit <n>
260
332
  --only-changed
333
+ --affected
334
+ --staged
335
+ --base <ref>
336
+ --head <ref>
337
+ --since <ref>
261
338
  --interactive
262
339
  --show-impact
263
340
  --show-links
@@ -0,0 +1 @@
1
+ export declare function runCli(): Promise<void>;
@@ -0,0 +1,126 @@
1
+ import { parseCliArgs } from "../core/options.js";
2
+ import { createRunId, writeArtifactManifest } from "../core/artifacts.js";
3
+ import { renderResult } from "../output/format.js";
4
+ import { writeGitHubOutput } from "../output/github.js";
5
+ import { createSarifReport } from "../output/sarif.js";
6
+ import { renderPrReport } from "../output/pr-report.js";
7
+ import { writeFileAtomic } from "../utils/io.js";
8
+ import { resolveFailReason } from "../core/summary.js";
9
+ import { stableStringify } from "../utils/stable-json.js";
10
+ import { getRuntimeArgv, setRuntimeExitCode, writeStderr, writeStdout, } from "../utils/runtime.js";
11
+ import { CLI_VERSION } from "../generated/version.js";
12
+ import { handleDirectCommand, runPrimaryCommand } from "./dispatch.js";
13
+ import { renderHelp } from "./help.js";
14
+ export async function runCli() {
15
+ try {
16
+ const argv = getRuntimeArgv();
17
+ if (argv.includes("--version") || argv.includes("-v")) {
18
+ writeStdout((await readPackageVersion()) + "\n");
19
+ return;
20
+ }
21
+ if (argv.includes("--help") || argv.includes("-h")) {
22
+ writeStdout(renderHelp(argv[0]) + "\n");
23
+ return;
24
+ }
25
+ const parsed = await parseCliArgs(argv);
26
+ if (await handleDirectCommand(parsed))
27
+ return;
28
+ if (parsed.command !== "check" &&
29
+ parsed.command !== "upgrade" &&
30
+ parsed.command !== "warm-cache" &&
31
+ parsed.command !== "ci") {
32
+ throw new Error(`Unhandled command: ${parsed.command}`);
33
+ }
34
+ const result = await runPrimaryCommand(parsed);
35
+ result.summary.runId = createRunId(parsed.command, parsed.options, result);
36
+ if (parsed.options.fixPr &&
37
+ (parsed.command === "check" ||
38
+ parsed.command === "upgrade" ||
39
+ parsed.command === "ci")) {
40
+ result.summary.fixPrApplied = false;
41
+ result.summary.fixBranchName =
42
+ parsed.options.fixBranch ?? "chore/rainy-updates";
43
+ result.summary.fixCommitSha = "";
44
+ result.summary.fixPrBranchesCreated = 0;
45
+ if (parsed.command === "ci") {
46
+ const { applyFixPrBatches } = await import("../core/fix-pr-batch.js");
47
+ const batched = await applyFixPrBatches(parsed.options, result);
48
+ result.summary.fixPrApplied = batched.applied;
49
+ result.summary.fixBranchName =
50
+ batched.branches[0] ??
51
+ parsed.options.fixBranch ??
52
+ "chore/rainy-updates";
53
+ result.summary.fixCommitSha = batched.commits[0] ?? "";
54
+ result.summary.fixPrBranchesCreated = batched.branches.length;
55
+ if (batched.branches.length > 1) {
56
+ result.warnings.push(`Created ${batched.branches.length} fix-pr batch branches.`);
57
+ }
58
+ }
59
+ else {
60
+ const { applyFixPr } = await import("../core/fix-pr.js");
61
+ const fixResult = await applyFixPr(parsed.options, result, []);
62
+ result.summary.fixPrApplied = fixResult.applied;
63
+ result.summary.fixBranchName = fixResult.branchName ?? "";
64
+ result.summary.fixCommitSha = fixResult.commitSha ?? "";
65
+ result.summary.fixPrBranchesCreated = fixResult.applied ? 1 : 0;
66
+ }
67
+ }
68
+ if (parsed.options.prReportFile) {
69
+ const markdown = renderPrReport(result);
70
+ await writeFileAtomic(parsed.options.prReportFile, markdown + "\n");
71
+ }
72
+ const artifactManifest = await writeArtifactManifest(parsed.command, parsed.options, result);
73
+ if (artifactManifest) {
74
+ result.summary.artifactManifest = artifactManifest.artifactManifestPath;
75
+ }
76
+ result.summary.failReason = resolveFailReason(result.updates, result.errors, parsed.options.failOn, parsed.options.maxUpdates, parsed.options.ci);
77
+ const renderStartedAt = Date.now();
78
+ let rendered = renderResult(result, parsed.options.format, {
79
+ showImpact: parsed.options.showImpact,
80
+ showHomepage: parsed.options.showHomepage,
81
+ });
82
+ result.summary.durationMs.render = Math.max(0, Date.now() - renderStartedAt);
83
+ if (parsed.options.format === "json" ||
84
+ parsed.options.format === "metrics") {
85
+ rendered = renderResult(result, parsed.options.format, {
86
+ showImpact: parsed.options.showImpact,
87
+ showHomepage: parsed.options.showHomepage,
88
+ });
89
+ }
90
+ if (parsed.options.onlyChanged &&
91
+ result.updates.length === 0 &&
92
+ result.errors.length === 0 &&
93
+ result.warnings.length === 0 &&
94
+ (parsed.options.format === "table" ||
95
+ parsed.options.format === "minimal" ||
96
+ parsed.options.format === "github")) {
97
+ rendered = "";
98
+ }
99
+ if (parsed.options.jsonFile) {
100
+ await writeFileAtomic(parsed.options.jsonFile, stableStringify(result, 2) + "\n");
101
+ }
102
+ if (parsed.options.githubOutputFile) {
103
+ await writeGitHubOutput(parsed.options.githubOutputFile, result);
104
+ }
105
+ if (parsed.options.sarifFile) {
106
+ const sarif = createSarifReport(result);
107
+ await writeFileAtomic(parsed.options.sarifFile, stableStringify(sarif, 2) + "\n");
108
+ }
109
+ writeStdout(rendered + "\n");
110
+ setRuntimeExitCode(resolveExitCode(result, result.summary.failReason));
111
+ }
112
+ catch (error) {
113
+ writeStderr(`rainy-updates (rup): ${String(error)}\n`);
114
+ setRuntimeExitCode(2);
115
+ }
116
+ }
117
+ async function readPackageVersion() {
118
+ return CLI_VERSION;
119
+ }
120
+ function resolveExitCode(result, failReason) {
121
+ if (result.errors.length > 0)
122
+ return 2;
123
+ if (failReason !== "none")
124
+ return 1;
125
+ return 0;
126
+ }
@@ -1,7 +1,7 @@
1
- import { promises as fs } from "node:fs";
2
- import os from "node:os";
3
1
  import path from "node:path";
4
- import process from "node:process";
2
+ import { writeFileAtomic } from "../utils/io.js";
3
+ import { getCacheDir } from "../utils/runtime-paths.js";
4
+ import { readEnv } from "../utils/runtime.js";
5
5
  class FileCacheStore {
6
6
  filePath;
7
7
  constructor(filePath) {
@@ -15,19 +15,19 @@ class FileCacheStore {
15
15
  return null;
16
16
  return {
17
17
  ...entry,
18
- availableVersions: Array.isArray(entry.availableVersions) ? entry.availableVersions : [entry.latestVersion],
18
+ availableVersions: Array.isArray(entry.availableVersions)
19
+ ? entry.availableVersions
20
+ : [entry.latestVersion],
19
21
  };
20
22
  }
21
23
  async set(entry) {
22
24
  const entries = await this.readEntries();
23
25
  entries[this.getKey(entry.packageName, entry.target)] = entry;
24
- await fs.mkdir(path.dirname(this.filePath), { recursive: true });
25
- await fs.writeFile(this.filePath, JSON.stringify(entries), "utf8");
26
+ await writeFileAtomic(this.filePath, JSON.stringify(entries));
26
27
  }
27
28
  async readEntries() {
28
29
  try {
29
- const content = await fs.readFile(this.filePath, "utf8");
30
- return JSON.parse(content);
30
+ return (await Bun.file(this.filePath).json());
31
31
  }
32
32
  catch {
33
33
  return {};
@@ -93,7 +93,9 @@ class SqliteCacheStore {
93
93
  }
94
94
  ensureSchema() {
95
95
  try {
96
- const columns = this.db.prepare("PRAGMA table_info(versions);").all();
96
+ const columns = this.db
97
+ .prepare("PRAGMA table_info(versions);")
98
+ .all();
97
99
  const hasAvailableVersions = columns.some((column) => column.name === "available_versions");
98
100
  if (!hasAvailableVersions) {
99
101
  this.db.exec("ALTER TABLE versions ADD COLUMN available_versions TEXT;");
@@ -117,8 +119,8 @@ export class VersionCache {
117
119
  this.fallbackReason = fallbackReason;
118
120
  }
119
121
  static async create(customPath) {
120
- const basePath = customPath ?? path.join(os.homedir(), ".cache", "rainy-updates");
121
- if (process.env.RAINY_UPDATES_CACHE_BACKEND === "file") {
122
+ const basePath = customPath ?? getCacheDir();
123
+ if (readEnv("RAINY_UPDATES_CACHE_BACKEND") === "file") {
122
124
  const jsonPath = path.join(basePath, "cache.json");
123
125
  return new VersionCache(new FileCacheStore(jsonPath), "file", true, "forced via RAINY_UPDATES_CACHE_BACKEND=file");
124
126
  }
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import process from "node:process";
2
+ import { getRuntimeCwd } from "../../utils/runtime.js";
3
3
  const SEVERITY_LEVELS = ["critical", "high", "medium", "low"];
4
4
  const SOURCE_MODES = ["auto", "osv", "github", "all"];
5
5
  export function parseSeverity(value) {
@@ -10,8 +10,13 @@ export function parseSeverity(value) {
10
10
  }
11
11
  export function parseAuditArgs(args) {
12
12
  const options = {
13
- cwd: process.cwd(),
13
+ cwd: getRuntimeCwd(),
14
14
  workspace: false,
15
+ affected: false,
16
+ staged: false,
17
+ baseRef: undefined,
18
+ headRef: undefined,
19
+ sinceRef: undefined,
15
20
  severity: undefined,
16
21
  fix: false,
17
22
  dryRun: false,
@@ -39,6 +44,37 @@ export function parseAuditArgs(args) {
39
44
  index += 1;
40
45
  continue;
41
46
  }
47
+ if (current === "--affected") {
48
+ options.affected = true;
49
+ index += 1;
50
+ continue;
51
+ }
52
+ if (current === "--staged") {
53
+ options.staged = true;
54
+ index += 1;
55
+ continue;
56
+ }
57
+ if (current === "--base" && next) {
58
+ options.baseRef = next;
59
+ index += 2;
60
+ continue;
61
+ }
62
+ if (current === "--base")
63
+ throw new Error("Missing value for --base");
64
+ if (current === "--head" && next) {
65
+ options.headRef = next;
66
+ index += 2;
67
+ continue;
68
+ }
69
+ if (current === "--head")
70
+ throw new Error("Missing value for --head");
71
+ if (current === "--since" && next) {
72
+ options.sinceRef = next;
73
+ index += 2;
74
+ continue;
75
+ }
76
+ if (current === "--since")
77
+ throw new Error("Missing value for --since");
42
78
  if (current === "--severity" && next) {
43
79
  options.severity = parseSeverity(next);
44
80
  index += 2;