codex-plugin-doctor 1.26.0 → 1.28.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 +114 -84
- package/dist/core/doctor-config-store.d.ts +12 -0
- package/dist/core/doctor-config-store.js +61 -0
- package/dist/core/doctor-config.d.ts +4 -1
- package/dist/core/doctor-config.js +78 -5
- package/dist/core/npm-package-doctor.js +8 -0
- package/dist/core/output-contract.js +18 -0
- package/dist/core/suppression-management.d.ts +34 -0
- package/dist/core/suppression-management.js +170 -0
- package/dist/core/suppression-record.d.ts +26 -0
- package/dist/core/suppression-record.js +62 -0
- package/dist/domain/types.d.ts +16 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/reporting/render-json-report.js +7 -1
- package/dist/reporting/render-markdown-report.js +35 -15
- package/dist/reporting/render-sarif-report.js +9 -1
- package/dist/reporting/render-suppression-management.d.ts +7 -0
- package/dist/reporting/render-suppression-management.js +117 -0
- package/dist/reporting/render-text-report.js +14 -1
- package/dist/rules/rule-catalog.d.ts +1 -1
- package/dist/rules/rule-catalog.js +18 -0
- package/dist/run-cli.d.ts +3 -0
- package/dist/run-cli.js +411 -1
- package/package.json +64 -64
package/README.md
CHANGED
|
@@ -11,23 +11,23 @@ It catches packaging, metadata, security, and runtime protocol problems before a
|
|
|
11
11
|
|
|
12
12
|
## Status
|
|
13
13
|
|
|
14
|
-
Codex Plugin Doctor is a public stable CLI for local and CI validation.
|
|
14
|
+
Codex Plugin Doctor is a public stable CLI for local and CI validation.
|
|
15
15
|
|
|
16
16
|
- Primary surface: GitHub repository and npm package
|
|
17
17
|
- Distribution today: `npm install -g`, local source install, `npm link`, `npm pack`, GitHub Releases
|
|
18
18
|
- Public npm package: `codex-plugin-doctor`
|
|
19
19
|
- License: [MIT](./LICENSE)
|
|
20
20
|
|
|
21
|
-
## 1.0 Stability
|
|
22
|
-
|
|
23
|
-
The 1.0 line is the stable compatibility baseline for plugin authors and CI consumers.
|
|
24
|
-
|
|
25
|
-
- Public JSON schema surfaces and existing rule IDs/default severities are treated as stable through 1.0.
|
|
26
|
-
- Runtime probing remains opt-in because it executes package-local MCP servers.
|
|
27
|
-
- The project remains Codex-first; broader MCP Doctor positioning stays a post-1.0 expansion path.
|
|
28
|
-
- Post-1.0 feature work should stay additive unless a documented major-version decision is made.
|
|
29
|
-
|
|
30
|
-
See [
|
|
21
|
+
## 1.0 Stability
|
|
22
|
+
|
|
23
|
+
The 1.0 line is the stable compatibility baseline for plugin authors and CI consumers.
|
|
24
|
+
|
|
25
|
+
- Public JSON schema surfaces and existing rule IDs/default severities are treated as stable through 1.0.
|
|
26
|
+
- Runtime probing remains opt-in because it executes package-local MCP servers.
|
|
27
|
+
- The project remains Codex-first; broader MCP Doctor positioning stays a post-1.0 expansion path.
|
|
28
|
+
- Post-1.0 feature work should stay additive unless a documented major-version decision is made.
|
|
29
|
+
|
|
30
|
+
See the [documentation index](./docs/README.md) for architecture, usage, security, and contributor guides.
|
|
31
31
|
|
|
32
32
|
## Why This Exists
|
|
33
33
|
|
|
@@ -64,7 +64,7 @@ Security scorecard with `security`:
|
|
|
64
64
|
- MCP server `cwd` paths that escape the package root
|
|
65
65
|
- plain HTTP remote transport warnings
|
|
66
66
|
|
|
67
|
-
Runtime MCP validation with `--runtime`:
|
|
67
|
+
Runtime MCP validation with `--runtime`:
|
|
68
68
|
|
|
69
69
|
- `initialize`
|
|
70
70
|
- `notifications/initialized`
|
|
@@ -76,9 +76,9 @@ Runtime MCP validation with `--runtime`:
|
|
|
76
76
|
- `prompts/list`
|
|
77
77
|
- `prompts/get`
|
|
78
78
|
- paginated list responses
|
|
79
|
-
- runtime capability scorecard
|
|
80
|
-
- redacted verbose transcript with `--verbose-runtime`
|
|
81
|
-
- optional runtime approval gating with a precomputed `doctor runtime-plan` digest
|
|
79
|
+
- runtime capability scorecard
|
|
80
|
+
- redacted verbose transcript with `--verbose-runtime`
|
|
81
|
+
- optional runtime approval gating with a precomputed `doctor runtime-plan` digest
|
|
82
82
|
|
|
83
83
|
Output formats:
|
|
84
84
|
|
|
@@ -195,14 +195,14 @@ codex-plugin-doctor doctor contract
|
|
|
195
195
|
codex-plugin-doctor doctor contract --json --output output-contract.json
|
|
196
196
|
codex-plugin-doctor doctor corpus
|
|
197
197
|
codex-plugin-doctor doctor corpus --json --output validation-corpus.json
|
|
198
|
-
codex-plugin-doctor doctor npm <published-plugin-package>
|
|
199
|
-
codex-plugin-doctor doctor npm <published-plugin-package> --json --output npm-preinstall.json
|
|
200
|
-
codex-plugin-doctor doctor attest .
|
|
201
|
-
codex-plugin-doctor doctor attest . --json --output attestation.json
|
|
202
|
-
codex-plugin-doctor doctor attest . --json --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY --output attestation.json
|
|
203
|
-
codex-plugin-doctor doctor attest verify attestation.json --target . --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
204
|
-
codex-plugin-doctor doctor attest verify attestation.json --target . --json --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
205
|
-
codex-plugin-doctor doctor inspector .
|
|
198
|
+
codex-plugin-doctor doctor npm <published-plugin-package>
|
|
199
|
+
codex-plugin-doctor doctor npm <published-plugin-package> --json --output npm-preinstall.json
|
|
200
|
+
codex-plugin-doctor doctor attest .
|
|
201
|
+
codex-plugin-doctor doctor attest . --json --output attestation.json
|
|
202
|
+
codex-plugin-doctor doctor attest . --json --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY --output attestation.json
|
|
203
|
+
codex-plugin-doctor doctor attest verify attestation.json --target . --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
204
|
+
codex-plugin-doctor doctor attest verify attestation.json --target . --json --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
205
|
+
codex-plugin-doctor doctor inspector .
|
|
206
206
|
codex-plugin-doctor doctor inspector . --server context7 --json --output inspector-command.json
|
|
207
207
|
codex-plugin-doctor doctor diff --before ./old-plugin --after ./new-plugin
|
|
208
208
|
codex-plugin-doctor doctor diff --before ./old-plugin --after ./new-plugin --json --output risk-diff.json
|
|
@@ -210,20 +210,20 @@ codex-plugin-doctor doctor recommend .
|
|
|
210
210
|
codex-plugin-doctor doctor recommend . --json --output recommendations.json
|
|
211
211
|
codex-plugin-doctor doctor trust .
|
|
212
212
|
codex-plugin-doctor doctor trust . --json --output trust-score.json
|
|
213
|
-
codex-plugin-doctor doctor perf .
|
|
214
|
-
codex-plugin-doctor doctor perf . --json --output perf.json
|
|
215
|
-
codex-plugin-doctor doctor perf . --max-total-ms 2500 --max-stage-ms validation=500
|
|
216
|
-
codex-plugin-doctor doctor runtime-plan .
|
|
217
|
-
codex-plugin-doctor doctor runtime-plan . --json --output runtime-plan.json
|
|
218
|
-
codex-plugin-doctor doctor runtime-plan . --markdown --output runtime-plan.md
|
|
219
|
-
codex-plugin-doctor doctor runtime-policy .
|
|
220
|
-
codex-plugin-doctor doctor runtime-policy . --json --output runtime-policy.json
|
|
221
|
-
codex-plugin-doctor doctor review-bundle . --output review-bundle --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
222
|
-
codex-plugin-doctor doctor review-bundle verify review-bundle --target . --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
223
|
-
codex-plugin-doctor doctor review-bundle diff --before old-review-bundle --after review-bundle
|
|
224
|
-
codex-plugin-doctor doctor mcp .
|
|
225
|
-
codex-plugin-doctor doctor mcp . --json --output mcp-healthcheck.json
|
|
226
|
-
codex-plugin-doctor doctor export --bundle .
|
|
213
|
+
codex-plugin-doctor doctor perf .
|
|
214
|
+
codex-plugin-doctor doctor perf . --json --output perf.json
|
|
215
|
+
codex-plugin-doctor doctor perf . --max-total-ms 2500 --max-stage-ms validation=500
|
|
216
|
+
codex-plugin-doctor doctor runtime-plan .
|
|
217
|
+
codex-plugin-doctor doctor runtime-plan . --json --output runtime-plan.json
|
|
218
|
+
codex-plugin-doctor doctor runtime-plan . --markdown --output runtime-plan.md
|
|
219
|
+
codex-plugin-doctor doctor runtime-policy .
|
|
220
|
+
codex-plugin-doctor doctor runtime-policy . --json --output runtime-policy.json
|
|
221
|
+
codex-plugin-doctor doctor review-bundle . --output review-bundle --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
222
|
+
codex-plugin-doctor doctor review-bundle verify review-bundle --target . --sign-key-env CODEX_PLUGIN_DOCTOR_SIGNING_KEY
|
|
223
|
+
codex-plugin-doctor doctor review-bundle diff --before old-review-bundle --after review-bundle
|
|
224
|
+
codex-plugin-doctor doctor mcp .
|
|
225
|
+
codex-plugin-doctor doctor mcp . --json --output mcp-healthcheck.json
|
|
226
|
+
codex-plugin-doctor doctor export --bundle .
|
|
227
227
|
codex-plugin-doctor doctor export --bundle . --output doctor-bundle.json
|
|
228
228
|
codex-plugin-doctor doctor snapshot
|
|
229
229
|
codex-plugin-doctor doctor snapshot --json
|
|
@@ -232,18 +232,18 @@ codex-plugin-doctor doctor clients
|
|
|
232
232
|
codex-plugin-doctor doctor --update-check
|
|
233
233
|
codex-plugin-doctor audit --installed
|
|
234
234
|
codex-plugin-doctor audit --installed --security --compat
|
|
235
|
-
codex-plugin-doctor audit --installed --security --compat --json --output local-audit.json
|
|
236
|
-
codex-plugin-doctor audit --installed --security --compat --cache
|
|
237
|
-
codex-plugin-doctor audit --installed --changed --cache
|
|
238
|
-
codex-plugin-doctor audit deps .
|
|
239
|
-
codex-plugin-doctor audit deps . --json --output dependency-audit.json
|
|
240
|
-
codex-plugin-doctor watch .
|
|
241
|
-
codex-plugin-doctor watch . --json --output watch-latest.json --debounce-ms 500
|
|
242
|
-
codex-plugin-doctor init-git-hooks .
|
|
243
|
-
codex-plugin-doctor init-git-hooks . --json
|
|
244
|
-
codex-plugin-doctor mcp .
|
|
245
|
-
codex-plugin-doctor mcp . --json
|
|
246
|
-
codex-plugin-doctor mcp . --json --output mcp-doctor.json
|
|
235
|
+
codex-plugin-doctor audit --installed --security --compat --json --output local-audit.json
|
|
236
|
+
codex-plugin-doctor audit --installed --security --compat --cache
|
|
237
|
+
codex-plugin-doctor audit --installed --changed --cache
|
|
238
|
+
codex-plugin-doctor audit deps .
|
|
239
|
+
codex-plugin-doctor audit deps . --json --output dependency-audit.json
|
|
240
|
+
codex-plugin-doctor watch .
|
|
241
|
+
codex-plugin-doctor watch . --json --output watch-latest.json --debounce-ms 500
|
|
242
|
+
codex-plugin-doctor init-git-hooks .
|
|
243
|
+
codex-plugin-doctor init-git-hooks . --json
|
|
244
|
+
codex-plugin-doctor mcp .
|
|
245
|
+
codex-plugin-doctor mcp . --json
|
|
246
|
+
codex-plugin-doctor mcp . --json --output mcp-doctor.json
|
|
247
247
|
codex-plugin-doctor init my-plugin
|
|
248
248
|
codex-plugin-doctor init my-mcp --template mcp-stdio
|
|
249
249
|
codex-plugin-doctor init remote-mcp --template mcp-http
|
|
@@ -282,10 +282,10 @@ codex-plugin-doctor check . --badge-json --output doctor-badge.json
|
|
|
282
282
|
codex-plugin-doctor check . --badge-markdown
|
|
283
283
|
codex-plugin-doctor check . --sarif --output results.sarif
|
|
284
284
|
codex-plugin-doctor check . --ascii
|
|
285
|
-
codex-plugin-doctor check . --no-animations
|
|
286
|
-
codex-plugin-doctor check . --runtime
|
|
287
|
-
codex-plugin-doctor check . --runtime --require-runtime-approval --runtime-approval-digest sha256:<approved-plan-digest>
|
|
288
|
-
codex-plugin-doctor check . --config .codex-doctor.json
|
|
285
|
+
codex-plugin-doctor check . --no-animations
|
|
286
|
+
codex-plugin-doctor check . --runtime
|
|
287
|
+
codex-plugin-doctor check . --runtime --require-runtime-approval --runtime-approval-digest sha256:<approved-plan-digest>
|
|
288
|
+
codex-plugin-doctor check . --config .codex-doctor.json
|
|
289
289
|
codex-plugin-doctor check . --history validation-history.jsonl
|
|
290
290
|
codex-plugin-doctor history validation-history.jsonl
|
|
291
291
|
codex-plugin-doctor history validation-history.jsonl --json
|
|
@@ -298,23 +298,23 @@ codex-plugin-doctor check . --json --runtime --verbose-runtime
|
|
|
298
298
|
|
|
299
299
|
`self-test` runs the bundled runtime-complete sample through static validation, runtime MCP probes, and the compatibility scorecard. It is the fastest post-install check after `npm install -g codex-plugin-doctor`.
|
|
300
300
|
|
|
301
|
-
`doctor` checks the local environment, including package version, platform, Node version, npm global prefix, Codex home, and Codex plugin cache visibility. The text output also includes recommended next commands for self-test, installed plugin discovery, runtime checks, compatibility scoring, and CI setup. `doctor contract` publishes the machine-readable output contract, including public JSON schema surfaces, stable-through-1.0 compatibility metadata, and a frozen rule catalog digest. Add `--json` for automation or `--output output-contract.json` to write the contract to disk. `doctor corpus` runs the bundled validation corpus against healthy runtime, risky security, starter skill, and generic MCP packages, then reports whether each case matched its expected outcome. Add `--json` for automation or `--output validation-corpus.json` to write the corpus report to disk. `doctor npm <package>` runs a preinstall scan by packing the npm package with scripts disabled, extracting the publish tarball, and running validation, security, trust, and recommendation checks against the shipped contents. The JSON report includes tarball metadata such as filename, path, integrity, shasum, sizes, file count, and extracted package root so automation can prove which archive was scanned. Use a published Codex plugin package as the target; scanning `codex-plugin-doctor` itself intentionally reports a missing plugin manifest because this CLI package is not a plugin package. Add `--json` for automation or `--output npm-preinstall.json` to write the report to disk. `doctor attest <path>` creates a local attestation with stable package/report digests, validation/security/compatibility/trust summary, and verification metadata. Add `--sign-key-env NAME` to attach a local HMAC-SHA256 signature without printing the secret, or `--json --output attestation.json` to write the artifact to disk. `doctor attest verify <attestation.json> --target <path> --sign-key-env NAME` recomputes the package fingerprint, report digest, and HMAC signature offline; verification intentionally treats `generatedAt`, `targetPath`, `verification`, and `signature.keyHint` as unsigned display metadata. `doctor runtime-plan <path>` creates a non-executing runtime plan that lists MCP server commands, safe probe methods, risk reasons, and a stable approval digest before any local server is started. Add `--markdown --output runtime-plan.md` to preserve a review-ready approval artifact with the execution boundary, checklist, servers, probes, and risk reasons. `doctor runtime-policy <path>` evaluates the same runtime plan and security signals, then recommends `allow`, `review`, `sandbox_recommended`, or `deny` before local MCP execution starts. `doctor review-bundle <path> --output <dir> --sign-key-env NAME` writes a signed review directory with runtime plan, runtime policy, attestation, release evidence, manifest, Markdown summary files, and SHA-256 file integrity digests. `doctor review-bundle verify <bundle-dir> --target <path> --sign-key-env NAME` verifies the bundle manifest, expected files, manifest integrity digests, runtime artifacts, signed attestation, and signed release evidence offline before a reviewer trusts the handoff. `doctor review-bundle diff --before <dir> --after <dir>` compares two review bundles and flags risk-increasing changes in status, runtime policy, release readiness, signatures, release evidence, and runtime plan digest. `check --runtime --require-runtime-approval --runtime-approval-digest <digest>` refuses to run runtime probes unless the current plan digest matches the approved digest. `doctor release-evidence <path> --sign-key-env NAME` creates one redacted release bundle with signed attestation, offline verification, corpus, performance, security, trust, package metadata, git release gates, and runtime approval status. Strict release evidence requires a clean tagged worktree; use `--allow-dirty` or `--allow-untagged` only for local rehearsal. `doctor release-evidence verify <evidence.json> --target <path> --sign-key-env NAME` verifies a shared release evidence artifact offline against an explicit package path; the artifact target path is treated as display metadata, not trusted input. `doctor release-evidence asset <path> --tag <tag> --output <evidence.json> --sign-key-env NAME` writes a signed release evidence file and prints the `gh release upload` command; add `--upload` to run the upload through GitHub CLI with `--clobber`. `doctor inspector <path>` builds a safe MCP Inspector launch command from a packaged `.mcp.json` file without starting the Inspector proxy automatically. Use `--server <name>` when the package contains multiple MCP server entries. `doctor diff --before <path> --after <path>` compares two package roots and reports new findings, resolved findings, trust score delta, and whether risk increased. `doctor recommend <path>` turns validation, security, and compatibility signals into a prioritized action plan with blocker, high, medium, and info actions. Add `--json` for automation or `--output recommendations.json` to write the report to disk. `doctor trust <path>` creates a local trust score from package lifecycle scripts, dependency specs, and MCP security findings. Use it before release when you want supply-chain risks summarized as one score. `doctor perf <path>` profiles the shared package analysis pipeline and reports per-stage durations for validation, config, security, compatibility, trust, recommendations, and total runtime. Add `--max-total-ms <ms>` or repeatable `--max-stage-ms stage=ms` to fail CI when a budget is exceeded. `doctor mcp <path>` exposes the generic MCP static health report under the doctor command family without starting local MCP servers. `doctor export --bundle <path>` creates a redacted operator handoff bundle that includes validation JSON, security scorecard data, compatibility matrix, recommendations, and trust score in one file. `doctor snapshot` creates a redacted diagnostics bundle with environment health, client config readiness, installed plugin metadata, and next commands. Add `--json` for machine-readable output or `--output doctor-snapshot.json` to write the bundle to disk. `doctor clients` reports local Codex, Claude Desktop, Cursor, Cline, and Windsurf config readiness. `doctor --update-check` compares the installed CLI version with the latest npm version and prints the upgrade command when a newer release is available.
|
|
301
|
+
`doctor` checks the local environment, including package version, platform, Node version, npm global prefix, Codex home, and Codex plugin cache visibility. The text output also includes recommended next commands for self-test, installed plugin discovery, runtime checks, compatibility scoring, and CI setup. `doctor contract` publishes the machine-readable output contract, including public JSON schema surfaces, stable-through-1.0 compatibility metadata, and a frozen rule catalog digest. Add `--json` for automation or `--output output-contract.json` to write the contract to disk. `doctor corpus` runs the bundled validation corpus against healthy runtime, risky security, starter skill, and generic MCP packages, then reports whether each case matched its expected outcome. Add `--json` for automation or `--output validation-corpus.json` to write the corpus report to disk. `doctor npm <package>` runs a preinstall scan by packing the npm package with scripts disabled, extracting the publish tarball, and running validation, security, trust, and recommendation checks against the shipped contents. The JSON report includes tarball metadata such as filename, path, integrity, shasum, sizes, file count, and extracted package root so automation can prove which archive was scanned. Use a published Codex plugin package as the target; scanning `codex-plugin-doctor` itself intentionally reports a missing plugin manifest because this CLI package is not a plugin package. Add `--json` for automation or `--output npm-preinstall.json` to write the report to disk. `doctor attest <path>` creates a local attestation with stable package/report digests, validation/security/compatibility/trust summary, and verification metadata. Add `--sign-key-env NAME` to attach a local HMAC-SHA256 signature without printing the secret, or `--json --output attestation.json` to write the artifact to disk. `doctor attest verify <attestation.json> --target <path> --sign-key-env NAME` recomputes the package fingerprint, report digest, and HMAC signature offline; verification intentionally treats `generatedAt`, `targetPath`, `verification`, and `signature.keyHint` as unsigned display metadata. `doctor runtime-plan <path>` creates a non-executing runtime plan that lists MCP server commands, safe probe methods, risk reasons, and a stable approval digest before any local server is started. Add `--markdown --output runtime-plan.md` to preserve a review-ready approval artifact with the execution boundary, checklist, servers, probes, and risk reasons. `doctor runtime-policy <path>` evaluates the same runtime plan and security signals, then recommends `allow`, `review`, `sandbox_recommended`, or `deny` before local MCP execution starts. `doctor review-bundle <path> --output <dir> --sign-key-env NAME` writes a signed review directory with runtime plan, runtime policy, attestation, release evidence, manifest, Markdown summary files, and SHA-256 file integrity digests. `doctor review-bundle verify <bundle-dir> --target <path> --sign-key-env NAME` verifies the bundle manifest, expected files, manifest integrity digests, runtime artifacts, signed attestation, and signed release evidence offline before a reviewer trusts the handoff. `doctor review-bundle diff --before <dir> --after <dir>` compares two review bundles and flags risk-increasing changes in status, runtime policy, release readiness, signatures, release evidence, and runtime plan digest. `check --runtime --require-runtime-approval --runtime-approval-digest <digest>` refuses to run runtime probes unless the current plan digest matches the approved digest. `doctor release-evidence <path> --sign-key-env NAME` creates one redacted release bundle with signed attestation, offline verification, corpus, performance, security, trust, package metadata, git release gates, and runtime approval status. Strict release evidence requires a clean tagged worktree; use `--allow-dirty` or `--allow-untagged` only for local rehearsal. `doctor release-evidence verify <evidence.json> --target <path> --sign-key-env NAME` verifies a shared release evidence artifact offline against an explicit package path; the artifact target path is treated as display metadata, not trusted input. `doctor release-evidence asset <path> --tag <tag> --output <evidence.json> --sign-key-env NAME` writes a signed release evidence file and prints the `gh release upload` command; add `--upload` to run the upload through GitHub CLI with `--clobber`. `doctor inspector <path>` builds a safe MCP Inspector launch command from a packaged `.mcp.json` file without starting the Inspector proxy automatically. Use `--server <name>` when the package contains multiple MCP server entries. `doctor diff --before <path> --after <path>` compares two package roots and reports new findings, resolved findings, trust score delta, and whether risk increased. `doctor recommend <path>` turns validation, security, and compatibility signals into a prioritized action plan with blocker, high, medium, and info actions. Add `--json` for automation or `--output recommendations.json` to write the report to disk. `doctor trust <path>` creates a local trust score from package lifecycle scripts, dependency specs, and MCP security findings. Use it before release when you want supply-chain risks summarized as one score. `doctor perf <path>` profiles the shared package analysis pipeline and reports per-stage durations for validation, config, security, compatibility, trust, recommendations, and total runtime. Add `--max-total-ms <ms>` or repeatable `--max-stage-ms stage=ms` to fail CI when a budget is exceeded. `doctor mcp <path>` exposes the generic MCP static health report under the doctor command family without starting local MCP servers. `doctor export --bundle <path>` creates a redacted operator handoff bundle that includes validation JSON, security scorecard data, compatibility matrix, recommendations, and trust score in one file. `doctor snapshot` creates a redacted diagnostics bundle with environment health, client config readiness, installed plugin metadata, and next commands. Add `--json` for machine-readable output or `--output doctor-snapshot.json` to write the bundle to disk. `doctor clients` reports local Codex, Claude Desktop, Cursor, Cline, and Windsurf config readiness. `doctor --update-check` compares the installed CLI version with the latest npm version and prints the upgrade command when a newer release is available.
|
|
302
|
+
|
|
303
|
+
`audit --installed` runs a local ecosystem audit against every discovered Codex plugin in the installed plugin cache. Add `--security` to include security scorecards, `--compat` to include the all-client compatibility matrix, and `--json --output local-audit.json` when you want a shareable machine-readable report. Add `--cache` to reuse unchanged plugin results between runs; add `--changed` to only report plugins whose fingerprint changed since the last cached audit. Use `--cache-file path/to/audit-cache.json` when CI or scripted runs need an explicit cache location.
|
|
304
|
+
|
|
305
|
+
`audit deps <path>` runs `npm audit --json` for the target package and normalizes dependency vulnerability results into a stable `schemaVersion: "1.0.0"` report. Use `--json --output dependency-audit.json` when CI needs machine-readable status, vulnerability counts, and the raw npm audit payload.
|
|
302
306
|
|
|
303
|
-
`
|
|
304
|
-
|
|
305
|
-
`
|
|
306
|
-
|
|
307
|
-
`
|
|
308
|
-
|
|
309
|
-
`init-git-hooks [path]` installs generated pre-commit and pre-push hooks that run Codex Plugin Doctor validation before local commits and pushes. Add `--force` to overwrite existing hooks and `--json` when setup automation needs the written hook paths.
|
|
310
|
-
|
|
311
|
-
`--policy codex-publish|mcp-strict|security` applies opinionated gates without requiring a local `.codex-doctor.json`. `codex-publish` fails warnings and enables runtime probes for release checks, `mcp-strict` does the same for MCP-heavy packages, and `security` fails warning-level security findings so advisory risks can block a local audit or CI gate.
|
|
307
|
+
`watch <path>` continuously validates a plugin package after file changes. Use `--debounce-ms <ms>` to tune noisy repositories, `--runtime` when runtime probes are required, and `--json --output watch-latest.json` when another process should consume the latest validation snapshot.
|
|
308
|
+
|
|
309
|
+
`init-git-hooks [path]` installs generated pre-commit and pre-push hooks that run Codex Plugin Doctor validation before local commits and pushes. Add `--force` to overwrite existing hooks and `--json` when setup automation needs the written hook paths.
|
|
310
|
+
|
|
311
|
+
`--policy codex-publish|mcp-strict|security` applies opinionated gates without requiring a local `.codex-doctor.json`. `codex-publish` fails warnings and enables runtime probes for release checks, `mcp-strict` does the same for MCP-heavy packages, and `security` fails warning-level security findings so advisory risks can block a local audit or CI gate.
|
|
312
312
|
|
|
313
313
|
`mcp <path>` diagnoses generic MCP packages that may not have a Codex plugin manifest. It looks for `.mcp.json` or a manifest `mcpServers` reference, validates the top-level `mcpServers` object and server transports, adds MCP command-surface security findings, and includes the all-client compatibility matrix in the same report.
|
|
314
314
|
|
|
315
315
|
`init [path] --template ...` creates targeted starter packages. `skill-only` is the default minimal skill package, `mcp-stdio` adds a local stdio MCP config and mock server, `mcp-http` scaffolds a streamable HTTP MCP config, and `full-runtime` generates a stdio sample that passes the runtime protocol probes.
|
|
316
316
|
|
|
317
|
-
`security <path>` renders a focused package security scorecard. It reuses the existing package security findings, then adds deeper MCP command-surface checks for shell wrappers, encoded shell payloads, remote pipe-to-shell startup patterns, `cwd` values outside the plugin root, and plain HTTP URLs. Findings include structured, instance-specific evidence such as manifest fields, skill paths, server names, protocol methods, dependency sections, env keys, and bounded payload lengths. Deterministic SHA-256 fingerprints derive from the rule ID and canonical evidence, with package paths normalized for cross-machine stability. Secrets, prompt bodies, resource contents, tool payloads, stderr, and encoded commands are not copied into evidence. Use `--json` for automation or `--scorecard` for a compact status view.
|
|
317
|
+
`security <path>` renders a focused package security scorecard. It reuses the existing package security findings, then adds deeper MCP command-surface checks for shell wrappers, encoded shell payloads, remote pipe-to-shell startup patterns, `cwd` values outside the plugin root, and plain HTTP URLs. Findings include structured, instance-specific evidence such as manifest fields, skill paths, server names, protocol methods, dependency sections, env keys, and bounded payload lengths. Deterministic SHA-256 fingerprints derive from the rule ID and canonical evidence, with package paths normalized for cross-machine stability. Secrets, prompt bodies, resource contents, tool payloads, stderr, and encoded commands are not copied into evidence. Use `--json` for automation or `--scorecard` for a compact status view.
|
|
318
318
|
|
|
319
319
|
`compat --client claude-desktop` checks whether the MCP package can be added to the local Claude Desktop setup. On Windows it looks for `%APPDATA%\Claude\claude_desktop_config.json`; on macOS it looks for `~/Library/Application Support/Claude/claude_desktop_config.json`. A valid existing config returns `PASS`, a missing Claude Desktop install returns `WARN`, and a malformed local config returns `FAIL` so you do not add new servers into a broken config file. If the package server name already exists in Claude Desktop, the command returns `WARN` with the duplicate server name. Add `--install-preview` to print the JSON snippet that should be merged into `claude_desktop_config.json`; it does not modify files. Use `--apply --backup` only when you want the CLI to create a timestamped backup and merge the server config. Apply mode refuses to overwrite duplicate server names.
|
|
320
320
|
|
|
@@ -341,10 +341,40 @@ Optional local policy file:
|
|
|
341
341
|
```json
|
|
342
342
|
{
|
|
343
343
|
"ignoreRules": ["plugin.heuristic.description.too_long"],
|
|
344
|
+
"suppressions": [
|
|
345
|
+
{
|
|
346
|
+
"fingerprint": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
|
347
|
+
"reason": "Accepted until the upstream package layout is corrected.",
|
|
348
|
+
"expiresAt": "2026-07-31"
|
|
349
|
+
}
|
|
350
|
+
],
|
|
344
351
|
"failOnWarnings": true
|
|
345
352
|
}
|
|
346
353
|
```
|
|
347
354
|
|
|
355
|
+
`ignoreRules` disables every finding with a matching rule ID. Prefer `suppressions` when accepting one specific finding instance: each record requires the exact 64-character lowercase SHA-256 fingerprint shown in Doctor reports, a non-empty review reason, and a real `YYYY-MM-DD` expiration date.
|
|
356
|
+
|
|
357
|
+
Active suppressions remove only the matching finding from the active result and preserve it under `suppressedFindings` for audit output. Expired or invalid records do not suppress findings; Doctor emits `suppression.expired` or `suppression.invalid` warnings instead. Duplicate active records use the first valid match.
|
|
358
|
+
|
|
359
|
+
### Manage suppressions
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
codex-plugin-doctor suppress add .
|
|
363
|
+
codex-plugin-doctor suppress list .
|
|
364
|
+
codex-plugin-doctor suppress remove .
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Interactive add selects one active fingerprinted finding and defaults to an expiration 30 local calendar days from today. The stored `YYYY-MM-DD` record remains active through that UTC calendar date and expires at `00:00 UTC` on the following day. Suppressions match only the exact finding fingerprint; invalid or expired records never hide findings.
|
|
368
|
+
|
|
369
|
+
For automation, supply all add fields or one remove selector and add `--json` when machine-readable output is required:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
codex-plugin-doctor suppress add . --fingerprint <sha256> --reason "Accepted temporarily." --expires-at 2026-07-31 --json
|
|
373
|
+
codex-plugin-doctor suppress list . --json
|
|
374
|
+
codex-plugin-doctor suppress remove . --index 0 --json
|
|
375
|
+
codex-plugin-doctor suppress remove . --fingerprint <sha256> --json
|
|
376
|
+
```
|
|
377
|
+
|
|
348
378
|
Run these when you want Codex Plugin Doctor to find plugins from the local Codex installation:
|
|
349
379
|
|
|
350
380
|
```bash
|
|
@@ -369,22 +399,22 @@ jobs:
|
|
|
369
399
|
doctor:
|
|
370
400
|
runs-on: ubuntu-latest
|
|
371
401
|
steps:
|
|
372
|
-
- uses: actions/checkout@v5
|
|
373
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
374
|
-
with:
|
|
375
|
-
version: "1.
|
|
402
|
+
- uses: actions/checkout@v5
|
|
403
|
+
- uses: Esquetta/CodexPluginDoctor@v1.28.0
|
|
404
|
+
with:
|
|
405
|
+
version: "1.28.0"
|
|
376
406
|
path: .
|
|
377
|
-
runtime: "true"
|
|
378
|
-
policy: codex-publish
|
|
379
|
-
corpus: "true"
|
|
380
|
-
contract: "true"
|
|
381
|
-
upload-artifact: "true"
|
|
382
|
-
artifact-name: codex-plugin-doctor-reports
|
|
383
|
-
review-bundle: "true"
|
|
384
|
-
review-bundle-verify: "true"
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
The action writes `codex-plugin-doctor-summary.md`, `codex-plugin-doctor-report.json`, `codex-plugin-doctor-action-manifest.json`, optional `codex-plugin-doctor.sarif`, optional `validation-corpus.json`, optional `output-contract.json`, and optional signed `review-bundle/` files to `codex-plugin-doctor-reports`, appends the Markdown report to the GitHub Actions step summary, uploads the report directory as an artifact, and then returns the real validation exit code. Review bundle generation requires a signing key environment variable such as `CODEX_PLUGIN_DOCTOR_SIGNING_KEY`. For runtime probing, SARIF output, corpus and contract artifacts, review bundle artifacts, installed plugin cache checks, CI policy presets, and pinned release examples, see [GitHub Action Usage](./docs/
|
|
407
|
+
runtime: "true"
|
|
408
|
+
policy: codex-publish
|
|
409
|
+
corpus: "true"
|
|
410
|
+
contract: "true"
|
|
411
|
+
upload-artifact: "true"
|
|
412
|
+
artifact-name: codex-plugin-doctor-reports
|
|
413
|
+
review-bundle: "true"
|
|
414
|
+
review-bundle-verify: "true"
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
The action writes `codex-plugin-doctor-summary.md`, `codex-plugin-doctor-report.json`, `codex-plugin-doctor-action-manifest.json`, optional `codex-plugin-doctor.sarif`, optional `validation-corpus.json`, optional `output-contract.json`, and optional signed `review-bundle/` files to `codex-plugin-doctor-reports`, appends the Markdown report to the GitHub Actions step summary, uploads the report directory as an artifact, and then returns the real validation exit code. Review bundle generation requires a signing key environment variable such as `CODEX_PLUGIN_DOCTOR_SIGNING_KEY`. For runtime probing, SARIF output, corpus and contract artifacts, review bundle artifacts, installed plugin cache checks, CI policy presets, and pinned release examples, see [GitHub Action Usage](./docs/guides/github-action.md).
|
|
388
418
|
|
|
389
419
|
To self-test this repository after cloning it:
|
|
390
420
|
|
|
@@ -395,7 +425,7 @@ codex-plugin-doctor check examples/codex-doctor-runtime --runtime --no-animation
|
|
|
395
425
|
## Repository Layout
|
|
396
426
|
|
|
397
427
|
```text
|
|
398
|
-
docs/
|
|
428
|
+
docs/ Public architecture, guides, rules, security, and contributor docs
|
|
399
429
|
examples/ Manual plugin packs for local CLI testing
|
|
400
430
|
src/ CLI, validation logic, runtime probing, reports
|
|
401
431
|
tests/ Fixture-based regression tests
|
|
@@ -406,7 +436,7 @@ validation-sessions/ Real-world validation waves and tuning notes
|
|
|
406
436
|
|
|
407
437
|
The validator is tuned against local fixtures and real marketplace-style plugin packages. See:
|
|
408
438
|
|
|
409
|
-
- [Real-World Validation Workflow](./docs/
|
|
439
|
+
- [Real-World Validation Workflow](./docs/guides/real-world-validation.md)
|
|
410
440
|
- [Validation Sessions](./validation-sessions/README.md)
|
|
411
441
|
- [Examples](./examples/README.md)
|
|
412
442
|
- [Rule Catalog](./docs/rules/catalog.md)
|
|
@@ -435,9 +465,9 @@ Related docs:
|
|
|
435
465
|
- [Contributing](./CONTRIBUTING.md)
|
|
436
466
|
- [Security Policy](./SECURITY.md)
|
|
437
467
|
- [Code of Conduct](./CODE_OF_CONDUCT.md)
|
|
438
|
-
- [NPM Release Checklist](./docs/
|
|
439
|
-
- [Release Candidate Workflow](./docs/
|
|
440
|
-
- [
|
|
468
|
+
- [NPM Release Checklist](./docs/contributing/npm-release-checklist.md)
|
|
469
|
+
- [Release Candidate Workflow](./docs/contributing/release-candidates.md)
|
|
470
|
+
- [Versioning and Releases](./docs/contributing/releasing.md)
|
|
441
471
|
|
|
442
472
|
## Contributing
|
|
443
473
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { rename as renameFile } from "node:fs/promises";
|
|
2
|
+
export type RawDoctorConfig = Record<string, unknown>;
|
|
3
|
+
export interface LoadedDoctorConfig {
|
|
4
|
+
configPath: string;
|
|
5
|
+
exists: boolean;
|
|
6
|
+
value: RawDoctorConfig;
|
|
7
|
+
}
|
|
8
|
+
export declare function resolveDoctorConfigPath(targetPath: string, explicitConfigPath?: string | null): string;
|
|
9
|
+
export declare function readRawDoctorConfig(targetPath: string, explicitConfigPath?: string | null): Promise<LoadedDoctorConfig>;
|
|
10
|
+
export declare function writeRawDoctorConfig(configPath: string, value: RawDoctorConfig, options?: {
|
|
11
|
+
rename?: typeof renameFile;
|
|
12
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { mkdir, readFile, rename as renameFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseJsonText } from "./read-json-file.js";
|
|
5
|
+
export function resolveDoctorConfigPath(targetPath, explicitConfigPath) {
|
|
6
|
+
return explicitConfigPath
|
|
7
|
+
? path.resolve(explicitConfigPath)
|
|
8
|
+
: path.join(path.resolve(targetPath), ".codex-doctor.json");
|
|
9
|
+
}
|
|
10
|
+
function isRawDoctorConfig(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
export async function readRawDoctorConfig(targetPath, explicitConfigPath) {
|
|
14
|
+
const configPath = resolveDoctorConfigPath(targetPath, explicitConfigPath);
|
|
15
|
+
try {
|
|
16
|
+
const parsed = parseJsonText(await readFile(configPath, "utf8"));
|
|
17
|
+
if (!isRawDoctorConfig(parsed)) {
|
|
18
|
+
throw new Error("Doctor config root must be a JSON object");
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
configPath,
|
|
22
|
+
exists: true,
|
|
23
|
+
value: parsed
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (typeof error === "object" &&
|
|
28
|
+
error !== null &&
|
|
29
|
+
"code" in error &&
|
|
30
|
+
error.code === "ENOENT") {
|
|
31
|
+
return {
|
|
32
|
+
configPath,
|
|
33
|
+
exists: false,
|
|
34
|
+
value: {}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof SyntaxError) {
|
|
38
|
+
throw new Error(`Unable to parse Doctor config at ${configPath}: ${error.message}`, { cause: error });
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function writeRawDoctorConfig(configPath, value, options = {}) {
|
|
44
|
+
const rename = options.rename ?? renameFile;
|
|
45
|
+
const directoryPath = path.dirname(configPath);
|
|
46
|
+
const tempPath = path.join(directoryPath, `${path.basename(configPath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
47
|
+
await mkdir(directoryPath, { recursive: true });
|
|
48
|
+
try {
|
|
49
|
+
await writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
50
|
+
await rename(tempPath, configPath);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
try {
|
|
54
|
+
await rm(tempPath, { force: true });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Best effort cleanup should not replace the original failure.
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -2,6 +2,9 @@ import type { CheckResult } from "../domain/types.js";
|
|
|
2
2
|
export interface DoctorConfig {
|
|
3
3
|
ignoreRules: string[];
|
|
4
4
|
failOnWarnings: boolean;
|
|
5
|
+
suppressions: unknown[];
|
|
5
6
|
}
|
|
6
7
|
export declare function loadDoctorConfig(targetPath: string, explicitConfigPath?: string | null): Promise<DoctorConfig>;
|
|
7
|
-
export declare function applyDoctorConfig(result: CheckResult, config: DoctorConfig
|
|
8
|
+
export declare function applyDoctorConfig(result: CheckResult, config: DoctorConfig, options?: {
|
|
9
|
+
now?: Date;
|
|
10
|
+
}): CheckResult;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { withFindingFingerprint } from "../reporting/finding-fingerprint.js";
|
|
4
|
+
import { classifySuppressionRecord } from "./suppression-record.js";
|
|
3
5
|
const defaultConfig = {
|
|
4
6
|
ignoreRules: [],
|
|
5
|
-
failOnWarnings: false
|
|
7
|
+
failOnWarnings: false,
|
|
8
|
+
suppressions: []
|
|
6
9
|
};
|
|
7
10
|
function isStringArray(value) {
|
|
8
11
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
@@ -19,15 +22,69 @@ export async function loadDoctorConfig(targetPath, explicitConfigPath) {
|
|
|
19
22
|
: defaultConfig.ignoreRules,
|
|
20
23
|
failOnWarnings: typeof parsed.failOnWarnings === "boolean"
|
|
21
24
|
? parsed.failOnWarnings
|
|
22
|
-
: defaultConfig.failOnWarnings
|
|
25
|
+
: defaultConfig.failOnWarnings,
|
|
26
|
+
suppressions: Array.isArray(parsed.suppressions)
|
|
27
|
+
? parsed.suppressions
|
|
28
|
+
: defaultConfig.suppressions
|
|
23
29
|
};
|
|
24
30
|
}
|
|
25
31
|
catch {
|
|
26
32
|
return defaultConfig;
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
|
-
export function applyDoctorConfig(result, config) {
|
|
30
|
-
const
|
|
35
|
+
export function applyDoctorConfig(result, config, options = {}) {
|
|
36
|
+
const configuredFindings = result.findings.filter((finding) => !config.ignoreRules.includes(finding.id));
|
|
37
|
+
const now = options.now ?? new Date();
|
|
38
|
+
const activeSuppressions = new Map();
|
|
39
|
+
const governanceWarnings = [];
|
|
40
|
+
const summary = {
|
|
41
|
+
applied: 0,
|
|
42
|
+
expired: 0,
|
|
43
|
+
invalid: 0
|
|
44
|
+
};
|
|
45
|
+
for (const [index, suppression] of config.suppressions.entries()) {
|
|
46
|
+
const classified = classifySuppressionRecord(suppression, now);
|
|
47
|
+
if (classified.status === "invalid") {
|
|
48
|
+
summary.invalid += 1;
|
|
49
|
+
governanceWarnings.push(buildGovernanceWarning(result.targetPath, "suppression.invalid", "A targeted suppression record is invalid and was not applied.", "Invalid suppression records can create false confidence because they do not match or expire as intended.", "Fix or remove the invalid suppression record in `.codex-doctor.json`.", {
|
|
50
|
+
suppressionIndex: index,
|
|
51
|
+
field: classified.field
|
|
52
|
+
}));
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (classified.status === "expired") {
|
|
56
|
+
summary.expired += 1;
|
|
57
|
+
governanceWarnings.push(buildGovernanceWarning(result.targetPath, "suppression.expired", "A targeted suppression has expired and was not applied.", "Expired risk acceptance must be reviewed again before the finding can be suppressed.", "Remove the expired suppression or replace it with a newly reviewed expiration date.", {
|
|
58
|
+
suppressionIndex: index,
|
|
59
|
+
fingerprint: classified.fingerprint,
|
|
60
|
+
expiresAt: classified.expiresAt
|
|
61
|
+
}));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!activeSuppressions.has(classified.fingerprint)) {
|
|
65
|
+
activeSuppressions.set(classified.fingerprint, {
|
|
66
|
+
reason: classified.reason,
|
|
67
|
+
expiresAt: classified.expiresAt
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const findings = [];
|
|
72
|
+
const suppressedFindings = [];
|
|
73
|
+
for (const finding of configuredFindings) {
|
|
74
|
+
const suppression = finding.fingerprint
|
|
75
|
+
? activeSuppressions.get(finding.fingerprint)
|
|
76
|
+
: undefined;
|
|
77
|
+
if (!suppression) {
|
|
78
|
+
findings.push(finding);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
summary.applied += 1;
|
|
82
|
+
suppressedFindings.push({
|
|
83
|
+
...finding,
|
|
84
|
+
suppression
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
findings.push(...governanceWarnings);
|
|
31
88
|
const hasFailures = findings.some((finding) => finding.severity === "fail");
|
|
32
89
|
const hasWarnings = findings.some((finding) => finding.severity === "warn");
|
|
33
90
|
const warningFailure = config.failOnWarnings && hasWarnings;
|
|
@@ -40,6 +97,22 @@ export function applyDoctorConfig(result, config) {
|
|
|
40
97
|
...result,
|
|
41
98
|
status,
|
|
42
99
|
exitCode: status === "fail" ? 1 : 0,
|
|
43
|
-
findings
|
|
100
|
+
findings,
|
|
101
|
+
...(config.suppressions.length > 0
|
|
102
|
+
? {
|
|
103
|
+
suppressedFindings,
|
|
104
|
+
suppressionSummary: summary
|
|
105
|
+
}
|
|
106
|
+
: {})
|
|
44
107
|
};
|
|
45
108
|
}
|
|
109
|
+
function buildGovernanceWarning(targetPath, id, message, impact, suggestedFix, evidence) {
|
|
110
|
+
return withFindingFingerprint({
|
|
111
|
+
id,
|
|
112
|
+
severity: "warn",
|
|
113
|
+
message,
|
|
114
|
+
impact,
|
|
115
|
+
suggestedFix,
|
|
116
|
+
evidence
|
|
117
|
+
}, targetPath);
|
|
118
|
+
}
|
|
@@ -16,6 +16,13 @@ function npmCommand(args) {
|
|
|
16
16
|
}
|
|
17
17
|
return { command: "npm", args };
|
|
18
18
|
}
|
|
19
|
+
function npmPackEnvironment() {
|
|
20
|
+
const environment = Object.fromEntries(Object.entries(process.env).filter(([key]) => key.toLowerCase() !== "npm_config_dry_run"));
|
|
21
|
+
return {
|
|
22
|
+
...environment,
|
|
23
|
+
npm_config_dry_run: "false"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
19
26
|
function isPathWithinRoot(rootPath, candidatePath) {
|
|
20
27
|
const relativePath = path.relative(rootPath, candidatePath);
|
|
21
28
|
return (relativePath === "" ||
|
|
@@ -96,6 +103,7 @@ async function packNpmPackage(packageSpec, destinationPath) {
|
|
|
96
103
|
]);
|
|
97
104
|
const { stdout } = await execFileAsync(npmPackCommand.command, npmPackCommand.args, {
|
|
98
105
|
cwd: destinationPath,
|
|
106
|
+
env: npmPackEnvironment(),
|
|
99
107
|
maxBuffer: 10 * 1024 * 1024
|
|
100
108
|
});
|
|
101
109
|
const packEntries = JSON.parse(stdout);
|
|
@@ -55,6 +55,24 @@ const publicSchemaDefinitions = [
|
|
|
55
55
|
outputKind: "doctor.git.hooks",
|
|
56
56
|
required: ["schemaVersion", "kind", "rootPath", "hookPaths", "preExisting"]
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
id: "doctor.suppress.add.json",
|
|
60
|
+
command: "codex-plugin-doctor suppress add <path> --fingerprint <sha256> --reason <text> --expires-at YYYY-MM-DD --json",
|
|
61
|
+
outputKind: "doctor.suppress.add",
|
|
62
|
+
required: ["schemaVersion", "kind", "command", "configPath", "index", "suppression"]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "doctor.suppress.list.json",
|
|
66
|
+
command: "codex-plugin-doctor suppress list <path> --json",
|
|
67
|
+
outputKind: "doctor.suppress.list",
|
|
68
|
+
required: ["schemaVersion", "kind", "command", "configPath", "suppressions"]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "doctor.suppress.remove.json",
|
|
72
|
+
command: "codex-plugin-doctor suppress remove <path> --index <n> --json",
|
|
73
|
+
outputKind: "doctor.suppress.remove",
|
|
74
|
+
required: ["schemaVersion", "kind", "command", "configPath", "index", "suppression"]
|
|
75
|
+
},
|
|
58
76
|
{
|
|
59
77
|
id: "doctor.history.json",
|
|
60
78
|
command: "codex-plugin-doctor history <history.jsonl> --json",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RawDoctorConfig } from "./doctor-config-store.js";
|
|
2
|
+
import { type SuppressionRecordField } from "./suppression-record.js";
|
|
3
|
+
export interface ManagedSuppressionRecord {
|
|
4
|
+
index: number;
|
|
5
|
+
status: "active" | "expired" | "invalid";
|
|
6
|
+
fingerprint?: string;
|
|
7
|
+
reason?: string;
|
|
8
|
+
expiresAt?: string;
|
|
9
|
+
invalidField?: SuppressionRecordField;
|
|
10
|
+
}
|
|
11
|
+
export interface SuppressionMutationResult {
|
|
12
|
+
config: RawDoctorConfig;
|
|
13
|
+
index: number;
|
|
14
|
+
suppression: unknown;
|
|
15
|
+
}
|
|
16
|
+
export type SuppressionManagementErrorCode = "suppression_non_array" | "suppression_invalid_record" | "suppression_duplicate_fingerprint" | "suppression_invalid_index" | "suppression_invalid_fingerprint" | "suppression_fingerprint_not_found" | "suppression_fingerprint_ambiguous";
|
|
17
|
+
type SuppressionManagementErrorDetails = {
|
|
18
|
+
code: SuppressionManagementErrorCode;
|
|
19
|
+
field?: SuppressionRecordField;
|
|
20
|
+
index?: number;
|
|
21
|
+
indexes?: number[];
|
|
22
|
+
};
|
|
23
|
+
export declare class SuppressionManagementError extends Error {
|
|
24
|
+
readonly code: SuppressionManagementErrorCode;
|
|
25
|
+
readonly field?: SuppressionRecordField;
|
|
26
|
+
readonly index?: number;
|
|
27
|
+
readonly indexes?: number[];
|
|
28
|
+
constructor(message: string, details: SuppressionManagementErrorDetails);
|
|
29
|
+
}
|
|
30
|
+
export declare function listSuppressions(config: RawDoctorConfig, now?: Date): ManagedSuppressionRecord[];
|
|
31
|
+
export declare function addSuppression(config: RawDoctorConfig, record: unknown): SuppressionMutationResult;
|
|
32
|
+
export declare function removeSuppressionByIndex(config: RawDoctorConfig, index: number): SuppressionMutationResult;
|
|
33
|
+
export declare function removeSuppressionByFingerprint(config: RawDoctorConfig, fingerprint: string): SuppressionMutationResult;
|
|
34
|
+
export {};
|