codex-plugin-doctor 1.43.0 → 1.44.0
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/README.md +11 -7
- package/dist/core/validation-baseline.d.ts +17 -0
- package/dist/core/validation-baseline.js +83 -0
- package/dist/domain/types.d.ts +9 -0
- package/dist/reporting/render-json-report.js +5 -1
- package/dist/reporting/render-markdown-report.js +4 -1
- package/dist/reporting/render-text-report.js +4 -1
- package/dist/run-cli.js +59 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -287,8 +287,10 @@ codex-plugin-doctor check . --ascii
|
|
|
287
287
|
codex-plugin-doctor check . --no-animations
|
|
288
288
|
codex-plugin-doctor check . --runtime
|
|
289
289
|
codex-plugin-doctor check . --runtime --require-runtime-approval --runtime-approval-digest sha256:<approved-plan-digest>
|
|
290
|
-
codex-plugin-doctor check . --config .codex-doctor.json
|
|
291
|
-
codex-plugin-doctor
|
|
290
|
+
codex-plugin-doctor check . --config .codex-doctor.json
|
|
291
|
+
codex-plugin-doctor baseline create . --output .codex-doctor-baseline.json
|
|
292
|
+
codex-plugin-doctor check . --baseline .codex-doctor-baseline.json
|
|
293
|
+
codex-plugin-doctor check . --history validation-history.jsonl
|
|
292
294
|
codex-plugin-doctor history validation-history.jsonl
|
|
293
295
|
codex-plugin-doctor history validation-history.jsonl --json
|
|
294
296
|
codex-plugin-doctor history validation-history.jsonl --fail-on-regression
|
|
@@ -334,9 +336,11 @@ codex-plugin-doctor check . --json --runtime --verbose-runtime
|
|
|
334
336
|
|
|
335
337
|
`check --badge-json` emits Shields endpoint-compatible JSON such as `{"schemaVersion":1,"label":"doctor","message":"PASS","color":"brightgreen"}`. `check --badge-markdown` emits a static shields.io Markdown badge for README or release notes. Badge output is intentionally limited to single package checks, not `check --installed`.
|
|
336
338
|
|
|
337
|
-
`check --history <path>` appends a compact JSONL validation snapshot after a single package check. `history <path>` reads the JSONL file and compares the latest run to the previous run, including status, finding-count deltas, and whether the latest run regressed. Add `history --json` for automation output or `history --fail-on-regression` when CI should fail after a worse latest run.
|
|
338
|
-
|
|
339
|
-
`
|
|
339
|
+
`check --history <path>` appends a compact JSONL validation snapshot after a single package check. `history <path>` reads the JSONL file and compares the latest run to the previous run, including status, finding-count deltas, and whether the latest run regressed. Add `history --json` for automation output or `history --fail-on-regression` when CI should fail after a worse latest run.
|
|
340
|
+
|
|
341
|
+
`baseline create <path> --output <path>` records the current fingerprinted findings for gradual CI adoption. A later `check <path> --baseline <path>` keeps matched findings visible in baseline metadata but gates only new active findings; reports summarize new, matched, and resolved counts. Commit the baseline for review and regenerate it deliberately after accepted findings change.
|
|
342
|
+
|
|
343
|
+
`fix --dry-run` renders safe automatic fix plans without changing files. `fix --interactive --backup` shows the same numbered plan, then applies everything after `yes` or only selected action numbers such as `1,3`. `fix --apply --backup` applies supported safe fixes, such as manifest defaults and missing skills directories, after creating backups.
|
|
340
344
|
|
|
341
345
|
Optional local policy file:
|
|
342
346
|
|
|
@@ -402,9 +406,9 @@ jobs:
|
|
|
402
406
|
runs-on: ubuntu-latest
|
|
403
407
|
steps:
|
|
404
408
|
- uses: actions/checkout@v5
|
|
405
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
409
|
+
- uses: Esquetta/CodexPluginDoctor@v1.44.0
|
|
406
410
|
with:
|
|
407
|
-
version: "1.
|
|
411
|
+
version: "1.44.0"
|
|
408
412
|
path: .
|
|
409
413
|
runtime: "true"
|
|
410
414
|
policy: codex-publish
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CheckResult, FindingSeverity } from "../domain/types.js";
|
|
2
|
+
export interface ValidationBaselineFinding {
|
|
3
|
+
id: string;
|
|
4
|
+
severity: FindingSeverity;
|
|
5
|
+
fingerprint: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ValidationBaseline {
|
|
8
|
+
schemaVersion: "1.0.0";
|
|
9
|
+
generatedAt: string;
|
|
10
|
+
findings: ValidationBaselineFinding[];
|
|
11
|
+
}
|
|
12
|
+
export declare function buildValidationBaseline(result: CheckResult): ValidationBaseline;
|
|
13
|
+
export declare function writeValidationBaseline(baselinePath: string, baseline: ValidationBaseline): Promise<void>;
|
|
14
|
+
export declare function readValidationBaseline(baselinePath: string): Promise<ValidationBaseline>;
|
|
15
|
+
export declare function applyValidationBaseline(result: CheckResult, baseline: ValidationBaseline, options: {
|
|
16
|
+
failOnWarnings: boolean;
|
|
17
|
+
}): CheckResult;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const fingerprintPattern = /^[a-f0-9]{64}$/;
|
|
4
|
+
export function buildValidationBaseline(result) {
|
|
5
|
+
const findings = new Map();
|
|
6
|
+
for (const finding of result.findings) {
|
|
7
|
+
if (finding.fingerprint && !findings.has(finding.fingerprint)) {
|
|
8
|
+
findings.set(finding.fingerprint, {
|
|
9
|
+
id: finding.id,
|
|
10
|
+
severity: finding.severity,
|
|
11
|
+
fingerprint: finding.fingerprint
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
schemaVersion: "1.0.0",
|
|
17
|
+
generatedAt: new Date().toISOString(),
|
|
18
|
+
findings: [...findings.values()]
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export async function writeValidationBaseline(baselinePath, baseline) {
|
|
22
|
+
const absolutePath = path.resolve(baselinePath);
|
|
23
|
+
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
24
|
+
await writeFile(absolutePath, JSON.stringify(baseline, null, 2), "utf8");
|
|
25
|
+
}
|
|
26
|
+
export async function readValidationBaseline(baselinePath) {
|
|
27
|
+
const value = JSON.parse(await readFile(path.resolve(baselinePath), "utf8"));
|
|
28
|
+
if (!isValidationBaseline(value)) {
|
|
29
|
+
throw new Error("expected schemaVersion 1.0.0 and valid fingerprinted findings");
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
export function applyValidationBaseline(result, baseline, options) {
|
|
34
|
+
const baselineFingerprints = new Set(baseline.findings.map((finding) => finding.fingerprint));
|
|
35
|
+
const currentFingerprints = new Set(result.findings.flatMap((finding) => finding.fingerprint ? [finding.fingerprint] : []));
|
|
36
|
+
const baselinedFindings = [];
|
|
37
|
+
const findings = [];
|
|
38
|
+
for (const finding of result.findings) {
|
|
39
|
+
if (finding.fingerprint && baselineFingerprints.has(finding.fingerprint)) {
|
|
40
|
+
baselinedFindings.push(finding);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
findings.push(finding);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const hasFailures = findings.some((finding) => finding.severity === "fail");
|
|
47
|
+
const hasWarnings = findings.some((finding) => finding.severity === "warn");
|
|
48
|
+
const status = hasFailures || (options.failOnWarnings && hasWarnings)
|
|
49
|
+
? "fail"
|
|
50
|
+
: hasWarnings
|
|
51
|
+
? "warn"
|
|
52
|
+
: "pass";
|
|
53
|
+
return {
|
|
54
|
+
...result,
|
|
55
|
+
status,
|
|
56
|
+
exitCode: status === "fail" ? 1 : 0,
|
|
57
|
+
findings,
|
|
58
|
+
baselinedFindings,
|
|
59
|
+
baselineSummary: {
|
|
60
|
+
matched: baselinedFindings.length,
|
|
61
|
+
new: findings.length,
|
|
62
|
+
resolved: baseline.findings.filter((finding) => !currentFingerprints.has(finding.fingerprint)).length
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function isValidationBaseline(value) {
|
|
67
|
+
if (!value || typeof value !== "object") {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const candidate = value;
|
|
71
|
+
return candidate.schemaVersion === "1.0.0"
|
|
72
|
+
&& typeof candidate.generatedAt === "string"
|
|
73
|
+
&& !Number.isNaN(Date.parse(candidate.generatedAt))
|
|
74
|
+
&& Array.isArray(candidate.findings)
|
|
75
|
+
&& candidate.findings.every((finding) => finding
|
|
76
|
+
&& typeof finding.id === "string"
|
|
77
|
+
&& finding.id.length > 0
|
|
78
|
+
&& (finding.severity === "warn" || finding.severity === "fail")
|
|
79
|
+
&& typeof finding.fingerprint === "string"
|
|
80
|
+
&& fingerprintPattern.test(finding.fingerprint))
|
|
81
|
+
&& new Set(candidate.findings.map((finding) => finding.fingerprint)).size
|
|
82
|
+
=== candidate.findings.length;
|
|
83
|
+
}
|
package/dist/domain/types.d.ts
CHANGED
|
@@ -30,6 +30,13 @@ export interface CheckResult {
|
|
|
30
30
|
suppressedFindings?: SuppressedFinding[];
|
|
31
31
|
suppressionSummary?: SuppressionSummary;
|
|
32
32
|
runtimeScorecard?: RuntimeScorecard;
|
|
33
|
+
baselinedFindings?: Finding[];
|
|
34
|
+
baselineSummary?: BaselineSummary;
|
|
35
|
+
}
|
|
36
|
+
export interface BaselineSummary {
|
|
37
|
+
matched: number;
|
|
38
|
+
new: number;
|
|
39
|
+
resolved: number;
|
|
33
40
|
}
|
|
34
41
|
export interface CheckOptions {
|
|
35
42
|
runtime?: boolean;
|
|
@@ -59,6 +66,7 @@ export interface JsonReportSummary {
|
|
|
59
66
|
warn: number;
|
|
60
67
|
total: number;
|
|
61
68
|
};
|
|
69
|
+
baseline?: BaselineSummary;
|
|
62
70
|
}
|
|
63
71
|
export interface JsonReport {
|
|
64
72
|
schemaVersion: "1.0.0";
|
|
@@ -67,6 +75,7 @@ export interface JsonReport {
|
|
|
67
75
|
findings: Finding[];
|
|
68
76
|
suppressedFindings?: SuppressedFinding[];
|
|
69
77
|
suppressionSummary?: SuppressionSummary;
|
|
78
|
+
baselinedFindings?: Finding[];
|
|
70
79
|
}
|
|
71
80
|
export type RuntimeCapabilityStatus = "pass" | "fail" | "warn" | "skipped" | "unsupported";
|
|
72
81
|
export interface RuntimeScorecard {
|
|
@@ -16,7 +16,8 @@ export function buildJsonReport(result, options) {
|
|
|
16
16
|
fail: failCount,
|
|
17
17
|
warn: warnCount,
|
|
18
18
|
total: result.findings.length
|
|
19
|
-
}
|
|
19
|
+
},
|
|
20
|
+
...(result.baselineSummary ? { baseline: result.baselineSummary } : {})
|
|
20
21
|
},
|
|
21
22
|
findings: result.findings,
|
|
22
23
|
...(result.suppressedFindings
|
|
@@ -24,6 +25,9 @@ export function buildJsonReport(result, options) {
|
|
|
24
25
|
: {}),
|
|
25
26
|
...(result.suppressionSummary
|
|
26
27
|
? { suppressionSummary: result.suppressionSummary }
|
|
28
|
+
: {}),
|
|
29
|
+
...(result.baselinedFindings
|
|
30
|
+
? { baselinedFindings: result.baselinedFindings }
|
|
27
31
|
: {})
|
|
28
32
|
};
|
|
29
33
|
}
|
|
@@ -20,8 +20,11 @@ export function buildMarkdownReport(result, options) {
|
|
|
20
20
|
if (result.suppressionSummary) {
|
|
21
21
|
lines.push(`| Suppressions Applied | ${result.suppressionSummary.applied} |`, `| Suppressions Expired | ${result.suppressionSummary.expired} |`, `| Suppressions Invalid | ${result.suppressionSummary.invalid} |`);
|
|
22
22
|
}
|
|
23
|
+
if (result.baselineSummary) {
|
|
24
|
+
lines.push(`| Baseline New | ${result.baselineSummary.new} |`, `| Baseline Matched | ${result.baselineSummary.matched} |`, `| Baseline Resolved | ${result.baselineSummary.resolved} |`);
|
|
25
|
+
}
|
|
23
26
|
if (result.findings.length === 0 && !result.suppressedFindings?.length) {
|
|
24
|
-
lines.push("", "No findings.");
|
|
27
|
+
lines.push("", result.baselineSummary ? "No new findings." : "No findings.");
|
|
25
28
|
return lines.join("\n");
|
|
26
29
|
}
|
|
27
30
|
if (result.runtimeScorecard) {
|
|
@@ -39,6 +39,9 @@ export function renderTextReport(result, options = {}) {
|
|
|
39
39
|
if (result.suppressionSummary) {
|
|
40
40
|
lines.push(`Suppressions: ${result.suppressionSummary.applied} applied, ${result.suppressionSummary.expired} expired, ${result.suppressionSummary.invalid} invalid`);
|
|
41
41
|
}
|
|
42
|
+
if (result.baselineSummary) {
|
|
43
|
+
lines.push(`Baseline: ${result.baselineSummary.new} new, ${result.baselineSummary.matched} matched, ${result.baselineSummary.resolved} resolved`);
|
|
44
|
+
}
|
|
42
45
|
if (result.findings.length === 0 && !result.suppressedFindings?.length) {
|
|
43
46
|
if (result.runtimeScorecard) {
|
|
44
47
|
lines.push("", "Runtime Scorecard", "----------------");
|
|
@@ -51,7 +54,7 @@ export function renderTextReport(result, options = {}) {
|
|
|
51
54
|
lines.push(`prompts/list: ${result.runtimeScorecard.promptsList}`);
|
|
52
55
|
lines.push(`prompts/get: ${result.runtimeScorecard.promptGet}`);
|
|
53
56
|
}
|
|
54
|
-
lines.push("", "No findings.");
|
|
57
|
+
lines.push("", result.baselineSummary ? "No new findings." : "No findings.");
|
|
55
58
|
return lines.join("\n");
|
|
56
59
|
}
|
|
57
60
|
const failures = result.findings.filter((finding) => finding.severity === "fail");
|
package/dist/run-cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { discoverInstalledPlugins, filterInstalledPlugins } from "./core/discover-installed-plugins.js";
|
|
7
7
|
import { buildEcosystemAudit, renderEcosystemAudit, renderEcosystemAuditJson } from "./audit/ecosystem-audit.js";
|
|
8
8
|
import { appendValidationHistoryEntry, readValidationHistory, summarizeValidationHistory } from "./core/validation-history.js";
|
|
9
|
+
import { applyValidationBaseline, buildValidationBaseline, readValidationBaseline, writeValidationBaseline } from "./core/validation-baseline.js";
|
|
9
10
|
import { buildCompatibilityMatrix, matrixExitCode } from "./compatibility/compatibility-matrix.js";
|
|
10
11
|
import { applyInstallPreview, renderApplyInstallResult } from "./compatibility/apply-install-preview.js";
|
|
11
12
|
import { buildClaudeDesktopInstallPreview, renderClaudeDesktopInstallPreview } from "./compatibility/claude-desktop-install-preview.js";
|
|
@@ -80,6 +81,7 @@ const defaultIo = {
|
|
|
80
81
|
function printUsage(io) {
|
|
81
82
|
io.writeStderr("Usage: codex-plugin-doctor check <path|--installed> [filter] [--policy codex-publish|mcp-strict|security] [--compat] [--json|--markdown|--badge-json|--badge-markdown] [--output <path>] [--history <path>] [--runtime] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>]\n codex-plugin-doctor audit --installed [filter] [--policy codex-publish|mcp-strict|security] [--security] [--compat] [--json] [--output <path>] [--cache] [--changed]\n codex-plugin-doctor audit deps <path> [--policy codex-publish|mcp-strict|security] [--recommend] [--json|--sarif] [--output <path>]\n codex-plugin-doctor mcp <path> [--json] [--output <path>]\n codex-plugin-doctor security <path> [--policy security] [--json|--scorecard]\n codex-plugin-doctor compat <path> [--all|--client <client>] [--json] [--scorecard] [--output <path>] [--install-preview|--apply --backup]\n codex-plugin-doctor suppress add <path> [--fingerprint <sha256> --reason <text> --expires-at YYYY-MM-DD] [--config <path>] [--json]\n codex-plugin-doctor suppress list <path> [--config <path>] [--json]\n codex-plugin-doctor suppress remove <path> [--fingerprint <sha256>|--index <n>] [--config <path>] [--json]\n codex-plugin-doctor fix <path> (--dry-run|--interactive --backup|--apply --backup)\n codex-plugin-doctor history <history.jsonl> [--json] [--fail-on-regression]\n codex-plugin-doctor watch <path> [--runtime] [--json] [--output <path>] [--debounce-ms <ms>] [--max-iterations <n>] [--fail-fast] [--accumulate-json <path>]\n codex-plugin-doctor doctor [npm <package>|contract|corpus|runtime-plan <path> [--json|--markdown] [--output <path>]|runtime-policy <path> [--json] [--output <path>]|review-bundle <path> --output <dir> --sign-key-env NAME [--json] [--allow-dirty] [--allow-untagged]|review-bundle verify <bundle-dir> --target <path> --sign-key-env NAME [--json] [--output <path>] [--failures-only]|review-bundle diff --before <dir> --after <dir> [--json]|attest <path> [--sign-key-env NAME]|attest verify <attestation.json> --target <path> --sign-key-env NAME|release-evidence <path> --sign-key-env NAME [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>]|release-evidence verify <evidence.json> --target <path> --sign-key-env NAME|release-evidence asset <path> --tag <tag> --output <evidence.json> --sign-key-env NAME [--upload]|mcp <path>|inspector <path>|diff --before <path> --after <path>|recommend <path>|trust <path>|perf <path> [--max-total-ms <ms>] [--max-stage-ms stage=ms]|export --bundle <path>|snapshot|clients|--json|--update-check]\n codex-plugin-doctor init [path] [--template skill-only|mcp-stdio|mcp-http|full-runtime]\n codex-plugin-doctor init-ci [path]\n codex-plugin-doctor init-git-hooks [path] [--force] [--json]\n codex-plugin-doctor init-git-hooks [path] --remove [--json]\n codex-plugin-doctor self-test\n codex-plugin-doctor list --installed\n codex-plugin-doctor explain <finding-id>\n codex-plugin-doctor --version\n\nFirst run:\n codex-plugin-doctor doctor\n codex-plugin-doctor self-test\n codex-plugin-doctor init my-plugin\n codex-plugin-doctor check . --runtime --explain");
|
|
82
83
|
io.writeStderr("Suppression governance: codex-plugin-doctor suppress list <path> [--fail-on-expired] [--fail-on-invalid] [--warn-expiring-within-days <days>]\n codex-plugin-doctor suppress prune <path> [--apply] [--json]");
|
|
84
|
+
io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
|
|
83
85
|
}
|
|
84
86
|
const suppressUsageText = [
|
|
85
87
|
"Usage:",
|
|
@@ -817,6 +819,31 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
817
819
|
io.writeStdout(renderInstalledPlugins(installedPlugins));
|
|
818
820
|
return 0;
|
|
819
821
|
}
|
|
822
|
+
if (command === "baseline") {
|
|
823
|
+
if (maybePath !== "create") {
|
|
824
|
+
io.writeStderr("Usage: codex-plugin-doctor baseline create <path> --output <path> [--runtime]");
|
|
825
|
+
return 2;
|
|
826
|
+
}
|
|
827
|
+
const targetPath = remainingArgs[0];
|
|
828
|
+
const baselineFlags = remainingArgs.slice(1);
|
|
829
|
+
const outputIndex = baselineFlags.indexOf("--output");
|
|
830
|
+
const outputPath = outputIndex === -1 ? null : baselineFlags[outputIndex + 1];
|
|
831
|
+
if (!targetPath || targetPath.startsWith("--")) {
|
|
832
|
+
io.writeStderr("Missing target path for baseline create.");
|
|
833
|
+
return 2;
|
|
834
|
+
}
|
|
835
|
+
if (!outputPath || outputPath.startsWith("--")) {
|
|
836
|
+
io.writeStderr("baseline create requires --output <path>.");
|
|
837
|
+
return 2;
|
|
838
|
+
}
|
|
839
|
+
const result = await (options.runCheckImpl ?? runCheck)(targetPath, {
|
|
840
|
+
runtime: baselineFlags.includes("--runtime")
|
|
841
|
+
});
|
|
842
|
+
const baseline = buildValidationBaseline(result);
|
|
843
|
+
await writeValidationBaseline(outputPath, baseline);
|
|
844
|
+
io.writeStdout(`Baseline created: ${path.resolve(outputPath)}\nFindings: ${baseline.findings.length}`);
|
|
845
|
+
return 0;
|
|
846
|
+
}
|
|
820
847
|
if (command === "doctor") {
|
|
821
848
|
const doctorFlags = maybePath?.startsWith("--")
|
|
822
849
|
? [maybePath, ...remainingArgs]
|
|
@@ -2256,6 +2283,8 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2256
2283
|
: normalizedFlags[runtimeApprovalDigestIndex + 1];
|
|
2257
2284
|
const changedSinceIndex = normalizedFlags.indexOf("--changed-since");
|
|
2258
2285
|
const changedSinceRef = changedSinceIndex === -1 ? null : normalizedFlags[changedSinceIndex + 1];
|
|
2286
|
+
const baselineIndex = normalizedFlags.indexOf("--baseline");
|
|
2287
|
+
const baselinePath = baselineIndex === -1 ? null : normalizedFlags[baselineIndex + 1];
|
|
2259
2288
|
if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
|
|
2260
2289
|
io.writeStderr("Missing path after --output.");
|
|
2261
2290
|
return 2;
|
|
@@ -2292,6 +2321,18 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2292
2321
|
io.writeStderr("--changed-since requires a single package target.");
|
|
2293
2322
|
return 2;
|
|
2294
2323
|
}
|
|
2324
|
+
if (baselineIndex !== -1 && (!baselinePath || baselinePath.startsWith("--"))) {
|
|
2325
|
+
io.writeStderr("Missing path after --baseline.");
|
|
2326
|
+
return 2;
|
|
2327
|
+
}
|
|
2328
|
+
if (baselinePath && checkInstalled) {
|
|
2329
|
+
io.writeStderr("Baseline gating requires a single package target.");
|
|
2330
|
+
return 2;
|
|
2331
|
+
}
|
|
2332
|
+
if (baselinePath && changedSinceRef) {
|
|
2333
|
+
io.writeStderr("Use either --baseline or --changed-since, not both.");
|
|
2334
|
+
return 2;
|
|
2335
|
+
}
|
|
2295
2336
|
if (runtimeApprovalDigestIndex !== -1 &&
|
|
2296
2337
|
(!runtimeApprovalDigest || runtimeApprovalDigest.startsWith("--"))) {
|
|
2297
2338
|
io.writeStderr("Missing digest after --runtime-approval-digest.");
|
|
@@ -2316,6 +2357,16 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2316
2357
|
io.writeStderr("Runtime approval gating requires a single package target, not --installed.");
|
|
2317
2358
|
return 2;
|
|
2318
2359
|
}
|
|
2360
|
+
let baseline = null;
|
|
2361
|
+
if (baselinePath) {
|
|
2362
|
+
try {
|
|
2363
|
+
baseline = await readValidationBaseline(baselinePath);
|
|
2364
|
+
}
|
|
2365
|
+
catch (error) {
|
|
2366
|
+
io.writeStderr(`Invalid baseline file: ${error.message}`);
|
|
2367
|
+
return 2;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2319
2370
|
const outputPolicy = determineOutputPolicy({
|
|
2320
2371
|
jsonOutput: jsonOutput || badgeJsonOutput,
|
|
2321
2372
|
markdownOutput: markdownOutput || badgeMarkdownOutput,
|
|
@@ -2447,12 +2498,18 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2447
2498
|
return 2;
|
|
2448
2499
|
}
|
|
2449
2500
|
}
|
|
2450
|
-
const
|
|
2501
|
+
const doctorConfig = applyPolicyToDoctorConfig(applyCheckProfile(await loadDoctorConfig(targetPath, configPath), checkProfile), policy);
|
|
2502
|
+
const configuredResult = applyDoctorConfig(await runCheckImpl(targetPath, {
|
|
2451
2503
|
runtime: effectiveRuntimeProbeEnabled,
|
|
2452
2504
|
runtimeTranscript: effectiveRuntimeProbeEnabled && verboseRuntime
|
|
2453
2505
|
? (line) => io.writeStderr(line)
|
|
2454
2506
|
: undefined
|
|
2455
|
-
}),
|
|
2507
|
+
}), doctorConfig);
|
|
2508
|
+
const result = baseline
|
|
2509
|
+
? applyValidationBaseline(configuredResult, baseline, {
|
|
2510
|
+
failOnWarnings: doctorConfig.failOnWarnings
|
|
2511
|
+
})
|
|
2512
|
+
: configuredResult;
|
|
2456
2513
|
if (renderer) {
|
|
2457
2514
|
if (result.status === "fail") {
|
|
2458
2515
|
renderer.stopFailure("Validation failed");
|
package/package.json
CHANGED