@rainy-updates/cli 0.5.4 → 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.
- package/CHANGELOG.md +136 -0
- package/README.md +5 -0
- package/dist/bin/cli.js +16 -438
- package/dist/bin/dispatch.d.ts +16 -0
- package/dist/bin/dispatch.js +150 -0
- package/dist/bin/help.d.ts +1 -0
- package/dist/bin/help.js +284 -0
- package/dist/commands/audit/runner.js +43 -26
- package/dist/commands/dashboard/parser.d.ts +2 -0
- package/dist/commands/dashboard/parser.js +59 -0
- package/dist/commands/dashboard/runner.d.ts +2 -0
- package/dist/commands/dashboard/runner.js +47 -0
- package/dist/commands/doctor/parser.js +12 -0
- package/dist/commands/doctor/runner.js +5 -2
- package/dist/commands/ga/parser.d.ts +2 -0
- package/dist/commands/ga/parser.js +50 -0
- package/dist/commands/ga/runner.d.ts +2 -0
- package/dist/commands/ga/runner.js +129 -0
- package/dist/commands/resolve/runner.js +7 -3
- package/dist/commands/review/parser.js +6 -0
- package/dist/commands/review/runner.js +4 -3
- package/dist/core/analysis/options.d.ts +6 -0
- package/dist/core/analysis/options.js +69 -0
- package/dist/core/analysis/review-items.d.ts +4 -0
- package/dist/core/analysis/review-items.js +128 -0
- package/dist/core/analysis/run-silenced.d.ts +1 -0
- package/dist/core/analysis/run-silenced.js +14 -0
- package/dist/core/analysis-bundle.d.ts +4 -0
- package/dist/core/analysis-bundle.js +33 -0
- package/dist/core/artifacts.d.ts +3 -0
- package/dist/core/artifacts.js +48 -0
- package/dist/core/check.js +6 -1
- package/dist/core/doctor/findings.d.ts +2 -0
- package/dist/core/doctor/findings.js +166 -0
- package/dist/core/doctor/render.d.ts +3 -0
- package/dist/core/doctor/render.js +44 -0
- package/dist/core/doctor/result.d.ts +2 -0
- package/dist/core/doctor/result.js +55 -0
- package/dist/core/doctor/score.d.ts +5 -0
- package/dist/core/doctor/score.js +28 -0
- package/dist/core/options.d.ts +7 -1
- package/dist/core/options.js +14 -0
- package/dist/core/review-model.d.ts +3 -3
- package/dist/core/review-model.js +55 -245
- package/dist/core/review-verdict.d.ts +2 -0
- package/dist/core/review-verdict.js +14 -0
- package/dist/core/summary.js +19 -0
- package/dist/output/format.js +22 -0
- package/dist/output/github.js +12 -0
- package/dist/output/sarif.js +16 -0
- package/dist/types/index.d.ts +120 -0
- package/dist/ui/dashboard/DashboardTUI.d.ts +6 -0
- package/dist/ui/dashboard/DashboardTUI.js +34 -0
- package/dist/ui/dashboard/components/DetailPanel.d.ts +4 -0
- package/dist/ui/dashboard/components/DetailPanel.js +30 -0
- package/dist/ui/dashboard/components/Footer.d.ts +4 -0
- package/dist/ui/dashboard/components/Footer.js +9 -0
- package/dist/ui/dashboard/components/Header.d.ts +4 -0
- package/dist/ui/dashboard/components/Header.js +12 -0
- package/dist/ui/dashboard/components/Sidebar.d.ts +4 -0
- package/dist/ui/dashboard/components/Sidebar.js +23 -0
- package/dist/ui/dashboard/store.d.ts +34 -0
- package/dist/ui/dashboard/store.js +148 -0
- package/dist/ui/tui.d.ts +2 -2
- package/dist/ui/tui.js +310 -79
- package/package.json +1 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function buildDoctorFindings(review) {
|
|
3
|
+
const findings = [];
|
|
4
|
+
for (const error of review.errors) {
|
|
5
|
+
findings.push({
|
|
6
|
+
id: `execution-error:${error}`,
|
|
7
|
+
code: "execution-error",
|
|
8
|
+
category: "Registry / Execution",
|
|
9
|
+
severity: "error",
|
|
10
|
+
scope: "project",
|
|
11
|
+
summary: error,
|
|
12
|
+
details: "Execution errors make the scan incomplete or require operator review.",
|
|
13
|
+
help: "Resolve execution and registry issues before treating the run as clean.",
|
|
14
|
+
recommendedAction: "Run `rup review --interactive` after fixing execution failures.",
|
|
15
|
+
evidence: [error],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
for (const degradedSource of review.summary.degradedSources ?? []) {
|
|
19
|
+
findings.push({
|
|
20
|
+
id: `degraded-source:${degradedSource}`,
|
|
21
|
+
code: "degraded-advisory-source",
|
|
22
|
+
category: "Registry / Execution",
|
|
23
|
+
severity: "warning",
|
|
24
|
+
scope: "project",
|
|
25
|
+
summary: `${degradedSource} returned degraded advisory coverage.`,
|
|
26
|
+
details: "Security findings may be partial while an advisory source is degraded.",
|
|
27
|
+
help: "Retry the scan or pin the advisory source when full coverage matters.",
|
|
28
|
+
recommendedAction: "Re-run `rup doctor` or `rup audit --source` once the degraded source recovers.",
|
|
29
|
+
evidence: [degradedSource],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
for (const item of review.items) {
|
|
33
|
+
const workspace = path.basename(item.update.packagePath);
|
|
34
|
+
if (item.update.peerConflictSeverity === "error" || item.update.peerConflictSeverity === "warning") {
|
|
35
|
+
findings.push({
|
|
36
|
+
id: `peer-conflict:${item.update.name}:${workspace}`,
|
|
37
|
+
code: "peer-conflict",
|
|
38
|
+
category: "Compatibility",
|
|
39
|
+
severity: item.update.peerConflictSeverity === "error" ? "error" : "warning",
|
|
40
|
+
scope: "package",
|
|
41
|
+
packageName: item.update.name,
|
|
42
|
+
workspace,
|
|
43
|
+
summary: `${item.update.name} has ${item.update.peerConflictSeverity} peer conflicts after the proposed upgrade.`,
|
|
44
|
+
details: item.peerConflicts[0]?.suggestion,
|
|
45
|
+
help: "Inspect peer dependency requirements before applying the update.",
|
|
46
|
+
recommendedAction: item.update.recommendedAction ?? "Review peer requirements in `rup review --interactive`.",
|
|
47
|
+
evidence: item.peerConflicts.map((conflict) => `${conflict.requester} -> ${conflict.peer}@${conflict.requiredRange}`),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (item.update.licenseStatus === "denied") {
|
|
51
|
+
findings.push({
|
|
52
|
+
id: `license-denied:${item.update.name}:${workspace}`,
|
|
53
|
+
code: "license-policy-denied",
|
|
54
|
+
category: "Licensing",
|
|
55
|
+
severity: "error",
|
|
56
|
+
scope: "package",
|
|
57
|
+
packageName: item.update.name,
|
|
58
|
+
workspace,
|
|
59
|
+
summary: `${item.update.name} violates the current license policy.`,
|
|
60
|
+
details: item.license?.license,
|
|
61
|
+
help: "Keep denied licenses out of the approved update set.",
|
|
62
|
+
recommendedAction: item.update.recommendedAction ?? "Block this package in `rup review --interactive`.",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if ((item.update.advisoryCount ?? 0) > 0) {
|
|
66
|
+
findings.push({
|
|
67
|
+
id: `security-advisory:${item.update.name}:${workspace}`,
|
|
68
|
+
code: "security-advisory",
|
|
69
|
+
category: "Security",
|
|
70
|
+
severity: item.update.riskLevel === "critical" ? "error" : "warning",
|
|
71
|
+
scope: "package",
|
|
72
|
+
packageName: item.update.name,
|
|
73
|
+
workspace,
|
|
74
|
+
summary: `${item.update.name} has ${item.update.advisoryCount} known security advisories.`,
|
|
75
|
+
details: item.advisories[0]?.title,
|
|
76
|
+
help: "Prioritize secure minimum upgrades before applying other dependency changes.",
|
|
77
|
+
recommendedAction: item.update.recommendedAction ??
|
|
78
|
+
"Run `rup review --security-only` and consider `rup audit --fix` for minimum safe patches.",
|
|
79
|
+
evidence: item.advisories.map((advisory) => advisory.cveId),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (item.update.riskLevel === "critical" || item.update.riskLevel === "high") {
|
|
83
|
+
findings.push({
|
|
84
|
+
id: `release-risk:${item.update.name}:${workspace}`,
|
|
85
|
+
code: item.update.riskLevel === "critical" ? "release-risk-critical" : "release-risk-high",
|
|
86
|
+
category: "Release Risk",
|
|
87
|
+
severity: item.update.riskLevel === "critical" ? "error" : "warning",
|
|
88
|
+
scope: "package",
|
|
89
|
+
packageName: item.update.name,
|
|
90
|
+
workspace,
|
|
91
|
+
summary: `${item.update.name} is classified as ${item.update.riskLevel} release risk.`,
|
|
92
|
+
details: item.update.riskReasons?.[0],
|
|
93
|
+
help: "Use review mode to inspect why the update was classified as risky before applying it.",
|
|
94
|
+
recommendedAction: item.update.recommendedAction ?? "Keep this package in review until risk reasons are cleared.",
|
|
95
|
+
evidence: item.update.riskReasons,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (item.update.diffType === "major") {
|
|
99
|
+
findings.push({
|
|
100
|
+
id: `major-upgrade:${item.update.name}:${workspace}`,
|
|
101
|
+
code: "major-upgrade",
|
|
102
|
+
category: "Release Risk",
|
|
103
|
+
severity: "warning",
|
|
104
|
+
scope: "package",
|
|
105
|
+
packageName: item.update.name,
|
|
106
|
+
workspace,
|
|
107
|
+
summary: `${item.update.name} is a major version upgrade.`,
|
|
108
|
+
help: "Major upgrades should be reviewed explicitly before being applied.",
|
|
109
|
+
recommendedAction: item.update.recommendedAction ?? "Review major changes in `rup review --interactive`.",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (item.update.healthStatus === "stale" || item.update.healthStatus === "archived") {
|
|
113
|
+
findings.push({
|
|
114
|
+
id: `health:${item.update.name}:${workspace}`,
|
|
115
|
+
code: item.update.healthStatus === "archived" ? "package-archived" : "package-stale",
|
|
116
|
+
category: "Operational Health",
|
|
117
|
+
severity: "warning",
|
|
118
|
+
scope: "package",
|
|
119
|
+
packageName: item.update.name,
|
|
120
|
+
workspace,
|
|
121
|
+
summary: `${item.update.name} is flagged as ${item.update.healthStatus}.`,
|
|
122
|
+
help: "Monitor package health and plan alternatives if maintenance does not improve.",
|
|
123
|
+
recommendedAction: item.update.monitorReason ?? "Keep this package under monitoring in `rup review`.",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (item.unusedIssues.length > 0) {
|
|
127
|
+
findings.push({
|
|
128
|
+
id: `unused:${item.update.name}:${workspace}`,
|
|
129
|
+
code: "unused-dependency-signal",
|
|
130
|
+
category: "Unused / Cleanup",
|
|
131
|
+
severity: "warning",
|
|
132
|
+
scope: "package",
|
|
133
|
+
packageName: item.update.name,
|
|
134
|
+
workspace,
|
|
135
|
+
summary: `${item.update.name} has unused or missing dependency signals.`,
|
|
136
|
+
details: item.unusedIssues[0]?.kind,
|
|
137
|
+
help: "Clean up unused or missing dependencies before widening the upgrade scope.",
|
|
138
|
+
recommendedAction: "Run `rup unused` to inspect cleanup opportunities.",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return findings.sort(compareDoctorFindings);
|
|
143
|
+
}
|
|
144
|
+
function compareDoctorFindings(left, right) {
|
|
145
|
+
const severityOrder = { error: 0, warning: 1 };
|
|
146
|
+
const categoryOrder = {
|
|
147
|
+
Security: 0,
|
|
148
|
+
Compatibility: 1,
|
|
149
|
+
Policy: 2,
|
|
150
|
+
Licensing: 3,
|
|
151
|
+
"Release Risk": 4,
|
|
152
|
+
"Operational Health": 5,
|
|
153
|
+
"Unused / Cleanup": 6,
|
|
154
|
+
"Workspace Integrity": 7,
|
|
155
|
+
"Registry / Execution": 8,
|
|
156
|
+
};
|
|
157
|
+
const bySeverity = severityOrder[left.severity] - severityOrder[right.severity];
|
|
158
|
+
if (bySeverity !== 0)
|
|
159
|
+
return bySeverity;
|
|
160
|
+
const byCategory = categoryOrder[left.category] - categoryOrder[right.category];
|
|
161
|
+
if (byCategory !== 0)
|
|
162
|
+
return byCategory;
|
|
163
|
+
const leftTarget = `${left.packageName ?? ""}:${left.workspace ?? ""}:${left.code}`;
|
|
164
|
+
const rightTarget = `${right.packageName ?? ""}:${right.workspace ?? ""}:${right.code}`;
|
|
165
|
+
return leftTarget.localeCompare(rightTarget);
|
|
166
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function renderDoctorResult(result, verdictOnly = false) {
|
|
2
|
+
const lines = [
|
|
3
|
+
`State: ${result.verdict}`,
|
|
4
|
+
`Score: ${result.score}/100 (${result.scoreLabel})`,
|
|
5
|
+
`PrimaryRisk: ${result.primaryFindings[0] ?? "No blocking findings."}`,
|
|
6
|
+
`NextAction: ${result.recommendedCommand}`,
|
|
7
|
+
];
|
|
8
|
+
if (!verdictOnly) {
|
|
9
|
+
lines.push(`Counts: updates=${result.summary.updatesFound}, security=${result.summary.securityPackages ?? 0}, risk=${result.summary.riskPackages ?? 0}, peer=${result.summary.peerConflictPackages ?? 0}, license=${result.summary.licenseViolationPackages ?? 0}`);
|
|
10
|
+
lines.push(`NextActionReason: ${result.nextActionReason}`);
|
|
11
|
+
}
|
|
12
|
+
return lines.join("\n");
|
|
13
|
+
}
|
|
14
|
+
export function renderDoctorAgentReport(result) {
|
|
15
|
+
const lines = [
|
|
16
|
+
`Rainy Updates doctor report for ${result.review.projectPath}`,
|
|
17
|
+
`State: ${result.verdict}`,
|
|
18
|
+
`Score: ${result.score}/100 (${result.scoreLabel})`,
|
|
19
|
+
`PrimaryRisk: ${result.primaryFindings[0] ?? "No blocking findings."}`,
|
|
20
|
+
`NextAction: ${result.recommendedCommand}`,
|
|
21
|
+
`Why: ${result.nextActionReason}`,
|
|
22
|
+
"",
|
|
23
|
+
"Priority findings:",
|
|
24
|
+
];
|
|
25
|
+
const findings = result.findings.slice(0, 8);
|
|
26
|
+
if (findings.length === 0) {
|
|
27
|
+
lines.push("- No blocking findings detected.");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
for (const finding of findings) {
|
|
31
|
+
const target = finding.packageName ? ` package=${finding.packageName}` : "";
|
|
32
|
+
lines.push(`- [${finding.severity}] ${finding.category}${target}: ${finding.summary}`);
|
|
33
|
+
if (finding.recommendedAction) {
|
|
34
|
+
lines.push(` fix: ${finding.recommendedAction}`);
|
|
35
|
+
}
|
|
36
|
+
if (finding.help) {
|
|
37
|
+
lines.push(` help: ${finding.help}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
lines.push("");
|
|
42
|
+
lines.push(`Verification: ${result.recommendedCommand}`);
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { deriveReviewVerdict } from "../review-verdict.js";
|
|
2
|
+
import { buildDoctorFindings } from "./findings.js";
|
|
3
|
+
import { calculateDoctorScore, countFindingsByCategory, countFindingsBySeverity, labelDoctorScore, } from "./score.js";
|
|
4
|
+
export function createDoctorResult(review) {
|
|
5
|
+
const verdict = review.summary.verdict ?? deriveReviewVerdict(review.items, review.errors);
|
|
6
|
+
const findings = buildDoctorFindings(review);
|
|
7
|
+
const score = calculateDoctorScore(findings);
|
|
8
|
+
const scoreLabel = labelDoctorScore(score);
|
|
9
|
+
const primaryFindings = findings.length > 0
|
|
10
|
+
? findings.map((finding) => finding.summary)
|
|
11
|
+
: ["No blocking findings; remaining updates are low-risk."];
|
|
12
|
+
const recommendedCommand = recommendDoctorCommand(review, verdict);
|
|
13
|
+
const nextActionReason = describeNextActionReason(review, verdict);
|
|
14
|
+
review.summary.dependencyHealthScore = score;
|
|
15
|
+
review.summary.findingCountsByCategory = countFindingsByCategory(findings);
|
|
16
|
+
review.summary.findingCountsBySeverity = countFindingsBySeverity(findings);
|
|
17
|
+
review.summary.primaryFindingCode = findings[0]?.code;
|
|
18
|
+
review.summary.primaryFindingCategory = findings[0]?.category;
|
|
19
|
+
review.summary.nextActionReason = nextActionReason;
|
|
20
|
+
return {
|
|
21
|
+
verdict,
|
|
22
|
+
score,
|
|
23
|
+
scoreLabel,
|
|
24
|
+
summary: review.summary,
|
|
25
|
+
review,
|
|
26
|
+
findings,
|
|
27
|
+
primaryFindings,
|
|
28
|
+
recommendedCommand,
|
|
29
|
+
nextActionReason,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function recommendDoctorCommand(review, verdict) {
|
|
33
|
+
if (verdict === "blocked")
|
|
34
|
+
return "rup review --interactive";
|
|
35
|
+
if ((review.summary.securityPackages ?? 0) > 0)
|
|
36
|
+
return "rup review --security-only";
|
|
37
|
+
if (review.errors.length > 0 || review.items.length > 0)
|
|
38
|
+
return "rup review --interactive";
|
|
39
|
+
return "rup check";
|
|
40
|
+
}
|
|
41
|
+
function describeNextActionReason(review, verdict) {
|
|
42
|
+
if (verdict === "blocked") {
|
|
43
|
+
return "Blocked findings exist, so the update set needs an explicit package-by-package review before any mutation.";
|
|
44
|
+
}
|
|
45
|
+
if ((review.summary.securityPackages ?? 0) > 0) {
|
|
46
|
+
return "Security advisories are present, so the next step should focus on the secure subset first.";
|
|
47
|
+
}
|
|
48
|
+
if (review.errors.length > 0) {
|
|
49
|
+
return "Execution issues reduce trust in the current scan, so the result should be reviewed before treating it as clean.";
|
|
50
|
+
}
|
|
51
|
+
if (review.items.length > 0) {
|
|
52
|
+
return "Reviewable updates were found, so the next step is to inspect and approve the package set.";
|
|
53
|
+
}
|
|
54
|
+
return "No reviewable changes remain, so a normal check is enough to verify the repository stays clean.";
|
|
55
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DoctorFinding, DoctorFindingCategory, DoctorFindingSeverity, DoctorScoreLabel } from "../../types/index.js";
|
|
2
|
+
export declare function calculateDoctorScore(findings: DoctorFinding[]): number;
|
|
3
|
+
export declare function labelDoctorScore(score: number): DoctorScoreLabel;
|
|
4
|
+
export declare function countFindingsByCategory(findings: DoctorFinding[]): Partial<Record<DoctorFindingCategory, number>>;
|
|
5
|
+
export declare function countFindingsBySeverity(findings: DoctorFinding[]): Partial<Record<DoctorFindingSeverity, number>>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function calculateDoctorScore(findings) {
|
|
2
|
+
const uniqueErrorCodes = new Set(findings.filter((finding) => finding.severity === "error").map((finding) => finding.code));
|
|
3
|
+
const uniqueWarningCodes = new Set(findings.filter((finding) => finding.severity === "warning").map((finding) => finding.code));
|
|
4
|
+
return Math.max(0, 100 - uniqueErrorCodes.size * 12 - uniqueWarningCodes.size * 5);
|
|
5
|
+
}
|
|
6
|
+
export function labelDoctorScore(score) {
|
|
7
|
+
if (score >= 85)
|
|
8
|
+
return "Strong";
|
|
9
|
+
if (score >= 65)
|
|
10
|
+
return "Needs Review";
|
|
11
|
+
if (score >= 40)
|
|
12
|
+
return "Action Needed";
|
|
13
|
+
return "Blocked / Critical";
|
|
14
|
+
}
|
|
15
|
+
export function countFindingsByCategory(findings) {
|
|
16
|
+
const counts = {};
|
|
17
|
+
for (const finding of findings) {
|
|
18
|
+
counts[finding.category] = (counts[finding.category] ?? 0) + 1;
|
|
19
|
+
}
|
|
20
|
+
return counts;
|
|
21
|
+
}
|
|
22
|
+
export function countFindingsBySeverity(findings) {
|
|
23
|
+
const counts = {};
|
|
24
|
+
for (const finding of findings) {
|
|
25
|
+
counts[finding.severity] = (counts[finding.severity] ?? 0) + 1;
|
|
26
|
+
}
|
|
27
|
+
return counts;
|
|
28
|
+
}
|
package/dist/core/options.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaselineOptions, CheckOptions, UpgradeOptions, AuditOptions, BisectOptions, HealthOptions, UnusedOptions, ResolveOptions, LicenseOptions, SnapshotOptions, ReviewOptions, DoctorOptions, RiskLevel } from "../types/index.js";
|
|
1
|
+
import type { BaselineOptions, CheckOptions, UpgradeOptions, AuditOptions, BisectOptions, HealthOptions, UnusedOptions, ResolveOptions, LicenseOptions, SnapshotOptions, ReviewOptions, DoctorOptions, RiskLevel, DashboardOptions, GaOptions } from "../types/index.js";
|
|
2
2
|
import type { InitCiMode, InitCiSchedule } from "./init-ci.js";
|
|
3
3
|
export type ParsedCliArgs = {
|
|
4
4
|
command: "check";
|
|
@@ -52,6 +52,12 @@ export type ParsedCliArgs = {
|
|
|
52
52
|
} | {
|
|
53
53
|
command: "doctor";
|
|
54
54
|
options: DoctorOptions;
|
|
55
|
+
} | {
|
|
56
|
+
command: "dashboard";
|
|
57
|
+
options: DashboardOptions;
|
|
58
|
+
} | {
|
|
59
|
+
command: "ga";
|
|
60
|
+
options: GaOptions;
|
|
55
61
|
};
|
|
56
62
|
export declare function parseCliArgs(argv: string[]): Promise<ParsedCliArgs>;
|
|
57
63
|
export declare function ensureRiskLevel(value: string): RiskLevel;
|
package/dist/core/options.js
CHANGED
|
@@ -23,6 +23,8 @@ const KNOWN_COMMANDS = [
|
|
|
23
23
|
"snapshot",
|
|
24
24
|
"review",
|
|
25
25
|
"doctor",
|
|
26
|
+
"dashboard",
|
|
27
|
+
"ga",
|
|
26
28
|
];
|
|
27
29
|
export async function parseCliArgs(argv) {
|
|
28
30
|
const firstArg = argv[0];
|
|
@@ -72,6 +74,14 @@ export async function parseCliArgs(argv) {
|
|
|
72
74
|
const { parseDoctorArgs } = await import("../commands/doctor/parser.js");
|
|
73
75
|
return { command, options: parseDoctorArgs(args) };
|
|
74
76
|
}
|
|
77
|
+
if (command === "dashboard") {
|
|
78
|
+
const { parseDashboardArgs } = await import("../commands/dashboard/parser.js");
|
|
79
|
+
return { command, options: parseDashboardArgs(args) };
|
|
80
|
+
}
|
|
81
|
+
if (command === "ga") {
|
|
82
|
+
const { parseGaArgs } = await import("../commands/ga/parser.js");
|
|
83
|
+
return { command, options: parseGaArgs(args) };
|
|
84
|
+
}
|
|
75
85
|
const base = {
|
|
76
86
|
cwd: process.cwd(),
|
|
77
87
|
target: "latest",
|
|
@@ -450,6 +460,10 @@ export async function parseCliArgs(argv) {
|
|
|
450
460
|
base.showImpact = true;
|
|
451
461
|
continue;
|
|
452
462
|
}
|
|
463
|
+
if (current === "--show-links") {
|
|
464
|
+
base.showHomepage = true;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
453
467
|
if (current === "--show-homepage") {
|
|
454
468
|
base.showHomepage = true;
|
|
455
469
|
continue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CheckOptions, DoctorOptions,
|
|
1
|
+
import type { CheckOptions, DoctorOptions, ReviewOptions, ReviewResult } from "../types/index.js";
|
|
2
|
+
export { createDoctorResult } from "./doctor/result.js";
|
|
3
|
+
export { renderDoctorAgentReport, renderDoctorResult, } from "./doctor/render.js";
|
|
2
4
|
export declare function buildReviewResult(options: ReviewOptions | DoctorOptions | CheckOptions): Promise<ReviewResult>;
|
|
3
|
-
export declare function createDoctorResult(review: ReviewResult): DoctorResult;
|
|
4
5
|
export declare function renderReviewResult(review: ReviewResult): string;
|
|
5
|
-
export declare function renderDoctorResult(result: DoctorResult, verdictOnly?: boolean): string;
|