@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/dist/index.d.ts CHANGED
@@ -4,9 +4,10 @@ export { warmCache } from "./core/warm-cache.js";
4
4
  export { runCi } from "./core/ci.js";
5
5
  export { initCiWorkflow } from "./core/init-ci.js";
6
6
  export { saveBaseline, diffBaseline } from "./core/baseline.js";
7
+ export { runHook } from "./commands/hook/runner.js";
7
8
  export { createSarifReport } from "./output/sarif.js";
8
9
  export { writeGitHubOutput, renderGitHubAnnotations } from "./output/github.js";
9
10
  export { renderPrReport } from "./output/pr-report.js";
10
11
  export { buildReviewResult, createDoctorResult } from "./core/review-model.js";
11
12
  export { applyRiskAssessments } from "./risk/index.js";
12
- export type { CheckOptions, CheckResult, CiProfile, DependencyKind, FailOnLevel, GroupBy, OutputFormat, PackageUpdate, RunOptions, TargetLevel, UpgradeOptions, UpgradeResult, ReviewOptions, ReviewResult, DoctorOptions, DoctorResult, Verdict, RiskLevel, RiskCategory, RiskAssessment, RiskFactor, MaintainerChurnStatus, } from "./types/index.js";
13
+ export type { CheckOptions, CheckResult, CiProfile, DependencyKind, FailOnLevel, GroupBy, OutputFormat, PackageUpdate, RunOptions, TargetLevel, UpgradeOptions, UpgradeResult, ReviewOptions, ReviewResult, DoctorOptions, DoctorResult, HookOptions, HookResult, Verdict, RiskLevel, RiskCategory, RiskAssessment, RiskFactor, MaintainerChurnStatus, } from "./types/index.js";
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export { warmCache } from "./core/warm-cache.js";
4
4
  export { runCi } from "./core/ci.js";
5
5
  export { initCiWorkflow } from "./core/init-ci.js";
6
6
  export { saveBaseline, diffBaseline } from "./core/baseline.js";
7
+ export { runHook } from "./commands/hook/runner.js";
7
8
  export { createSarifReport } from "./output/sarif.js";
8
9
  export { writeGitHubOutput, renderGitHubAnnotations } from "./output/github.js";
9
10
  export { renderPrReport } from "./output/pr-report.js";
@@ -1,4 +1,4 @@
1
- import { readFileSync } from "node:fs";
1
+ import { CLI_VERSION } from "../generated/version.js";
2
2
  export function createSarifReport(result) {
3
3
  const dependencyRuleId = "rainy-updates/dependency-update";
4
4
  const runtimeRuleId = "rainy-updates/runtime-error";
@@ -118,12 +118,6 @@ let TOOL_VERSION_CACHE = null;
118
118
  function getToolVersion() {
119
119
  if (TOOL_VERSION_CACHE)
120
120
  return TOOL_VERSION_CACHE;
121
- try {
122
- const packageJson = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
123
- TOOL_VERSION_CACHE = packageJson.version ?? "0.0.0";
124
- }
125
- catch {
126
- TOOL_VERSION_CACHE = "0.0.0";
127
- }
121
+ TOOL_VERSION_CACHE = CLI_VERSION;
128
122
  return TOOL_VERSION_CACHE;
129
123
  }
@@ -1,3 +1,40 @@
1
1
  import type { DetectedPackageManager, SelectedPackageManager, SupportedPackageManager } from "../types/index.js";
2
+ export type PackageManagerDetectionSource = "packageManager-field" | "lockfile" | "fallback";
3
+ export type YarnFlavor = "classic" | "berry" | "unknown";
4
+ export interface PackageManagerDetection {
5
+ manager: DetectedPackageManager;
6
+ source: PackageManagerDetectionSource;
7
+ lockfile?: string;
8
+ packageManagerField?: string;
9
+ yarnFlavor?: YarnFlavor;
10
+ }
11
+ export interface PackageManagerProfile {
12
+ manager: SupportedPackageManager;
13
+ command: SupportedPackageManager;
14
+ source: PackageManagerDetectionSource;
15
+ lockfile?: string;
16
+ packageManagerField?: string;
17
+ yarnFlavor?: YarnFlavor;
18
+ }
2
19
  export declare function detectPackageManager(cwd: string): Promise<DetectedPackageManager>;
20
+ export declare function detectPackageManagerDetails(cwd: string): Promise<PackageManagerDetection>;
3
21
  export declare function resolvePackageManager(requested: SelectedPackageManager, detected: DetectedPackageManager, fallback?: SupportedPackageManager): SupportedPackageManager;
22
+ export declare function resolvePackageManagerProfile(cwd: string, requested: SelectedPackageManager, fallback?: SupportedPackageManager): Promise<PackageManagerProfile>;
23
+ export declare function createPackageManagerProfile(requested: SelectedPackageManager, detected: PackageManagerDetection, fallback?: SupportedPackageManager): PackageManagerProfile;
24
+ export declare function buildInstallInvocation(profile: PackageManagerProfile, options?: {
25
+ frozen?: boolean;
26
+ ci?: boolean;
27
+ }): {
28
+ command: string;
29
+ args: string[];
30
+ display: string;
31
+ };
32
+ export declare function buildAddInvocation(profile: PackageManagerProfile, packages: string[], options?: {
33
+ exact?: boolean;
34
+ noSave?: boolean;
35
+ }): {
36
+ command: string;
37
+ args: string[];
38
+ display: string;
39
+ };
40
+ export declare function buildTestCommand(profile: PackageManagerProfile): string;
package/dist/pm/detect.js CHANGED
@@ -8,12 +8,36 @@ const PACKAGE_MANAGER_LOCKFILES = [
8
8
  ["yarn.lock", "yarn"],
9
9
  ];
10
10
  export async function detectPackageManager(cwd) {
11
+ return (await detectPackageManagerDetails(cwd)).manager;
12
+ }
13
+ export async function detectPackageManagerDetails(cwd) {
14
+ const packageManagerField = await readPackageManagerField(cwd);
15
+ if (packageManagerField) {
16
+ const parsed = parsePackageManagerField(packageManagerField);
17
+ if (parsed) {
18
+ return {
19
+ manager: parsed.manager,
20
+ source: "packageManager-field",
21
+ packageManagerField,
22
+ yarnFlavor: parsed.yarnFlavor,
23
+ };
24
+ }
25
+ }
11
26
  for (const [lockfile, packageManager] of PACKAGE_MANAGER_LOCKFILES) {
12
27
  if (await fileExists(path.join(cwd, lockfile))) {
13
- return packageManager;
28
+ return {
29
+ manager: packageManager,
30
+ source: "lockfile",
31
+ lockfile,
32
+ yarnFlavor: packageManager === "yarn" ? "unknown" : undefined,
33
+ };
14
34
  }
15
35
  }
16
- return "unknown";
36
+ return {
37
+ manager: "unknown",
38
+ source: "fallback",
39
+ packageManagerField: packageManagerField ?? undefined,
40
+ };
17
41
  }
18
42
  export function resolvePackageManager(requested, detected, fallback = "npm") {
19
43
  if (requested !== "auto")
@@ -22,6 +46,80 @@ export function resolvePackageManager(requested, detected, fallback = "npm") {
22
46
  return detected;
23
47
  return fallback;
24
48
  }
49
+ export async function resolvePackageManagerProfile(cwd, requested, fallback = "npm") {
50
+ const detected = await detectPackageManagerDetails(cwd);
51
+ return createPackageManagerProfile(requested, detected, fallback);
52
+ }
53
+ export function createPackageManagerProfile(requested, detected, fallback = "npm") {
54
+ const manager = resolvePackageManager(requested, detected.manager, fallback);
55
+ return {
56
+ manager,
57
+ command: manager,
58
+ source: requested === "auto" && detected.manager !== "unknown"
59
+ ? detected.source
60
+ : "fallback",
61
+ lockfile: detected.lockfile,
62
+ packageManagerField: detected.packageManagerField,
63
+ yarnFlavor: manager === "yarn" ? detected.yarnFlavor ?? inferYarnFlavor(undefined) : undefined,
64
+ };
65
+ }
66
+ export function buildInstallInvocation(profile, options = {}) {
67
+ const args = profile.manager === "bun"
68
+ ? ["install", ...(options.frozen ? ["--frozen-lockfile"] : [])]
69
+ : profile.manager === "pnpm"
70
+ ? ["install", ...(options.frozen ? ["--frozen-lockfile"] : [])]
71
+ : profile.manager === "yarn"
72
+ ? [
73
+ "install",
74
+ ...(options.frozen
75
+ ? [
76
+ profile.yarnFlavor === "berry"
77
+ ? "--immutable"
78
+ : "--frozen-lockfile",
79
+ ]
80
+ : []),
81
+ ]
82
+ : options.ci || options.frozen
83
+ ? ["ci"]
84
+ : ["install"];
85
+ return {
86
+ command: profile.command,
87
+ args,
88
+ display: [profile.command, ...args].join(" "),
89
+ };
90
+ }
91
+ export function buildAddInvocation(profile, packages, options = {}) {
92
+ const args = profile.manager === "bun"
93
+ ? [
94
+ "add",
95
+ ...(options.exact ? ["--exact"] : []),
96
+ ...(options.noSave ? ["--no-save"] : []),
97
+ ...packages,
98
+ ]
99
+ : profile.manager === "pnpm"
100
+ ? [
101
+ "add",
102
+ ...(options.exact ? ["--save-exact"] : []),
103
+ ...(options.noSave ? ["--no-save"] : []),
104
+ ...packages,
105
+ ]
106
+ : profile.manager === "yarn"
107
+ ? ["add", ...(options.exact ? ["--exact"] : []), ...packages]
108
+ : [
109
+ "install",
110
+ ...(options.noSave ? ["--no-save"] : []),
111
+ ...(options.exact ? ["--save-exact"] : []),
112
+ ...packages,
113
+ ];
114
+ return {
115
+ command: profile.command,
116
+ args,
117
+ display: [profile.command, ...args].join(" "),
118
+ };
119
+ }
120
+ export function buildTestCommand(profile) {
121
+ return `${profile.command} test`;
122
+ }
25
123
  async function fileExists(filePath) {
26
124
  try {
27
125
  return await Bun.file(filePath).exists();
@@ -30,3 +128,36 @@ async function fileExists(filePath) {
30
128
  return false;
31
129
  }
32
130
  }
131
+ async function readPackageManagerField(cwd) {
132
+ const packageJsonPath = path.join(cwd, "package.json");
133
+ try {
134
+ const manifest = (await Bun.file(packageJsonPath).json());
135
+ return typeof manifest.packageManager === "string"
136
+ ? manifest.packageManager
137
+ : null;
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ function parsePackageManagerField(value) {
144
+ const trimmed = value.trim();
145
+ if (trimmed.length === 0)
146
+ return null;
147
+ const [name, version] = trimmed.split("@", 2);
148
+ if (name !== "bun" && name !== "npm" && name !== "pnpm" && name !== "yarn") {
149
+ return null;
150
+ }
151
+ return {
152
+ manager: name,
153
+ yarnFlavor: name === "yarn" ? inferYarnFlavor(version) : undefined,
154
+ };
155
+ }
156
+ function inferYarnFlavor(version) {
157
+ if (!version)
158
+ return "unknown";
159
+ const match = version.match(/^(\d+)/);
160
+ if (!match)
161
+ return "unknown";
162
+ return Number(match[1]) >= 2 ? "berry" : "classic";
163
+ }
@@ -1,2 +1,3 @@
1
1
  import type { DetectedPackageManager, SelectedPackageManager } from "../types/index.js";
2
- export declare function installDependencies(cwd: string, packageManager: SelectedPackageManager, detected: DetectedPackageManager): Promise<void>;
2
+ import { type PackageManagerDetection } from "./detect.js";
3
+ export declare function installDependencies(cwd: string, packageManager: SelectedPackageManager, detected: DetectedPackageManager | PackageManagerDetection): Promise<void>;
@@ -1,9 +1,11 @@
1
- import { resolvePackageManager } from "./detect.js";
1
+ import { buildInstallInvocation, createPackageManagerProfile, } from "./detect.js";
2
2
  export async function installDependencies(cwd, packageManager, detected) {
3
- const command = resolvePackageManager(packageManager, detected);
4
- const args = ["install"];
3
+ const detection = typeof detected === "string"
4
+ ? { manager: detected, source: "fallback" }
5
+ : detected;
6
+ const invocation = buildInstallInvocation(createPackageManagerProfile(packageManager, detection));
5
7
  try {
6
- const proc = Bun.spawn([command, ...args], {
8
+ const proc = Bun.spawn([invocation.command, ...invocation.args], {
7
9
  cwd,
8
10
  stdin: "inherit",
9
11
  stdout: "inherit",
@@ -11,7 +13,7 @@ export async function installDependencies(cwd, packageManager, detected) {
11
13
  });
12
14
  const code = await proc.exited;
13
15
  if (code !== 0) {
14
- throw new Error(`${command} ${args.join(" ")} failed with exit code ${code}`);
16
+ throw new Error(`${invocation.display} failed with exit code ${code}`);
15
17
  }
16
18
  }
17
19
  catch (err) {
package/dist/rup CHANGED
Binary file
@@ -60,6 +60,11 @@ export interface RunOptions {
60
60
  cooldownDays?: number;
61
61
  prLimit?: number;
62
62
  onlyChanged: boolean;
63
+ affected?: boolean;
64
+ staged?: boolean;
65
+ baseRef?: string;
66
+ headRef?: string;
67
+ sinceRef?: string;
63
68
  ciProfile: CiProfile;
64
69
  lockfileMode: LockfileMode;
65
70
  interactive: boolean;
@@ -295,6 +300,7 @@ export interface UpgradeResult extends CheckResult {
295
300
  export interface PackageManifest {
296
301
  name?: string;
297
302
  version?: string;
303
+ packageManager?: string;
298
304
  dependencies?: Record<string, string>;
299
305
  devDependencies?: Record<string, string>;
300
306
  optionalDependencies?: Record<string, string>;
@@ -320,6 +326,11 @@ export type AuditSourceStatusLevel = "ok" | "partial" | "failed";
320
326
  export interface AuditOptions {
321
327
  cwd: string;
322
328
  workspace: boolean;
329
+ affected?: boolean;
330
+ staged?: boolean;
331
+ baseRef?: string;
332
+ headRef?: string;
333
+ sinceRef?: string;
323
334
  severity?: AuditSeverity;
324
335
  fix: boolean;
325
336
  dryRun: boolean;
@@ -396,6 +407,11 @@ export type HealthFlag = "stale" | "deprecated" | "archived" | "unmaintained";
396
407
  export interface HealthOptions {
397
408
  cwd: string;
398
409
  workspace: boolean;
410
+ affected?: boolean;
411
+ staged?: boolean;
412
+ baseRef?: string;
413
+ headRef?: string;
414
+ sinceRef?: string;
399
415
  staleDays: number;
400
416
  includeDeprecated: boolean;
401
417
  includeAlternatives: boolean;
@@ -441,6 +457,11 @@ export interface PeerConflict {
441
457
  export interface ResolveOptions {
442
458
  cwd: string;
443
459
  workspace: boolean;
460
+ affected?: boolean;
461
+ staged?: boolean;
462
+ baseRef?: string;
463
+ headRef?: string;
464
+ sinceRef?: string;
444
465
  afterUpdate: boolean;
445
466
  safe: boolean;
446
467
  jsonFile?: string;
@@ -569,6 +590,11 @@ export interface UnusedDependency {
569
590
  export interface UnusedOptions {
570
591
  cwd: string;
571
592
  workspace: boolean;
593
+ affected?: boolean;
594
+ staged?: boolean;
595
+ baseRef?: string;
596
+ headRef?: string;
597
+ sinceRef?: string;
572
598
  srcDirs: string[];
573
599
  includeDevDependencies: boolean;
574
600
  fix: boolean;
@@ -617,6 +643,11 @@ export interface SbomRelationship {
617
643
  export interface LicenseOptions {
618
644
  cwd: string;
619
645
  workspace: boolean;
646
+ affected?: boolean;
647
+ staged?: boolean;
648
+ baseRef?: string;
649
+ headRef?: string;
650
+ sinceRef?: string;
620
651
  allow?: string[];
621
652
  deny?: string[];
622
653
  sbomFile?: string;
@@ -644,6 +675,11 @@ export type SnapshotAction = "save" | "list" | "restore" | "diff";
644
675
  export interface SnapshotOptions {
645
676
  cwd: string;
646
677
  workspace: boolean;
678
+ affected?: boolean;
679
+ staged?: boolean;
680
+ baseRef?: string;
681
+ headRef?: string;
682
+ sinceRef?: string;
647
683
  action: SnapshotAction;
648
684
  label?: string;
649
685
  snapshotId?: string;
@@ -666,13 +702,35 @@ export interface SnapshotResult {
666
702
  errors: string[];
667
703
  warnings: string[];
668
704
  }
705
+ export type HookAction = "install" | "uninstall" | "doctor";
706
+ export interface HookOptions {
707
+ cwd: string;
708
+ action: HookAction;
709
+ }
710
+ export interface HookResult {
711
+ action: HookAction;
712
+ hookDir?: string;
713
+ installed: string[];
714
+ removed: string[];
715
+ checked: Array<{
716
+ name: "pre-commit" | "pre-push";
717
+ status: "managed" | "missing" | "foreign";
718
+ }>;
719
+ errors: string[];
720
+ warnings: string[];
721
+ }
669
722
  export interface GaOptions {
670
723
  cwd: string;
671
724
  workspace: boolean;
725
+ affected?: boolean;
726
+ staged?: boolean;
727
+ baseRef?: string;
728
+ headRef?: string;
729
+ sinceRef?: string;
672
730
  jsonFile?: string;
673
731
  }
674
732
  export interface GaCheck {
675
- name: "package-manager" | "workspace-discovery" | "lockfile" | "cache-backend" | "dist-build" | "runtime-artifacts" | "benchmark-gates" | "docs-contract";
733
+ name: "package-manager" | "workspace-discovery" | "lockfile" | "cache-backend" | "dist-build" | "runtime-artifacts" | "automation-entrypoints" | "platform-support" | "benchmark-gates" | "docs-contract";
676
734
  status: "pass" | "warn" | "fail";
677
735
  detail: string;
678
736
  }
@@ -0,0 +1,7 @@
1
+ import type { DashboardOptions } from "../types/index.js";
2
+ export declare const FILTER_ORDER: readonly ["all", "security", "risky", "major", "peer-conflict", "license", "unused", "blocked"];
3
+ export declare const DETAIL_TABS: readonly ["overview", "risk", "security", "peer", "license", "health", "changelog"];
4
+ export type DashboardFilterKey = (typeof FILTER_ORDER)[number];
5
+ export type DashboardDetailTab = (typeof DETAIL_TABS)[number];
6
+ export declare function deriveDashboardInitialFilter(options: Pick<DashboardOptions, "focus" | "view">): DashboardFilterKey;
7
+ export declare function deriveDashboardInitialTab(options: Pick<DashboardOptions, "focus" | "view">): DashboardDetailTab;
@@ -0,0 +1,44 @@
1
+ export const FILTER_ORDER = [
2
+ "all",
3
+ "security",
4
+ "risky",
5
+ "major",
6
+ "peer-conflict",
7
+ "license",
8
+ "unused",
9
+ "blocked",
10
+ ];
11
+ export const DETAIL_TABS = [
12
+ "overview",
13
+ "risk",
14
+ "security",
15
+ "peer",
16
+ "license",
17
+ "health",
18
+ "changelog",
19
+ ];
20
+ export function deriveDashboardInitialFilter(options) {
21
+ if (options.focus === "security")
22
+ return "security";
23
+ if (options.focus === "risk")
24
+ return "risky";
25
+ if (options.focus === "major")
26
+ return "major";
27
+ if (options.focus === "blocked")
28
+ return "blocked";
29
+ if (options.view === "security")
30
+ return "security";
31
+ if (options.view === "health")
32
+ return "risky";
33
+ return "all";
34
+ }
35
+ export function deriveDashboardInitialTab(options) {
36
+ if (options.focus === "security" || options.view === "security") {
37
+ return "security";
38
+ }
39
+ if (options.focus === "risk")
40
+ return "risk";
41
+ if (options.view === "health")
42
+ return "health";
43
+ return "overview";
44
+ }
package/dist/ui/tui.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import type { ReviewItem } from "../types/index.js";
2
+ import { type DashboardDetailTab, type DashboardFilterKey } from "./dashboard-state.js";
2
3
  export declare function runTui(items: ReviewItem[], options?: {
3
4
  title?: string;
4
5
  subtitle?: string;
6
+ initialFilter?: DashboardFilterKey;
7
+ initialTab?: DashboardDetailTab;
5
8
  }): Promise<ReviewItem[]>;