@rainy-updates/cli 0.6.0 → 0.6.2

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 (63) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +12 -0
  3. package/dist/bin/cli.js +12 -127
  4. package/dist/bin/dispatch.js +6 -0
  5. package/dist/bin/help.js +48 -0
  6. package/dist/bin/main.d.ts +1 -0
  7. package/dist/bin/main.js +126 -0
  8. package/dist/commands/audit/parser.js +36 -0
  9. package/dist/commands/audit/runner.js +17 -18
  10. package/dist/commands/bisect/oracle.js +21 -17
  11. package/dist/commands/bisect/runner.js +4 -3
  12. package/dist/commands/dashboard/parser.js +41 -0
  13. package/dist/commands/dashboard/runner.js +3 -0
  14. package/dist/commands/doctor/parser.js +44 -0
  15. package/dist/commands/ga/parser.js +39 -0
  16. package/dist/commands/ga/runner.js +73 -9
  17. package/dist/commands/health/parser.js +36 -0
  18. package/dist/commands/health/runner.js +5 -1
  19. package/dist/commands/hook/parser.d.ts +2 -0
  20. package/dist/commands/hook/parser.js +40 -0
  21. package/dist/commands/hook/runner.d.ts +2 -0
  22. package/dist/commands/hook/runner.js +174 -0
  23. package/dist/commands/licenses/parser.js +39 -0
  24. package/dist/commands/licenses/runner.js +5 -1
  25. package/dist/commands/resolve/graph/builder.js +5 -1
  26. package/dist/commands/resolve/parser.js +39 -0
  27. package/dist/commands/resolve/runner.js +5 -0
  28. package/dist/commands/review/parser.js +44 -0
  29. package/dist/commands/snapshot/parser.js +39 -0
  30. package/dist/commands/snapshot/runner.js +4 -1
  31. package/dist/commands/unused/parser.js +39 -0
  32. package/dist/commands/unused/runner.js +4 -1
  33. package/dist/commands/unused/scanner.d.ts +2 -1
  34. package/dist/commands/unused/scanner.js +60 -44
  35. package/dist/core/check.js +5 -1
  36. package/dist/core/doctor/findings.js +4 -4
  37. package/dist/core/init-ci.js +28 -26
  38. package/dist/core/options.d.ts +4 -1
  39. package/dist/core/options.js +57 -0
  40. package/dist/core/verification.js +11 -9
  41. package/dist/core/warm-cache.js +5 -1
  42. package/dist/generated/version.d.ts +1 -0
  43. package/dist/generated/version.js +2 -0
  44. package/dist/git/scope.d.ts +19 -0
  45. package/dist/git/scope.js +167 -0
  46. package/dist/index.d.ts +2 -1
  47. package/dist/index.js +1 -0
  48. package/dist/output/sarif.js +2 -8
  49. package/dist/pm/detect.d.ts +37 -0
  50. package/dist/pm/detect.js +133 -2
  51. package/dist/pm/install.d.ts +2 -1
  52. package/dist/pm/install.js +7 -5
  53. package/dist/rup +0 -0
  54. package/dist/types/index.d.ts +59 -1
  55. package/dist/ui/dashboard-state.d.ts +7 -0
  56. package/dist/ui/dashboard-state.js +44 -0
  57. package/dist/ui/tui.d.ts +3 -0
  58. package/dist/ui/tui.js +311 -111
  59. package/dist/utils/shell.d.ts +6 -0
  60. package/dist/utils/shell.js +18 -0
  61. package/dist/workspace/discover.d.ts +7 -1
  62. package/dist/workspace/discover.js +12 -3
  63. package/package.json +16 -8
package/CHANGELOG.md CHANGED
@@ -2,6 +2,117 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.6.2] - 2026-03-04
6
+
7
+ Dashboard hardening, cross-platform execution cleanup, and portable release operations for the next `v0.6` patch.
8
+
9
+ ### Added
10
+
11
+ - **Portable operator entrypoints**:
12
+ - added a root `Makefile` for common build/check/release flows,
13
+ - added a `ga` package script so readiness checks can be invoked consistently from Bun scripts and `make`.
14
+ - **Shared shell invocation layer** for Windows, macOS, and Linux command execution in verification and bisect flows.
15
+ - **Dedicated binary release workflow**:
16
+ - GitHub Releases now have a separate workflow from npm publishing,
17
+ - tag builds can produce standalone binaries for Linux, macOS, and Windows,
18
+ - packaged archives are uploaded with SHA-256 checksum files.
19
+ - **Distribution manifest generator**:
20
+ - generates a Homebrew formula from release asset checksums,
21
+ - generates a Scoop manifest from the Windows release asset checksum,
22
+ - release artifacts now include copy-ready Homebrew and Scoop metadata.
23
+ - **New test coverage** for:
24
+ - shared shell invocation behavior across POSIX and Windows,
25
+ - dashboard startup state derived from `--view` and `--focus`,
26
+ - updated doctor dashboard-first recommendations,
27
+ - GA readiness checks for automation entrypoints.
28
+
29
+ ### Changed
30
+
31
+ - Dashboard review operations were tightened for larger queues and cross-platform operators:
32
+ - initial dashboard focus and detail tabs now respect `--view` and `--focus`,
33
+ - the Ink queue renderer now windows large result sets instead of rendering the full queue every frame,
34
+ - bulk actions now include actionable/review selection in addition to safe and blocked flows,
35
+ - keyboard navigation now works cleanly with arrows and `hjkl`,
36
+ - terminal sizing is handled more defensively for smaller shells,
37
+ - the dashboard TUI was split into smaller focused components to reduce maintenance overhead.
38
+ - Doctor findings now point operators to the dashboard-first workflow instead of the older `review --interactive` wording.
39
+ - Verification and bisect shell execution are now portable across Windows, macOS, and Linux by routing command execution through a shared shell invocation layer.
40
+ - Release automation scripts were made portable:
41
+ - `clean` no longer depends on `rm -rf`,
42
+ - `test:prod` no longer depends on POSIX `test -x`,
43
+ - build and production validation now work with compiled Bun artifacts on Windows (`dist/rup.exe`) as well as POSIX (`dist/rup`).
44
+ - Release automation is now intentionally split:
45
+ - one workflow publishes the npm package,
46
+ - one workflow creates and uploads GitHub binary assets for standalone installation.
47
+ - `ga` readiness checks now also verify:
48
+ - portable automation entrypoints,
49
+ - obvious platform-specific script risks,
50
+ - compiled runtime artifacts in either POSIX or Windows form.
51
+
52
+ ### Tests
53
+
54
+ - Validation completed for `0.6.2`:
55
+ - `bun run lint`
56
+ - `bun run build`
57
+ - `bun run check`
58
+ - `bun run test:prod`
59
+ - `bun run ga`
60
+ - `bun run perf:check`
61
+ - `npx -y react-doctor@latest . --verbose --diff` (`99/100`)
62
+
63
+ ## [0.6.1] - 2026-03-03
64
+
65
+ Compatibility, git-aware workspace scoping, and release-readiness stabilization for the `v0.6` line.
66
+
67
+ ### Added
68
+
69
+ - **First-class package-manager profile layer**:
70
+ - detection now prefers `package.json.packageManager` before falling back to lockfiles,
71
+ - additive package-manager metadata for lockfile source and Yarn flavor detection,
72
+ - centralized install, add, and test command construction for npm, pnpm, Bun, and Yarn.
73
+ - **Git-aware workspace scoping**:
74
+ - `--affected`,
75
+ - `--staged`,
76
+ - `--base <ref>`,
77
+ - `--head <ref>`,
78
+ - `--since <ref>`.
79
+ - **Workspace dependent expansion for affected scans**:
80
+ - changed packages can now expand to dependent workspace packages instead of stopping at direct file matches.
81
+ - **New `hook` command**:
82
+ - `rup hook install`,
83
+ - `rup hook uninstall`,
84
+ - `rup hook doctor`.
85
+ - **Rainy-managed git hooks**:
86
+ - `pre-commit` runs `rup unused --workspace --staged` and `rup resolve --workspace --staged`,
87
+ - `pre-push` runs `rup audit --workspace --affected --report summary`.
88
+ - **New test coverage** for:
89
+ - package-manager field precedence and Yarn Berry behavior,
90
+ - git-scoped workspace discovery,
91
+ - hook install/doctor/uninstall lifecycle,
92
+ - scoped standalone parser support.
93
+
94
+ ### Changed
95
+
96
+ - `init-ci` workflow generation now uses the centralized package-manager profile layer instead of special-casing npm/pnpm/Bun only.
97
+ - Yarn support is now explicit in generated workflows:
98
+ - Corepack enablement for Yarn/pnpm repos,
99
+ - Yarn Berry uses immutable installs,
100
+ - Yarn package adds no longer fall back to npm command construction.
101
+ - `verification`, `audit --fix`, and `bisect` now reuse the same package-manager command model as `upgrade`.
102
+ - `ga` package-manager reporting now includes detection source details and respects the git-scoped workspace discovery flow.
103
+ - `check`, `warm-cache`, `audit`, `unused`, `resolve`, `health`, `licenses`, `snapshot`, and `ga` now share the same git-aware workspace scoping path.
104
+ - Command help and parser support were aligned so git-scoping flags are consistently accepted across the primary and standalone command surfaces.
105
+
106
+ ### Tests
107
+
108
+ - Full release validation passed:
109
+ - `pnpm -s exec tsc --noEmit`
110
+ - `bun test`
111
+ - `pnpm run build`
112
+ - `bun run build:exe`
113
+ - `bun run test:prod`
114
+ - `bun ./dist/bin/cli.js ga --workspace`
115
+
5
116
  ## [0.6.0] - 2026-03-01
6
117
 
7
118
  Dashboard-first release candidate for the `v0.6` series, focused on unifying the interactive surface, introducing replayable decision plans, tightening CI/apply verification flows, and undergoing a complete native Bun performance optimization.
package/README.md CHANGED
@@ -79,6 +79,18 @@ pnpm add -D @rainy-updates/cli
79
79
  bun add -d @rainy-updates/cli
80
80
  ```
81
81
 
82
+ ### Standalone binaries from GitHub Releases
83
+
84
+ If you do not want to depend on global npm or a project-local `node_modules`, use the standalone compiled binaries from GitHub Releases.
85
+
86
+ Release assets are published for:
87
+
88
+ - Linux x64
89
+ - Linux arm64
90
+ - macOS x64
91
+ - macOS arm64
92
+ - Windows x64
93
+
82
94
  Once installed, three binary aliases are available in your `node_modules/.bin/`:
83
95
 
84
96
  | Alias | Use case |
package/dist/bin/cli.js CHANGED
@@ -1,134 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
3
- import { parseCliArgs } from "../core/options.js";
4
- import { applyFixPr } from "../core/fix-pr.js";
5
- import { applyFixPrBatches } from "../core/fix-pr-batch.js";
6
- import { createRunId, writeArtifactManifest } from "../core/artifacts.js";
7
- import { renderResult } from "../output/format.js";
8
- import { writeGitHubOutput } from "../output/github.js";
9
- import { createSarifReport } from "../output/sarif.js";
10
- import { renderPrReport } from "../output/pr-report.js";
11
- import { writeFileAtomic } from "../utils/io.js";
12
- import { resolveFailReason } from "../core/summary.js";
13
- import { stableStringify } from "../utils/stable-json.js";
14
- import { getRuntimeArgv, setRuntimeExitCode, writeStderr, writeStdout, } from "../utils/runtime.js";
15
- import { handleDirectCommand, runPrimaryCommand } from "./dispatch.js";
16
- import { renderHelp } from "./help.js";
2
+ import { spawnSync } from "node:child_process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { runCli } from "./main.js";
17
5
  async function main() {
18
- try {
19
- const argv = getRuntimeArgv();
20
- if (argv.includes("--version") || argv.includes("-v")) {
21
- writeStdout((await readPackageVersion()) + "\n");
22
- return;
23
- }
24
- if (argv.includes("--help") || argv.includes("-h")) {
25
- writeStdout(renderHelp(argv[0]) + "\n");
26
- return;
27
- }
28
- const parsed = await parseCliArgs(argv);
29
- if (await handleDirectCommand(parsed))
30
- return;
31
- if (parsed.command !== "check" &&
32
- parsed.command !== "upgrade" &&
33
- parsed.command !== "warm-cache" &&
34
- parsed.command !== "ci") {
35
- throw new Error(`Unhandled command: ${parsed.command}`);
36
- }
37
- const result = await runPrimaryCommand(parsed);
38
- result.summary.runId = createRunId(parsed.command, parsed.options, result);
39
- if (parsed.options.fixPr &&
40
- (parsed.command === "check" ||
41
- parsed.command === "upgrade" ||
42
- parsed.command === "ci")) {
43
- result.summary.fixPrApplied = false;
44
- result.summary.fixBranchName =
45
- parsed.options.fixBranch ?? "chore/rainy-updates";
46
- result.summary.fixCommitSha = "";
47
- result.summary.fixPrBranchesCreated = 0;
48
- if (parsed.command === "ci") {
49
- const batched = await applyFixPrBatches(parsed.options, result);
50
- result.summary.fixPrApplied = batched.applied;
51
- result.summary.fixBranchName =
52
- batched.branches[0] ??
53
- parsed.options.fixBranch ??
54
- "chore/rainy-updates";
55
- result.summary.fixCommitSha = batched.commits[0] ?? "";
56
- result.summary.fixPrBranchesCreated = batched.branches.length;
57
- if (batched.branches.length > 1) {
58
- result.warnings.push(`Created ${batched.branches.length} fix-pr batch branches.`);
59
- }
60
- }
61
- else {
62
- const fixResult = await applyFixPr(parsed.options, result, []);
63
- result.summary.fixPrApplied = fixResult.applied;
64
- result.summary.fixBranchName = fixResult.branchName ?? "";
65
- result.summary.fixCommitSha = fixResult.commitSha ?? "";
66
- result.summary.fixPrBranchesCreated = fixResult.applied ? 1 : 0;
67
- }
68
- }
69
- if (parsed.options.prReportFile) {
70
- const markdown = renderPrReport(result);
71
- await writeFileAtomic(parsed.options.prReportFile, markdown + "\n");
72
- }
73
- const artifactManifest = await writeArtifactManifest(parsed.command, parsed.options, result);
74
- if (artifactManifest) {
75
- result.summary.artifactManifest = artifactManifest.artifactManifestPath;
76
- }
77
- result.summary.failReason = resolveFailReason(result.updates, result.errors, parsed.options.failOn, parsed.options.maxUpdates, parsed.options.ci);
78
- const renderStartedAt = Date.now();
79
- let rendered = renderResult(result, parsed.options.format, {
80
- showImpact: parsed.options.showImpact,
81
- showHomepage: parsed.options.showHomepage,
6
+ if (typeof Bun === "undefined") {
7
+ const currentFile = fileURLToPath(import.meta.url);
8
+ const result = spawnSync("bun", [currentFile, ...process.argv.slice(2)], {
9
+ stdio: "inherit",
82
10
  });
83
- result.summary.durationMs.render = Math.max(0, Date.now() - renderStartedAt);
84
- if (parsed.options.format === "json" ||
85
- parsed.options.format === "metrics") {
86
- rendered = renderResult(result, parsed.options.format, {
87
- showImpact: parsed.options.showImpact,
88
- showHomepage: parsed.options.showHomepage,
89
- });
90
- }
91
- if (parsed.options.onlyChanged &&
92
- result.updates.length === 0 &&
93
- result.errors.length === 0 &&
94
- result.warnings.length === 0 &&
95
- (parsed.options.format === "table" ||
96
- parsed.options.format === "minimal" ||
97
- parsed.options.format === "github")) {
98
- rendered = "";
99
- }
100
- if (parsed.options.jsonFile) {
101
- await writeFileAtomic(parsed.options.jsonFile, stableStringify(result, 2) + "\n");
11
+ if (result.error) {
12
+ process.stderr.write("rainy-updates (rup): Bun is required to run the published JavaScript entrypoint. Install Bun or use the compiled binary release.\n");
13
+ process.exit(1);
102
14
  }
103
- if (parsed.options.githubOutputFile) {
104
- await writeGitHubOutput(parsed.options.githubOutputFile, result);
105
- }
106
- if (parsed.options.sarifFile) {
107
- const sarif = createSarifReport(result);
108
- await writeFileAtomic(parsed.options.sarifFile, stableStringify(sarif, 2) + "\n");
109
- }
110
- writeStdout(rendered + "\n");
111
- setRuntimeExitCode(resolveExitCode(result, result.summary.failReason));
112
- }
113
- catch (error) {
114
- writeStderr(`rainy-updates (rup): ${String(error)}\n`);
115
- setRuntimeExitCode(2);
15
+ process.exit(result.status ?? 1);
116
16
  }
17
+ await runCli();
117
18
  }
118
19
  void main();
119
- async function readPackageVersion() {
120
- try {
121
- const packageJson = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
122
- return packageJson.version ?? "0.0.0";
123
- }
124
- catch {
125
- return "0.0.0";
126
- }
127
- }
128
- function resolveExitCode(result, failReason) {
129
- if (result.errors.length > 0)
130
- return 2;
131
- if (failReason !== "none")
132
- return 1;
133
- return 0;
134
- }
@@ -108,6 +108,12 @@ export async function handleDirectCommand(parsed) {
108
108
  setRuntimeExitCode(result.ready ? 0 : 1);
109
109
  return true;
110
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);
115
+ return true;
116
+ }
111
117
  if (parsed.options.interactive &&
112
118
  (parsed.command === "check" ||
113
119
  parsed.command === "upgrade" ||
package/dist/bin/help.js CHANGED
@@ -40,6 +40,11 @@ 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
44
49
  --plan-file <path>
45
50
  --verify none|install|test|install,test
@@ -123,6 +128,11 @@ Options:
123
128
  --cooldown-days <n>
124
129
  --pr-limit <n>
125
130
  --only-changed
131
+ --affected
132
+ --staged
133
+ --base <ref>
134
+ --head <ref>
135
+ --since <ref>
126
136
  --offline
127
137
  --concurrency <n>
128
138
  --registry-timeout-ms <n>
@@ -180,6 +190,11 @@ Scan dependencies for CVEs using OSV.dev and GitHub Advisory Database.
180
190
 
181
191
  Options:
182
192
  --workspace
193
+ --affected
194
+ --staged
195
+ --base <ref>
196
+ --head <ref>
197
+ --since <ref>
183
198
  --severity critical|high|medium|low
184
199
  --summary
185
200
  --report table|summary|json
@@ -200,6 +215,12 @@ Use it to inspect risk, security, peer, license, and policy context before apply
200
215
 
201
216
  Options:
202
217
  --workspace
218
+ --only-changed
219
+ --affected
220
+ --staged
221
+ --base <ref>
222
+ --head <ref>
223
+ --since <ref>
203
224
  --interactive
204
225
  --security-only
205
226
  --risk critical|high|medium|low
@@ -220,6 +241,12 @@ Produce a fast summary verdict and point the operator to review when action is n
220
241
 
221
242
  Options:
222
243
  --workspace
244
+ --only-changed
245
+ --affected
246
+ --staged
247
+ --base <ref>
248
+ --head <ref>
249
+ --since <ref>
223
250
  --verdict-only
224
251
  --include-changelog
225
252
  --json-file <path>`;
@@ -231,6 +258,13 @@ Open the primary interactive dependency operations console.
231
258
 
232
259
  Options:
233
260
  --workspace
261
+ --only-changed
262
+ --affected
263
+ --staged
264
+ --base <ref>
265
+ --head <ref>
266
+ --since <ref>
267
+ --view dependencies|security|health
234
268
  --mode check|review|upgrade
235
269
  --focus all|security|risk|major|blocked|workspace
236
270
  --apply-selected
@@ -248,6 +282,14 @@ Audit release and CI readiness for Rainy Updates.
248
282
  Options:
249
283
  --workspace
250
284
  --json-file <path>
285
+ --cwd <path>`;
286
+ }
287
+ if (isCommand && command === "hook") {
288
+ return `rainy-updates hook <install|uninstall|doctor> [options]
289
+
290
+ Install, remove, or inspect Rainy-managed git hooks.
291
+
292
+ Options:
251
293
  --cwd <path>`;
252
294
  }
253
295
  return `rainy-updates (rup / rainy-up) <command> [options]
@@ -270,6 +312,7 @@ Commands:
270
312
  licenses Scan dependency licenses and generate SPDX SBOM
271
313
  snapshot Save, list, restore, and diff dependency state snapshots
272
314
  ga Audit GA and CI readiness for this checkout
315
+ hook Install or inspect Rainy-managed git hooks
273
316
 
274
317
  Global options:
275
318
  --cwd <path>
@@ -288,6 +331,11 @@ Global options:
288
331
  --cooldown-days <n>
289
332
  --pr-limit <n>
290
333
  --only-changed
334
+ --affected
335
+ --staged
336
+ --base <ref>
337
+ --head <ref>
338
+ --since <ref>
291
339
  --interactive
292
340
  --show-impact
293
341
  --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
+ }
@@ -12,6 +12,11 @@ export function parseAuditArgs(args) {
12
12
  const options = {
13
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;
@@ -1,5 +1,5 @@
1
1
  import { collectDependencies, readManifest, } from "../../parsers/package-json.js";
2
- import { detectPackageManager as detectProjectPackageManager, resolvePackageManager, } from "../../pm/detect.js";
2
+ import { buildAddInvocation, createPackageManagerProfile, detectPackageManagerDetails, } from "../../pm/detect.js";
3
3
  import { discoverPackageDirs } from "../../workspace/discover.js";
4
4
  import { writeFileAtomic } from "../../utils/io.js";
5
5
  import { stableStringify } from "../../utils/stable-json.js";
@@ -28,7 +28,15 @@ export async function runAudit(options) {
28
28
  unresolved: 0,
29
29
  },
30
30
  };
31
- const packageDirs = await discoverPackageDirs(options.cwd, options.workspace);
31
+ const packageDirs = await discoverPackageDirs(options.cwd, options.workspace, {
32
+ git: options,
33
+ includeKinds: [
34
+ "dependencies",
35
+ "devDependencies",
36
+ "optionalDependencies",
37
+ ],
38
+ includeDependents: options.affected === true,
39
+ });
32
40
  const depsByDir = new Map();
33
41
  for (const dir of packageDirs) {
34
42
  let manifest;
@@ -121,10 +129,10 @@ async function applyFix(advisories, options) {
121
129
  const patchMap = buildPatchMap(advisories);
122
130
  if (patchMap.size === 0)
123
131
  return;
124
- const detected = await detectProjectPackageManager(options.cwd);
125
- const pm = resolvePackageManager(options.packageManager, detected);
126
- const installArgs = buildInstallArgs(pm, patchMap);
127
- const installCmd = `${pm} ${installArgs.join(" ")}`;
132
+ const detected = await detectPackageManagerDetails(options.cwd);
133
+ const profile = createPackageManagerProfile(options.packageManager, detected);
134
+ const install = buildInstallArgs(profile, patchMap);
135
+ const installCmd = install.display;
128
136
  if (options.dryRun) {
129
137
  if (!options.silent) {
130
138
  writeStderr(`[audit] --dry-run: would execute:\n ${installCmd}\n`);
@@ -140,7 +148,7 @@ async function applyFix(advisories, options) {
140
148
  writeStderr(` → ${installCmd}\n`);
141
149
  }
142
150
  try {
143
- await runCommand(pm, installArgs, options.cwd);
151
+ await runCommand(install.command, install.args, options.cwd);
144
152
  }
145
153
  catch (err) {
146
154
  if (!options.silent) {
@@ -158,18 +166,9 @@ async function applyFix(advisories, options) {
158
166
  writeStderr(`[audit] Tip: run with --commit to automatically commit the changes.\n`);
159
167
  }
160
168
  }
161
- function buildInstallArgs(pm, patchMap) {
169
+ function buildInstallArgs(profile, patchMap) {
162
170
  const packages = [...patchMap.entries()].map(([n, v]) => `${n}@${v}`);
163
- switch (pm) {
164
- case "pnpm":
165
- return ["add", ...packages];
166
- case "bun":
167
- return ["add", ...packages];
168
- case "yarn":
169
- return ["add", ...packages];
170
- default:
171
- return ["install", ...packages]; // npm
172
- }
171
+ return buildAddInvocation(profile, packages);
173
172
  }
174
173
  async function commitFix(patchMap, cwd, silent) {
175
174
  const msg = buildCommitMessage(patchMap);