codex-plugin-doctor 1.56.0 → 1.57.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 CHANGED
@@ -480,9 +480,9 @@ jobs:
480
480
  runs-on: ubuntu-latest
481
481
  steps:
482
482
  - uses: actions/checkout@v5
483
- - uses: Esquetta/CodexPluginDoctor@v1.56.0
483
+ - uses: Esquetta/CodexPluginDoctor@v1.57.0
484
484
  with:
485
- version: "1.56.0"
485
+ version: "1.57.0"
486
486
  path: .
487
487
  runtime: "true"
488
488
  policy: codex-publish
@@ -10,7 +10,11 @@ export interface DoctorSizeReport {
10
10
  sizeHuman: string;
11
11
  }>;
12
12
  warnings: string[];
13
+ npmPackSize?: number;
14
+ npmPackSizeHuman?: string;
13
15
  }
14
- export declare function buildDoctorSize(targetPath: string): Promise<DoctorSizeReport>;
16
+ export declare function buildDoctorSize(targetPath: string, options?: {
17
+ npmPack?: boolean;
18
+ }): Promise<DoctorSizeReport>;
15
19
  export declare function renderDoctorSize(report: DoctorSizeReport): string;
16
20
  export declare function renderDoctorSizeJson(report: DoctorSizeReport): string;
@@ -1,4 +1,5 @@
1
1
  import { readdir, stat } from "node:fs/promises";
2
+ import { execFile } from "node:child_process";
2
3
  import path from "node:path";
3
4
  const skippedDirs = new Set([
4
5
  "node_modules",
@@ -68,11 +69,62 @@ async function scanDirectory(rootPath, currentPath = rootPath) {
68
69
  }
69
70
  return { totalSize, fileCount, largeFiles };
70
71
  }
71
- export async function buildDoctorSize(targetPath) {
72
+ export async function buildDoctorSize(targetPath, options = {}) {
72
73
  const resolvedPath = path.resolve(targetPath);
73
74
  const { totalSize, fileCount, largeFiles } = await scanDirectory(resolvedPath);
74
75
  const warnings = [];
75
76
  let status = "pass";
77
+ let npmPackSize;
78
+ let npmPackSizeHuman;
79
+ if (options.npmPack) {
80
+ try {
81
+ const packOutput = await new Promise((resolve, reject) => {
82
+ execFile("npm", ["pack", "--dry-run"], { cwd: resolvedPath, timeout: 30000, shell: process.platform === "win32" }, (error, stdout, stderr) => {
83
+ if (error) {
84
+ reject(new Error(stderr.trim() || error.message));
85
+ return;
86
+ }
87
+ resolve(stdout);
88
+ });
89
+ });
90
+ const tarballLine = packOutput.split("\n").find((line) => line.includes("total files") || line.includes("package size"));
91
+ if (tarballLine) {
92
+ const totalMatch = tarballLine.match(/(\d[\d.]*)\s*([KMG]?B)/i);
93
+ if (totalMatch) {
94
+ const value = Number.parseFloat(totalMatch[1]);
95
+ const unit = totalMatch[2].toUpperCase();
96
+ switch (unit) {
97
+ case "KB":
98
+ npmPackSize = Math.round(value * 1024);
99
+ break;
100
+ case "MB":
101
+ npmPackSize = Math.round(value * 1024 * 1024);
102
+ break;
103
+ case "GB":
104
+ npmPackSize = Math.round(value * 1024 * 1024 * 1024);
105
+ break;
106
+ default:
107
+ npmPackSize = Math.round(value);
108
+ }
109
+ npmPackSizeHuman = formatSize(npmPackSize);
110
+ }
111
+ }
112
+ if (npmPackSize === undefined) {
113
+ warnings.push("Could not determine npm pack size from output.");
114
+ }
115
+ else {
116
+ const diffPercent = totalSize > 0
117
+ ? Math.abs(Math.round(((npmPackSize - totalSize) / totalSize) * 100))
118
+ : 0;
119
+ if (diffPercent > 50 && npmPackSize > totalSize) {
120
+ warnings.push(`npm pack size (${npmPackSizeHuman}) differs from local size (${formatSize(totalSize)}) by ${diffPercent}%.`);
121
+ }
122
+ }
123
+ }
124
+ catch (error) {
125
+ warnings.push(`npm pack failed: ${error.message}`);
126
+ }
127
+ }
76
128
  if (totalSize >= packageSizeFailThreshold) {
77
129
  status = "fail";
78
130
  warnings.push(`Package size (${formatSize(totalSize)}) exceeds ${formatSize(packageSizeFailThreshold)}.`);
@@ -94,7 +146,9 @@ export async function buildDoctorSize(targetPath) {
94
146
  totalSizeHuman: formatSize(totalSize),
95
147
  fileCount,
96
148
  largeFiles: largeFiles.slice(0, 10),
97
- warnings
149
+ warnings,
150
+ npmPackSize,
151
+ npmPackSizeHuman
98
152
  };
99
153
  }
100
154
  export function renderDoctorSize(report) {
@@ -104,6 +158,7 @@ export function renderDoctorSize(report) {
104
158
  `Path: ${report.targetPath}`,
105
159
  `Status: ${report.status.toUpperCase()}`,
106
160
  `Total size: ${report.totalSizeHuman} (${report.fileCount} files)`,
161
+ report.npmPackSizeHuman ? `npm pack size: ${report.npmPackSizeHuman}` : "",
107
162
  ""
108
163
  ];
109
164
  if (report.warnings.length > 0) {
@@ -134,6 +189,10 @@ export function renderDoctorSizeJson(report) {
134
189
  totalSizeHuman: report.totalSizeHuman,
135
190
  fileCount: report.fileCount,
136
191
  largeFiles: report.largeFiles,
137
- warnings: report.warnings
192
+ warnings: report.warnings,
193
+ ...(report.npmPackSize !== undefined ? {
194
+ npmPackSize: report.npmPackSize,
195
+ npmPackSizeHuman: report.npmPackSizeHuman
196
+ } : {})
138
197
  }, null, 2);
139
198
  }
package/dist/run-cli.js CHANGED
@@ -169,7 +169,7 @@ function parseRemoteNetworkFlags(flags, runtime) {
169
169
  return { allowNetwork, allowLocalNetwork, allowSessionLifecycle, requireRemoteReliability };
170
170
  }
171
171
  function printUsage(io) {
172
- 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 [--allow-network [--allow-local-network]]] [--sandbox docker] [--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> [--runtime [--allow-network [--allow-local-network]]] [--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 [--manifest <corpus.json>] [--json] [--output <path>]|corpus metrics --manifest <corpus.json> [--json|--markdown] [--output <path>] [--min-precision <0..1>] [--min-recall <0..1>] [--max-false-positive-rate <0..1>]|runtime-plan <path> [--sandbox docker] [--json|--markdown] [--output <path>]|runtime-policy <path> [--sandbox docker] [--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 [--runtime [--allow-network [--allow-local-network]]] [--sandbox docker] [--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 [--runtime [--allow-network [--allow-local-network]]] [--sandbox docker] [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>] [--upload]|mcp <path> [--runtime [--allow-network [--allow-local-network]]]|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 completion bash|zsh|fish\n codex-plugin-doctor config validate <path> [--json]\n codex-plugin-doctor release check <path> [--json] [--runtime [--allow-network [--allow-local-network]]] [--sandbox docker]\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");
172
+ 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 [--allow-network [--allow-local-network]]] [--sandbox docker] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>] [--fail-on <rule-id>]\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> [--runtime [--allow-network [--allow-local-network]]] [--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 [--manifest <corpus.json>] [--json] [--output <path>]|corpus metrics --manifest <corpus.json> [--json|--markdown] [--output <path>] [--min-precision <0..1>] [--min-recall <0..1>] [--max-false-positive-rate <0..1>]|runtime-plan <path> [--sandbox docker] [--json|--markdown] [--output <path>]|runtime-policy <path> [--sandbox docker] [--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 [--runtime [--allow-network [--allow-local-network]]] [--sandbox docker] [--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 [--runtime [--allow-network [--allow-local-network]]] [--sandbox docker] [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>] [--upload]|mcp <path> [--runtime [--allow-network [--allow-local-network]]]|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 completion bash|zsh|fish\n codex-plugin-doctor config validate <path> [--json]\n codex-plugin-doctor release check <path> [--json] [--runtime [--allow-network [--allow-local-network]]] [--sandbox docker]\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");
173
173
  io.writeStderr("Registry readiness: codex-plugin-doctor registry check <server.json|directory> [--json] [--output <path>] [--require-registry-readiness]\n"
174
174
  + " codex-plugin-doctor registry inspect <server-name> --allow-network [--json] [--output <path>] [--require-registry-readiness]\n"
175
175
  + "Registry publication preflight: codex-plugin-doctor registry preflight <server.json|directory> [--allow-network] [--json] [--output <path>] [--require-publish-ready]");
@@ -2108,7 +2108,8 @@ export async function runCli(args, io = defaultIo, options = {}) {
2108
2108
  ? remainingArgs
2109
2109
  : remainingArgs.slice(1);
2110
2110
  const jsonOutput = sizeFlags.includes("--json");
2111
- const report = await buildDoctorSize(sizeTarget);
2111
+ const npmPack = sizeFlags.includes("--npm");
2112
+ const report = await buildDoctorSize(sizeTarget, { npmPack });
2112
2113
  const output = jsonOutput
2113
2114
  ? renderDoctorSizeJson(report)
2114
2115
  : renderDoctorSize(report);
@@ -2905,6 +2906,13 @@ export async function runCli(args, io = defaultIo, options = {}) {
2905
2906
  : normalizedFlags[runtimeApprovalDigestIndex + 1];
2906
2907
  const changedSinceIndex = normalizedFlags.indexOf("--changed-since");
2907
2908
  const changedSinceRef = changedSinceIndex === -1 ? null : normalizedFlags[changedSinceIndex + 1];
2909
+ const failOnRules = [];
2910
+ for (let i = 0; i < normalizedFlags.length; i += 1) {
2911
+ if (normalizedFlags[i] === "--fail-on" && normalizedFlags[i + 1] && !normalizedFlags[i + 1].startsWith("--")) {
2912
+ failOnRules.push(normalizedFlags[i + 1]);
2913
+ i += 1;
2914
+ }
2915
+ }
2908
2916
  const baselineIndex = normalizedFlags.indexOf("--baseline");
2909
2917
  const baselinePath = baselineIndex === -1 ? null : normalizedFlags[baselineIndex + 1];
2910
2918
  if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
@@ -3150,6 +3158,28 @@ export async function runCli(args, io = defaultIo, options = {}) {
3150
3158
  failOnWarnings: doctorConfig.failOnWarnings
3151
3159
  })
3152
3160
  : configuredResult;
3161
+ if (failOnRules.length > 0) {
3162
+ result.findings = result.findings.map((finding) => {
3163
+ if (finding.severity === "warn" && failOnRules.includes(finding.id)) {
3164
+ return { ...finding, severity: "fail" };
3165
+ }
3166
+ return finding;
3167
+ });
3168
+ const hasFail = result.findings.some((f) => f.severity === "fail");
3169
+ const hasWarn = result.findings.some((f) => f.severity === "warn");
3170
+ if (hasFail) {
3171
+ result.status = "fail";
3172
+ result.exitCode = 1;
3173
+ }
3174
+ else if (hasWarn) {
3175
+ result.status = "warn";
3176
+ result.exitCode = 0;
3177
+ }
3178
+ else {
3179
+ result.status = "pass";
3180
+ result.exitCode = 0;
3181
+ }
3182
+ }
3153
3183
  if (renderer) {
3154
3184
  if (result.status === "fail") {
3155
3185
  renderer.stopFailure("Validation failed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plugin-doctor",
3
- "version": "1.56.0",
3
+ "version": "1.57.0",
4
4
  "description": "CLI-first validator for Codex plugins, skills, and MCP package surfaces with runtime MCP protocol validation.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",