codex-plugin-doctor 1.55.0 → 1.56.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.55.0
483
+ - uses: Esquetta/CodexPluginDoctor@v1.56.0
484
484
  with:
485
- version: "1.55.0"
485
+ version: "1.56.0"
486
486
  path: .
487
487
  runtime: "true"
488
488
  policy: codex-publish
@@ -0,0 +1,16 @@
1
+ export interface DoctorSizeReport {
2
+ targetPath: string;
3
+ status: "pass" | "warn" | "fail";
4
+ totalSize: number;
5
+ totalSizeHuman: string;
6
+ fileCount: number;
7
+ largeFiles: Array<{
8
+ path: string;
9
+ size: number;
10
+ sizeHuman: string;
11
+ }>;
12
+ warnings: string[];
13
+ }
14
+ export declare function buildDoctorSize(targetPath: string): Promise<DoctorSizeReport>;
15
+ export declare function renderDoctorSize(report: DoctorSizeReport): string;
16
+ export declare function renderDoctorSizeJson(report: DoctorSizeReport): string;
@@ -0,0 +1,139 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ const skippedDirs = new Set([
4
+ "node_modules",
5
+ ".git",
6
+ ".cache",
7
+ "dist",
8
+ "coverage",
9
+ "__pycache__",
10
+ ".turbo",
11
+ ".next",
12
+ ".nuxt"
13
+ ]);
14
+ const largeFileThreshold = 1024 * 1024;
15
+ const packageSizeWarnThreshold = 10 * 1024 * 1024;
16
+ const packageSizeFailThreshold = 50 * 1024 * 1024;
17
+ function formatSize(bytes) {
18
+ if (bytes < 1024)
19
+ return `${bytes} B`;
20
+ if (bytes < 1024 * 1024)
21
+ return `${(bytes / 1024).toFixed(1)} KB`;
22
+ if (bytes < 1024 * 1024 * 1024)
23
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
24
+ return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GB`;
25
+ }
26
+ async function scanDirectory(rootPath, currentPath = rootPath) {
27
+ let totalSize = 0;
28
+ let fileCount = 0;
29
+ const largeFiles = [];
30
+ let entries;
31
+ try {
32
+ entries = await readdir(currentPath, { withFileTypes: true });
33
+ }
34
+ catch {
35
+ return { totalSize: 0, fileCount: 0, largeFiles: [] };
36
+ }
37
+ for (const entry of entries) {
38
+ const entryPath = path.join(currentPath, entry.name);
39
+ if (entry.isDirectory()) {
40
+ if (skippedDirs.has(entry.name)) {
41
+ continue;
42
+ }
43
+ const subResult = await scanDirectory(rootPath, entryPath);
44
+ totalSize += subResult.totalSize;
45
+ fileCount += subResult.fileCount;
46
+ largeFiles.push(...subResult.largeFiles);
47
+ continue;
48
+ }
49
+ if (!entry.isFile()) {
50
+ continue;
51
+ }
52
+ let details;
53
+ try {
54
+ details = await stat(entryPath);
55
+ }
56
+ catch {
57
+ continue;
58
+ }
59
+ totalSize += details.size;
60
+ fileCount += 1;
61
+ if (details.size >= largeFileThreshold) {
62
+ largeFiles.push({
63
+ path: path.relative(rootPath, entryPath).replace(/\\/g, "/"),
64
+ size: details.size,
65
+ sizeHuman: formatSize(details.size)
66
+ });
67
+ }
68
+ }
69
+ return { totalSize, fileCount, largeFiles };
70
+ }
71
+ export async function buildDoctorSize(targetPath) {
72
+ const resolvedPath = path.resolve(targetPath);
73
+ const { totalSize, fileCount, largeFiles } = await scanDirectory(resolvedPath);
74
+ const warnings = [];
75
+ let status = "pass";
76
+ if (totalSize >= packageSizeFailThreshold) {
77
+ status = "fail";
78
+ warnings.push(`Package size (${formatSize(totalSize)}) exceeds ${formatSize(packageSizeFailThreshold)}.`);
79
+ }
80
+ else if (totalSize >= packageSizeWarnThreshold) {
81
+ status = "warn";
82
+ warnings.push(`Package size (${formatSize(totalSize)}) exceeds ${formatSize(packageSizeWarnThreshold)}.`);
83
+ }
84
+ if (largeFiles.length > 0) {
85
+ if (status === "pass")
86
+ status = "warn";
87
+ warnings.push(`${largeFiles.length} file(s) exceed ${formatSize(largeFileThreshold)}.`);
88
+ }
89
+ largeFiles.sort((a, b) => b.size - a.size);
90
+ return {
91
+ targetPath: resolvedPath,
92
+ status,
93
+ totalSize,
94
+ totalSizeHuman: formatSize(totalSize),
95
+ fileCount,
96
+ largeFiles: largeFiles.slice(0, 10),
97
+ warnings
98
+ };
99
+ }
100
+ export function renderDoctorSize(report) {
101
+ const lines = [
102
+ "Codex Plugin Doctor Size Analysis",
103
+ "=================================",
104
+ `Path: ${report.targetPath}`,
105
+ `Status: ${report.status.toUpperCase()}`,
106
+ `Total size: ${report.totalSizeHuman} (${report.fileCount} files)`,
107
+ ""
108
+ ];
109
+ if (report.warnings.length > 0) {
110
+ lines.push("Warnings", "--------");
111
+ for (const warning of report.warnings) {
112
+ lines.push(` - ${warning}`);
113
+ }
114
+ lines.push("");
115
+ }
116
+ if (report.largeFiles.length > 0) {
117
+ lines.push("Large Files", "-----------");
118
+ for (const file of report.largeFiles) {
119
+ lines.push(` ${file.sizeHuman.padEnd(8)} ${file.path}`);
120
+ }
121
+ lines.push("");
122
+ }
123
+ if (report.status === "pass") {
124
+ lines.push("Package size within acceptable limits.");
125
+ }
126
+ return lines.join("\n");
127
+ }
128
+ export function renderDoctorSizeJson(report) {
129
+ return JSON.stringify({
130
+ schemaVersion: "1.0.0",
131
+ targetPath: report.targetPath,
132
+ status: report.status,
133
+ totalSize: report.totalSize,
134
+ totalSizeHuman: report.totalSizeHuman,
135
+ fileCount: report.fileCount,
136
+ largeFiles: report.largeFiles,
137
+ warnings: report.warnings
138
+ }, null, 2);
139
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { CheckOptions, CheckResult } from "./domain/types.js";
2
+ export { buildDoctorSize, renderDoctorSize, renderDoctorSizeJson, type DoctorSizeReport } from "./core/doctor-size.js";
2
3
  export { applyDoctorConfig, loadDoctorConfig, type DoctorConfig } from "./core/doctor-config.js";
3
4
  export { readRawDoctorConfig, resolveDoctorConfigPath, writeRawDoctorConfig, type LoadedDoctorConfig, type RawDoctorConfig } from "./core/doctor-config-store.js";
4
5
  export { classifySuppressionRecord, isValidSuppressionFingerprint, isSuppressionExpired, validateSuppressionRecord, type ClassifiedSuppressionRecord, type InvalidSuppressionRecord, type SuppressionRecordField, type ValidSuppressionRecord } from "./core/suppression-record.js";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { validatePlugin } from "./core/validate-plugin.js";
2
+ export { buildDoctorSize, renderDoctorSize, renderDoctorSizeJson } from "./core/doctor-size.js";
2
3
  export { applyDoctorConfig, loadDoctorConfig } from "./core/doctor-config.js";
3
4
  export { readRawDoctorConfig, resolveDoctorConfigPath, writeRawDoctorConfig } from "./core/doctor-config-store.js";
4
5
  export { classifySuppressionRecord, isValidSuppressionFingerprint, isSuppressionExpired, validateSuppressionRecord } from "./core/suppression-record.js";
package/dist/run-cli.js CHANGED
@@ -34,6 +34,7 @@ import { buildDoctorRiskDiffReport, renderDoctorRiskDiffReport, renderDoctorRisk
34
34
  import { buildDoctorInspectorReport, renderDoctorInspectorReport, renderDoctorInspectorReportJson } from "./core/inspector-bridge.js";
35
35
  import { applyFixPlan, buildFixPlan, renderApplyFixResult, renderFixPlanJsonReport, renderFixPlan } from "./core/fix-plan.js";
36
36
  import { renderClientDoctor, renderEnvironmentDoctor, renderEnvironmentDoctorJson } from "./core/environment-doctor.js";
37
+ import { buildDoctorSize, renderDoctorSize, renderDoctorSizeJson } from "./core/doctor-size.js";
37
38
  import { initCiWorkflow } from "./core/init-ci.js";
38
39
  import { watchPlugin } from "./core/watch-plugin.js";
39
40
  import { buildDepAudit, renderDepAudit, renderDepAuditJson, renderDepAuditSarif } from "./core/dep-audit.js";
@@ -2101,6 +2102,19 @@ export async function runCli(args, io = defaultIo, options = {}) {
2101
2102
  io.writeStdout(await renderClientDoctor(terminalContext));
2102
2103
  return 0;
2103
2104
  }
2105
+ if (maybePath === "size") {
2106
+ const sizeTarget = remainingArgs[0] && !remainingArgs[0].startsWith("--") ? remainingArgs[0] : ".";
2107
+ const sizeFlags = remainingArgs[0] && remainingArgs[0].startsWith("--")
2108
+ ? remainingArgs
2109
+ : remainingArgs.slice(1);
2110
+ const jsonOutput = sizeFlags.includes("--json");
2111
+ const report = await buildDoctorSize(sizeTarget);
2112
+ const output = jsonOutput
2113
+ ? renderDoctorSizeJson(report)
2114
+ : renderDoctorSize(report);
2115
+ io.writeStdout(output);
2116
+ return report.status === "fail" ? 1 : 0;
2117
+ }
2104
2118
  io.writeStdout(maybePath === "--json"
2105
2119
  ? await renderEnvironmentDoctorJson(terminalContext)
2106
2120
  : await renderEnvironmentDoctor(terminalContext));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plugin-doctor",
3
- "version": "1.55.0",
3
+ "version": "1.56.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",