codex-plugin-doctor 1.77.0 → 1.78.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
@@ -186,6 +186,9 @@ server with `--server <name> --runtime`, without retrieving any prompt.
186
186
  `doctor prompts-file` inspects a saved list offline, and `doctor prompts-diff`
187
187
  labels a removed prompt, a removed argument, or a newly required argument as
188
188
  breaking. Like the tool diff, reports omit names unless `--show-names` is given.
189
+ The GitHub Action runs the same capture and comparison through `prompts-*`
190
+ inputs and comments on pull requests, and
191
+ `init-ci --tools-diff-server <name> --prompts-baseline <path>` writes that workflow.
189
192
  See [MCP Prompt Catalog and Diff](docs/guides/mcp-prompts.md).
190
193
 
191
194
  ### Remote MCP Readiness
@@ -606,9 +609,9 @@ jobs:
606
609
  runs-on: ubuntu-latest
607
610
  steps:
608
611
  - uses: actions/checkout@v5
609
- - uses: Esquetta/CodexPluginDoctor@v1.77.0
612
+ - uses: Esquetta/CodexPluginDoctor@v1.78.0
610
613
  with:
611
- version: "1.77.0"
614
+ version: "1.78.0"
612
615
  path: .
613
616
  runtime: "true"
614
617
  policy: codex-publish
@@ -6,6 +6,7 @@ export interface ToolsDiffWorkflowOptions {
6
6
  server: string;
7
7
  baseline: string;
8
8
  sandbox: "docker" | null;
9
+ promptsBaseline: string | null;
9
10
  }
10
11
  export declare function initToolsDiffWorkflow(targetPath: string, options: ToolsDiffWorkflowOptions): Promise<InitCiResult>;
11
12
  export declare function initCiWorkflow(targetPath: string): Promise<InitCiResult>;
@@ -66,6 +66,11 @@ function buildToolsDiffWorkflow(options) {
66
66
  ...(options.sandbox === "docker" ? [" tools-capture-sandbox: docker"] : []),
67
67
  " # Catalog findings do not fail this job; the compare job re-checks the saved list.",
68
68
  " tools-capture-defer-findings: \"true\"",
69
+ ...(options.promptsBaseline === null ? [] : [
70
+ ` prompts-capture-server: ${yamlString(options.server)}`,
71
+ ...(options.sandbox === "docker" ? [" prompts-capture-sandbox: docker"] : []),
72
+ " prompts-capture-defer-findings: \"true\""
73
+ ]),
69
74
  " artifact-name: mcp-tools-capture",
70
75
  "",
71
76
  " compare:",
@@ -91,6 +96,12 @@ function buildToolsDiffWorkflow(options) {
91
96
  " tools-diff-after: ${{ runner.temp }}/mcp-tools-capture/mcp-tools-current.json",
92
97
  " tools-diff-fail-on: breaking",
93
98
  " tools-diff-comment: \"true\"",
99
+ ...(options.promptsBaseline === null ? [] : [
100
+ ` prompts-diff-before: ${yamlString(options.promptsBaseline)}`,
101
+ " prompts-diff-after: ${{ runner.temp }}/mcp-tools-capture/mcp-prompts-current.json",
102
+ " prompts-diff-fail-on: breaking",
103
+ " prompts-diff-comment: \"true\""
104
+ ]),
94
105
  " output-dir: ${{ runner.temp }}/codex-plugin-doctor-reports",
95
106
  " artifact-name: mcp-tools-diff",
96
107
  ""
@@ -101,8 +112,11 @@ export async function initToolsDiffWorkflow(targetPath, options) {
101
112
  const workflowPath = path.join(rootPath, ".github", "workflows", "codex-plugin-doctor-tools.yml");
102
113
  await mkdir(path.dirname(workflowPath), { recursive: true });
103
114
  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 });
115
+ // doctor tools --save-response writes into an existing directory, so the baselines can be captured next.
116
+ for (const baseline of [options.baseline, options.promptsBaseline]) {
117
+ if (baseline !== null)
118
+ await mkdir(path.dirname(path.resolve(rootPath, baseline)), { recursive: true });
119
+ }
106
120
  return { rootPath, workflowPath };
107
121
  }
108
122
  export async function initCiWorkflow(targetPath) {
package/dist/run-cli.js CHANGED
@@ -129,11 +129,11 @@ const PROMPT_CATALOG_COMMAND = { name: "prompts", noun: "Prompt", method: "promp
129
129
  const discoveryUsage = "Usage: codex-plugin-doctor doctor discover <url> --allow-network [--allow-local-network] [--json] [--bearer-token-env <NAME>]";
130
130
  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.";
131
131
  const ENVIRONMENT_VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
132
- /** Reads init-ci --tools-diff-server, --baseline, and --sandbox; options is null when none is given. */
132
+ /** Reads init-ci --tools-diff-server, --baseline, --prompts-baseline, and --sandbox; options is null when none is given. */
133
133
  function parseToolsDiffWorkflowOptions(flags) {
134
134
  const values = new Map();
135
135
  for (let index = 0; index < flags.length; index += 1) {
136
- if (!["--tools-diff-server", "--baseline", "--sandbox"].includes(flags[index]))
136
+ if (!["--tools-diff-server", "--baseline", "--prompts-baseline", "--sandbox"].includes(flags[index]))
137
137
  continue;
138
138
  const value = flags[index + 1];
139
139
  const present = value !== undefined && !value.startsWith("--");
@@ -144,7 +144,7 @@ function parseToolsDiffWorkflowOptions(flags) {
144
144
  if (values.size === 0)
145
145
  return { options: null };
146
146
  if (!values.has("--tools-diff-server"))
147
- return { error: "--baseline and --sandbox require --tools-diff-server." };
147
+ return { error: "--baseline, --prompts-baseline, and --sandbox require --tools-diff-server." };
148
148
  // Control characters would break the generated workflow lines, GitHub evaluates "${{" in any
149
149
  // workflow value, and the Action rejects values that start with "-".
150
150
  const usable = (value) => value !== undefined && value.trim() !== "" && !value.startsWith("-") && !value.includes("${{")
@@ -152,16 +152,23 @@ function parseToolsDiffWorkflowOptions(flags) {
152
152
  const server = values.get("--tools-diff-server");
153
153
  if (!usable(server))
154
154
  return { error: "--tools-diff-server requires a server name." };
155
- // The workflow runs on Linux from the repository root, so the baseline is a relative POSIX path.
155
+ // The workflow runs on Linux from the repository root, so baselines are relative POSIX paths.
156
+ const relative = (value) => usable(value) && !path.posix.isAbsolute(value)
157
+ && !/^[A-Za-z]:/u.test(value) && !path.posix.normalize(value).split("/").includes("..");
156
158
  const baseline = (values.has("--baseline") ? values.get("--baseline") : "mcp/tools-baseline.json")?.replaceAll("\\", "/");
157
- if (!usable(baseline) || path.posix.isAbsolute(baseline) || /^[A-Za-z]:/u.test(baseline)
158
- || path.posix.normalize(baseline).split("/").includes("..")) {
159
+ if (!relative(baseline))
159
160
  return { error: "--baseline requires a relative path inside the repository." };
161
+ const promptsBaseline = values.has("--prompts-baseline") ? values.get("--prompts-baseline")?.replaceAll("\\", "/") : null;
162
+ if (promptsBaseline !== null && !relative(promptsBaseline)) {
163
+ return { error: "--prompts-baseline requires a relative path inside the repository." };
164
+ }
165
+ if (promptsBaseline !== null && path.posix.normalize(promptsBaseline) === path.posix.normalize(baseline)) {
166
+ return { error: "--prompts-baseline must differ from --baseline." };
160
167
  }
161
168
  const sandbox = values.get("--sandbox");
162
169
  if (values.has("--sandbox") && sandbox !== "docker")
163
170
  return { error: "--sandbox must be docker." };
164
- return { options: { server, baseline, sandbox: sandbox === "docker" ? "docker" : null } };
171
+ return { options: { server, baseline, sandbox: sandbox === "docker" ? "docker" : null, promptsBaseline } };
165
172
  }
166
173
  const MAX_FORWARDED_ENVIRONMENT_VARIABLES = 32;
167
174
  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.";
@@ -509,7 +516,7 @@ function parseRemoteNetworkFlags(flags, runtime) {
509
516
  return { allowNetwork, allowLocalNetwork, allowSessionLifecycle, requireRemoteReliability };
510
517
  }
511
518
  function printUsage(io) {
512
- 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");
519
+ 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>] [--prompts-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");
513
520
  io.writeStderr(discoveryUsage);
514
521
  io.writeStderr(toolsUsage);
515
522
  io.writeStderr(promptsUsage);
@@ -2882,13 +2889,16 @@ export async function runCli(args, io = defaultIo, options = {}) {
2882
2889
  return 2;
2883
2890
  }
2884
2891
  if (toolsDiff.options !== null) {
2885
- const { server, baseline, sandbox } = toolsDiff.options;
2892
+ const { server, baseline, sandbox, promptsBaseline } = toolsDiff.options;
2886
2893
  const toolsResult = await initToolsDiffWorkflow(targetPath, toolsDiff.options);
2887
2894
  io.writeStdout([
2888
2895
  "Initialized Codex Plugin Doctor tool diff workflow",
2889
2896
  `Root: ${toolsResult.rootPath}`,
2890
2897
  `Workflow: ${toolsResult.workflowPath}`,
2891
- `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))}`
2898
+ `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))}`,
2899
+ ...(promptsBaseline === null ? [] : [
2900
+ `Capture and commit a prompt baseline at ${promptsBaseline}, for example with: codex-plugin-doctor doctor prompts ${JSON.stringify(targetPath)} --server ${JSON.stringify(server)} --runtime${sandbox === "docker" ? " --sandbox docker" : ""} --save-response ${JSON.stringify(path.join(targetPath, promptsBaseline))}`
2901
+ ])
2892
2902
  ].join("\n"));
2893
2903
  return 0;
2894
2904
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plugin-doctor",
3
- "version": "1.77.0",
3
+ "version": "1.78.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",