codex-plugin-doctor 1.73.0 → 1.74.1

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
@@ -149,7 +149,11 @@ live list with `tools-capture-url` (and `tools-capture-token-env`) or, for a loc
149
149
  stdio server with `runtime: "true"`, `tools-capture-server`, and skip the
150
150
  plugin package check with `check: "false"` in repositories that only ship an MCP
151
151
  server. With `tools-diff-comment: "true"` it also posts the comparison as one
152
- pull request comment that later runs update.
152
+ pull request comment that later runs update. `tools-capture-sandbox: docker`
153
+ starts a stdio server in the Docker sandbox, and
154
+ `codex-plugin-doctor init-ci . --tools-diff-server <name> --sandbox docker`
155
+ writes a pull request workflow that captures in one job and compares and
156
+ comments in another.
153
157
 
154
158
  Local stdio servers from a package's MCP configuration can be cataloged too.
155
159
  `--runtime` is required because this starts package code; the server is stopped
@@ -585,9 +589,9 @@ jobs:
585
589
  runs-on: ubuntu-latest
586
590
  steps:
587
591
  - uses: actions/checkout@v5
588
- - uses: Esquetta/CodexPluginDoctor@v1.73.0
592
+ - uses: Esquetta/CodexPluginDoctor@v1.74.1
589
593
  with:
590
- version: "1.73.0"
594
+ version: "1.74.1"
591
595
  path: .
592
596
  runtime: "true"
593
597
  policy: codex-publish
@@ -2,4 +2,10 @@ export interface InitCiResult {
2
2
  rootPath: string;
3
3
  workflowPath: string;
4
4
  }
5
+ export interface ToolsDiffWorkflowOptions {
6
+ server: string;
7
+ baseline: string;
8
+ sandbox: "docker" | null;
9
+ }
10
+ export declare function initToolsDiffWorkflow(targetPath: string, options: ToolsDiffWorkflowOptions): Promise<InitCiResult>;
5
11
  export declare function initCiWorkflow(targetPath: string): Promise<InitCiResult>;
@@ -15,7 +15,7 @@ function buildWorkflow() {
15
15
  " doctor:",
16
16
  " runs-on: ubuntu-latest",
17
17
  " steps:",
18
- " - uses: actions/checkout@v4",
18
+ " - uses: actions/checkout@v5",
19
19
  ` - uses: Esquetta/CodexPluginDoctor@v${packageVersion}`,
20
20
  " with:",
21
21
  ` version: \"${packageVersion}\"`,
@@ -31,6 +31,80 @@ function buildWorkflow() {
31
31
  ""
32
32
  ].join("\n");
33
33
  }
34
+ // JSON strings are valid YAML double-quoted scalars, so names and paths cannot break the document.
35
+ const yamlString = (value) => JSON.stringify(value);
36
+ /**
37
+ * A pull request workflow in two jobs: the first starts the stdio server and uploads its tool list,
38
+ * and the second compares it with the committed baseline and comments. Only the second job may write
39
+ * to pull requests, and it runs no package code.
40
+ */
41
+ function buildToolsDiffWorkflow(options) {
42
+ return [
43
+ "name: MCP tool diff",
44
+ "",
45
+ "on:",
46
+ " pull_request:",
47
+ "",
48
+ "permissions:",
49
+ " contents: read",
50
+ "",
51
+ "jobs:",
52
+ " capture:",
53
+ " runs-on: ubuntu-latest",
54
+ " steps:",
55
+ " - uses: actions/checkout@v5",
56
+ " with:",
57
+ " persist-credentials: false",
58
+ " # Add install or build steps here if the server needs them; the Docker sandbox has no network.",
59
+ ` - uses: Esquetta/CodexPluginDoctor@v${packageVersion}`,
60
+ " with:",
61
+ ` version: \"${packageVersion}\"`,
62
+ " path: .",
63
+ " check: \"false\"",
64
+ " runtime: \"true\"",
65
+ ` tools-capture-server: ${yamlString(options.server)}`,
66
+ ...(options.sandbox === "docker" ? [" tools-capture-sandbox: docker"] : []),
67
+ " # Catalog findings do not fail this job; the compare job re-checks the saved list.",
68
+ " tools-capture-defer-findings: \"true\"",
69
+ " artifact-name: mcp-tools-capture",
70
+ "",
71
+ " compare:",
72
+ " needs: capture",
73
+ " runs-on: ubuntu-latest",
74
+ " permissions:",
75
+ " contents: read",
76
+ " pull-requests: write",
77
+ " steps:",
78
+ " - uses: actions/checkout@v5",
79
+ " with:",
80
+ " persist-credentials: false",
81
+ " # Outside the checkout, so files from the pull request cannot shadow the downloaded list.",
82
+ " - uses: actions/download-artifact@v5",
83
+ " with:",
84
+ " name: mcp-tools-capture",
85
+ " path: ${{ runner.temp }}/mcp-tools-capture",
86
+ ` - uses: Esquetta/CodexPluginDoctor@v${packageVersion}`,
87
+ " with:",
88
+ ` version: \"${packageVersion}\"`,
89
+ " check: \"false\"",
90
+ ` tools-diff-before: ${yamlString(options.baseline)}`,
91
+ " tools-diff-after: ${{ runner.temp }}/mcp-tools-capture/mcp-tools-current.json",
92
+ " tools-diff-fail-on: breaking",
93
+ " tools-diff-comment: \"true\"",
94
+ " output-dir: ${{ runner.temp }}/codex-plugin-doctor-reports",
95
+ " artifact-name: mcp-tools-diff",
96
+ ""
97
+ ].join("\n");
98
+ }
99
+ export async function initToolsDiffWorkflow(targetPath, options) {
100
+ const rootPath = path.resolve(targetPath);
101
+ const workflowPath = path.join(rootPath, ".github", "workflows", "codex-plugin-doctor-tools.yml");
102
+ await mkdir(path.dirname(workflowPath), { recursive: true });
103
+ await writeFile(workflowPath, buildToolsDiffWorkflow(options), "utf8");
104
+ // doctor tools --save-response writes into an existing directory, so the baseline can be captured next.
105
+ await mkdir(path.dirname(path.resolve(rootPath, options.baseline)), { recursive: true });
106
+ return { rootPath, workflowPath };
107
+ }
34
108
  export async function initCiWorkflow(targetPath) {
35
109
  const rootPath = path.resolve(targetPath);
36
110
  const workflowPath = path.join(rootPath, ".github", "workflows", "codex-plugin-doctor.yml");
@@ -181,10 +181,8 @@ function compareBounds(inner, outer, codes, reasons) {
181
181
  */
182
182
  function numericBound(schema, inclusive, exclusive, lower) {
183
183
  if (typeof schema[exclusive] === "boolean") {
184
- // Draft-04 marks the inclusive keyword itself as exclusive.
184
+ // Draft-04 marks the inclusive keyword itself as exclusive; a boolean alone is invalid in every draft.
185
185
  const value = schema[inclusive];
186
- if (value === undefined)
187
- return undefined;
188
186
  return isFiniteNumber(value) ? { value, exclusive: schema[exclusive] } : null;
189
187
  }
190
188
  const candidates = [];
@@ -599,7 +597,15 @@ function compareSchema(before, after, direction, depth, equal, reasons, scope) {
599
597
  const beforeComposed = isComposition(resolvedBefore.schema);
600
598
  const afterComposed = isComposition(resolvedAfter.schema);
601
599
  if (beforeComposed !== afterComposed) {
602
- compareComposition(beforeComposed ? resolvedBefore.schema : before, afterComposed ? resolvedAfter.schema : after, direction, depth, equal, reasons, scope);
600
+ // The reference is kept only when an alternative can pair with it; otherwise its target compares
601
+ // with inline alternatives by type.
602
+ const composite = beforeComposed ? resolvedBefore.schema : resolvedAfter.schema;
603
+ const original = beforeComposed ? after : before;
604
+ const plainKey = alternativeKey(original);
605
+ const keepReference = (alternatives(composite) ?? [])
606
+ .some((alternative) => (plainKey !== null && alternativeKey(alternative) === plainKey) || sameMeaning(original, alternative, equal, scope));
607
+ const plain = keepReference ? original : (beforeComposed ? resolvedAfter.schema : resolvedBefore.schema);
608
+ compareComposition(beforeComposed ? composite : plain, beforeComposed ? plain : composite, direction, depth, equal, reasons, scope);
603
609
  return;
604
610
  }
605
611
  if (resolvedBefore.ref !== undefined || resolvedAfter.ref !== undefined) {
package/dist/run-cli.js CHANGED
@@ -49,7 +49,7 @@ import { buildDoctorInspectorReport, renderDoctorInspectorReport, renderDoctorIn
49
49
  import { applyFixPlan, buildFixPlan, renderApplyFixResult, renderFixPlanJsonReport, renderFixPlan } from "./core/fix-plan.js";
50
50
  import { renderClientDoctor, renderEnvironmentDoctor, renderEnvironmentDoctorJson } from "./core/environment-doctor.js";
51
51
  import { buildDoctorSize, renderDoctorSize, renderDoctorSizeJson } from "./core/doctor-size.js";
52
- import { initCiWorkflow } from "./core/init-ci.js";
52
+ import { initCiWorkflow, initToolsDiffWorkflow } from "./core/init-ci.js";
53
53
  import { watchPlugin } from "./core/watch-plugin.js";
54
54
  import { buildDepAudit, renderDepAudit, renderDepAuditJson, renderDepAuditSarif } from "./core/dep-audit.js";
55
55
  import { initGitHooks, removeGitHooks } from "./core/init-git-hooks.js";
@@ -117,6 +117,40 @@ const toolsUsage = "Usage: codex-plugin-doctor doctor tools <url> --allow-networ
117
117
  const discoveryUsage = "Usage: codex-plugin-doctor doctor discover <url> --allow-network [--allow-local-network] [--json] [--bearer-token-env <NAME>]";
118
118
  const bearerTokenHelp = "--bearer-token-env reads a bearer token from the named environment variable and sends it only over HTTPS or to an approved loopback target; the token is never printed or saved.";
119
119
  const ENVIRONMENT_VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
120
+ /** Reads init-ci --tools-diff-server, --baseline, and --sandbox; options is null when none is given. */
121
+ function parseToolsDiffWorkflowOptions(flags) {
122
+ const values = new Map();
123
+ for (let index = 0; index < flags.length; index += 1) {
124
+ if (!["--tools-diff-server", "--baseline", "--sandbox"].includes(flags[index]))
125
+ continue;
126
+ const value = flags[index + 1];
127
+ const present = value !== undefined && !value.startsWith("--");
128
+ values.set(flags[index], present ? value : undefined);
129
+ if (present)
130
+ index += 1;
131
+ }
132
+ if (values.size === 0)
133
+ return { options: null };
134
+ if (!values.has("--tools-diff-server"))
135
+ return { error: "--baseline and --sandbox require --tools-diff-server." };
136
+ // Control characters would break the generated workflow lines, GitHub evaluates "${{" in any
137
+ // workflow value, and the Action rejects values that start with "-".
138
+ const usable = (value) => value !== undefined && value.trim() !== "" && !value.startsWith("-") && !value.includes("${{")
139
+ && !/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/u.test(value);
140
+ const server = values.get("--tools-diff-server");
141
+ if (!usable(server))
142
+ return { error: "--tools-diff-server requires a server name." };
143
+ // The workflow runs on Linux from the repository root, so the baseline is a relative POSIX path.
144
+ const baseline = (values.has("--baseline") ? values.get("--baseline") : "mcp/tools-baseline.json")?.replaceAll("\\", "/");
145
+ if (!usable(baseline) || path.posix.isAbsolute(baseline) || /^[A-Za-z]:/u.test(baseline)
146
+ || path.posix.normalize(baseline).split("/").includes("..")) {
147
+ return { error: "--baseline requires a relative path inside the repository." };
148
+ }
149
+ const sandbox = values.get("--sandbox");
150
+ if (values.has("--sandbox") && sandbox !== "docker")
151
+ return { error: "--sandbox must be docker." };
152
+ return { options: { server, baseline, sandbox: sandbox === "docker" ? "docker" : null } };
153
+ }
120
154
  const MAX_FORWARDED_ENVIRONMENT_VARIABLES = 32;
121
155
  const forwardedEnvironmentHelp = "--env forwards the named host environment variable into the Docker sandbox by name; Doctor never prints its value, writes it to a report, or passes it as a command argument. Native servers already inherit Doctor's environment.";
122
156
  function resolveBearerToken(name, env) {
@@ -340,7 +374,7 @@ function parseRemoteNetworkFlags(flags, runtime) {
340
374
  return { allowNetwork, allowLocalNetwork, allowSessionLifecycle, requireRemoteReliability };
341
375
  }
342
376
  function printUsage(io) {
343
- 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");
377
+ 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] [--tools-diff-server <name> [--baseline <path>] [--sandbox docker]]\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");
344
378
  io.writeStderr(discoveryUsage);
345
379
  io.writeStderr(toolsUsage);
346
380
  io.writeStderr(toolsFileUsage);
@@ -1308,9 +1342,10 @@ export async function runCli(args, io = defaultIo, options = {}) {
1308
1342
  (command === "doctor" &&
1309
1343
  (["runtime-plan", "runtime-policy"].includes(maybePath ?? "") ||
1310
1344
  (maybePath === "release-evidence" && remainingArgs[0] !== "verify") ||
1311
- (maybePath === "tools" && remainingArgs.includes("--server"))));
1345
+ (maybePath === "tools" && remainingArgs.includes("--server")))) ||
1346
+ (command === "init-ci" && args.includes("--tools-diff-server"));
1312
1347
  if (args.includes("--sandbox") && !runtimeSandboxSupported) {
1313
- io.writeStderr("--sandbox is supported only by single-package check, release check, runtime-plan, runtime-policy, release-evidence, and doctor tools --server.");
1348
+ io.writeStderr("--sandbox is supported only by single-package check, release check, runtime-plan, runtime-policy, release-evidence, doctor tools --server, and init-ci --tools-diff-server.");
1314
1349
  return 2;
1315
1350
  }
1316
1351
  if (command === "--version" || command === "-v" || command === "version") {
@@ -2771,6 +2806,23 @@ export async function runCli(args, io = defaultIo, options = {}) {
2771
2806
  }
2772
2807
  if (command === "init-ci") {
2773
2808
  const targetPath = maybePath && !maybePath.startsWith("--") ? maybePath : ".";
2809
+ const initFlags = maybePath && maybePath.startsWith("--") ? [maybePath, ...remainingArgs] : remainingArgs;
2810
+ const toolsDiff = parseToolsDiffWorkflowOptions(initFlags);
2811
+ if ("error" in toolsDiff) {
2812
+ io.writeStderr(toolsDiff.error);
2813
+ return 2;
2814
+ }
2815
+ if (toolsDiff.options !== null) {
2816
+ const { server, baseline, sandbox } = toolsDiff.options;
2817
+ const toolsResult = await initToolsDiffWorkflow(targetPath, toolsDiff.options);
2818
+ io.writeStdout([
2819
+ "Initialized Codex Plugin Doctor tool diff workflow",
2820
+ `Root: ${toolsResult.rootPath}`,
2821
+ `Workflow: ${toolsResult.workflowPath}`,
2822
+ `Capture and commit a baseline at ${baseline}, for example with: codex-plugin-doctor doctor tools ${JSON.stringify(targetPath)} --server ${JSON.stringify(server)} --runtime${sandbox === "docker" ? " --sandbox docker" : ""} --save-response ${JSON.stringify(path.join(targetPath, baseline))}`
2823
+ ].join("\n"));
2824
+ return 0;
2825
+ }
2774
2826
  const result = await initCiWorkflow(targetPath);
2775
2827
  io.writeStdout([
2776
2828
  "Initialized Codex Plugin Doctor workflow",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plugin-doctor",
3
- "version": "1.73.0",
3
+ "version": "1.74.1",
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",