codex-plugin-doctor 1.78.0 → 1.79.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.
Files changed (30) hide show
  1. package/README.md +15 -2
  2. package/dist/core/mcp-prompt-catalog.js +2 -0
  3. package/dist/core/mcp-resource-template-catalog.d.ts +36 -0
  4. package/dist/core/mcp-resource-template-catalog.js +55 -0
  5. package/dist/core/mcp-resource-template-definition.d.ts +28 -0
  6. package/dist/core/mcp-resource-template-definition.js +116 -0
  7. package/dist/core/mcp-resource-template-diff.d.ts +43 -0
  8. package/dist/core/mcp-resource-template-diff.js +136 -0
  9. package/dist/core/mcp-resource-template-file.d.ts +31 -0
  10. package/dist/core/mcp-resource-template-file.js +118 -0
  11. package/dist/core/mcp-resource-template-impact.d.ts +21 -0
  12. package/dist/core/mcp-resource-template-impact.js +74 -0
  13. package/dist/core/mcp-stdio-tool-catalog.d.ts +1 -1
  14. package/dist/core/mcp-stdio-tool-catalog.js +6 -6
  15. package/dist/core/mcp-tool-catalog.d.ts +8 -6
  16. package/dist/core/mcp-tool-catalog.js +7 -5
  17. package/dist/core/mcp-tool-response-file.d.ts +1 -1
  18. package/dist/core/mcp-tool-response-file.js +3 -2
  19. package/dist/core/output-contract.js +172 -1
  20. package/dist/reporting/render-mcp-resource-template-catalog-report.d.ts +2 -0
  21. package/dist/reporting/render-mcp-resource-template-catalog-report.js +33 -0
  22. package/dist/reporting/render-mcp-resource-template-diff-markdown.d.ts +2 -0
  23. package/dist/reporting/render-mcp-resource-template-diff-markdown.js +39 -0
  24. package/dist/reporting/render-mcp-resource-template-diff-report.d.ts +2 -0
  25. package/dist/reporting/render-mcp-resource-template-diff-report.js +48 -0
  26. package/dist/reporting/render-mcp-resource-template-file-report.d.ts +5 -0
  27. package/dist/reporting/render-mcp-resource-template-file-report.js +31 -0
  28. package/dist/run-cli.d.ts +7 -0
  29. package/dist/run-cli.js +60 -6
  30. package/package.json +1 -1
package/dist/run-cli.d.ts CHANGED
@@ -2,8 +2,11 @@ import { discoverMcpServer } from "./core/mcp-discovery.js";
2
2
  import { captureMcpToolCatalog, inspectMcpToolCatalog } from "./core/mcp-tool-catalog.js";
3
3
  import { captureStdioMcpToolCatalog } from "./core/mcp-stdio-tool-catalog.js";
4
4
  import { captureMcpPromptCatalog, captureStdioMcpPromptCatalog } from "./core/mcp-prompt-catalog.js";
5
+ import { captureMcpResourceTemplateCatalog, captureStdioMcpResourceTemplateCatalog } from "./core/mcp-resource-template-catalog.js";
5
6
  import { compareMcpPromptFiles } from "./core/mcp-prompt-diff.js";
6
7
  import { inspectMcpPromptFile } from "./core/mcp-prompt-file.js";
8
+ import { compareMcpResourceTemplateFiles } from "./core/mcp-resource-template-diff.js";
9
+ import { inspectMcpResourceTemplateFile } from "./core/mcp-resource-template-file.js";
7
10
  import { inspectMcpToolFile } from "./core/mcp-tool-file.js";
8
11
  import { compareMcpToolFiles } from "./core/mcp-tool-diff.js";
9
12
  import { buildCorpusQualityMetricsDiffReport, buildCorpusQualityMetricsReport } from "./core/corpus-quality-metrics.js";
@@ -27,9 +30,13 @@ export interface RunCliOptions {
27
30
  captureMcpToolCatalogImpl?: typeof captureMcpToolCatalog;
28
31
  captureStdioMcpToolCatalogImpl?: typeof captureStdioMcpToolCatalog;
29
32
  compareMcpPromptFilesImpl?: typeof compareMcpPromptFiles;
33
+ compareMcpResourceTemplateFilesImpl?: typeof compareMcpResourceTemplateFiles;
34
+ inspectMcpResourceTemplateFileImpl?: typeof inspectMcpResourceTemplateFile;
30
35
  inspectMcpPromptFileImpl?: typeof inspectMcpPromptFile;
31
36
  captureMcpPromptCatalogImpl?: typeof captureMcpPromptCatalog;
32
37
  captureStdioMcpPromptCatalogImpl?: typeof captureStdioMcpPromptCatalog;
38
+ captureMcpResourceTemplateCatalogImpl?: typeof captureMcpResourceTemplateCatalog;
39
+ captureStdioMcpResourceTemplateCatalogImpl?: typeof captureStdioMcpResourceTemplateCatalog;
33
40
  discoverMcpServerImpl?: typeof discoverMcpServer;
34
41
  terminalContext?: CliTerminalContext;
35
42
  runCheckImpl?: typeof runCheck;
package/dist/run-cli.js CHANGED
@@ -26,8 +26,11 @@ import { discoverMcpServer } from "./core/mcp-discovery.js";
26
26
  import { captureMcpToolCatalog, inspectMcpToolCatalog } from "./core/mcp-tool-catalog.js";
27
27
  import { captureStdioMcpToolCatalog, resolveMcpStdioServer } from "./core/mcp-stdio-tool-catalog.js";
28
28
  import { captureMcpPromptCatalog, captureStdioMcpPromptCatalog } from "./core/mcp-prompt-catalog.js";
29
+ import { captureMcpResourceTemplateCatalog, captureStdioMcpResourceTemplateCatalog } from "./core/mcp-resource-template-catalog.js";
29
30
  import { compareMcpPromptFiles } from "./core/mcp-prompt-diff.js";
30
31
  import { inspectMcpPromptFile } from "./core/mcp-prompt-file.js";
32
+ import { compareMcpResourceTemplateFiles } from "./core/mcp-resource-template-diff.js";
33
+ import { inspectMcpResourceTemplateFile } from "./core/mcp-resource-template-file.js";
31
34
  import { inspectMcpToolFile, isBlockedOfflinePath } from "./core/mcp-tool-file.js";
32
35
  import { saveMcpListResponse } from "./core/mcp-tool-response-file.js";
33
36
  import { compareMcpToolFiles } from "./core/mcp-tool-diff.js";
@@ -36,9 +39,13 @@ import { renderMcpToolDiffReport } from "./reporting/render-mcp-tool-diff-report
36
39
  import { renderMcpToolFileReport } from "./reporting/render-mcp-tool-file-report.js";
37
40
  import { renderMcpToolCatalogReport } from "./reporting/render-mcp-tool-catalog-report.js";
38
41
  import { renderMcpPromptCatalogReport } from "./reporting/render-mcp-prompt-catalog-report.js";
42
+ import { renderMcpResourceTemplateCatalogReport } from "./reporting/render-mcp-resource-template-catalog-report.js";
39
43
  import { renderMcpPromptDiffMarkdown } from "./reporting/render-mcp-prompt-diff-markdown.js";
40
44
  import { renderMcpPromptDiffReport } from "./reporting/render-mcp-prompt-diff-report.js";
41
45
  import { renderMcpPromptFileReport } from "./reporting/render-mcp-prompt-file-report.js";
46
+ import { renderMcpResourceTemplateDiffMarkdown } from "./reporting/render-mcp-resource-template-diff-markdown.js";
47
+ import { renderMcpResourceTemplateDiffReport } from "./reporting/render-mcp-resource-template-diff-report.js";
48
+ import { renderMcpResourceTemplateFileReport } from "./reporting/render-mcp-resource-template-file-report.js";
42
49
  import { renderMcpDiscoveryReport } from "./reporting/render-mcp-discovery-report.js";
43
50
  import { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
44
51
  import { buildExternalValidationCorpusReport, ExternalCorpusManifestError } from "./core/external-validation-corpus.js";
@@ -122,10 +129,17 @@ const toolsDiffUsage = "Usage: codex-plugin-doctor doctor tools-diff --before <p
122
129
  const toolsFileUsage = "Usage: codex-plugin-doctor doctor tools-file <path> [--json]";
123
130
  const promptsDiffUsage = "Usage: codex-plugin-doctor doctor prompts-diff --before <path> --after <path> [--json|--markdown] [--fail-on any|breaking] [--show-names]";
124
131
  const promptsFileUsage = "Usage: codex-plugin-doctor doctor prompts-file <path> [--json]";
132
+ const resourceTemplatesDiffUsage = "Usage: codex-plugin-doctor doctor resource-templates-diff --before <path> --after <path> [--json|--markdown] [--fail-on any|breaking] [--show-names]";
133
+ const resourceTemplatesFileUsage = "Usage: codex-plugin-doctor doctor resource-templates-file <path> [--json]";
125
134
  const toolsUsage = "Usage: codex-plugin-doctor doctor tools <url> --allow-network [--allow-local-network] [--json] [--save-response <path>] [--bearer-token-env <NAME>]\n codex-plugin-doctor doctor tools <path> --server <name> --runtime [--sandbox docker [--env <NAME>]...] [--require-runtime-approval [--runtime-approval-digest <digest>]] [--json] [--save-response <path>]";
135
+ const resourceTemplatesUsage = "Usage: codex-plugin-doctor doctor resource-templates <url> --allow-network [--allow-local-network] [--json] [--save-response <path>] [--bearer-token-env <NAME>]\n codex-plugin-doctor doctor resource-templates <path> --server <name> --runtime [--sandbox docker [--env <NAME>]...] [--require-runtime-approval [--runtime-approval-digest <digest>]] [--json] [--save-response <path>]";
126
136
  const promptsUsage = "Usage: codex-plugin-doctor doctor prompts <url> --allow-network [--allow-local-network] [--json] [--save-response <path>] [--bearer-token-env <NAME>]\n codex-plugin-doctor doctor prompts <path> --server <name> --runtime [--sandbox docker [--env <NAME>]...] [--require-runtime-approval [--runtime-approval-digest <digest>]] [--json] [--save-response <path>]";
127
- const TOOL_CATALOG_COMMAND = { name: "tools", noun: "Tool", method: "tools/list", usage: toolsUsage };
128
- const PROMPT_CATALOG_COMMAND = { name: "prompts", noun: "Prompt", method: "prompts/list", usage: promptsUsage };
137
+ const TOOL_CATALOG_COMMAND = { name: "tools", noun: "Tool", plural: "tools", method: "tools/list", resultKey: "tools", usage: toolsUsage };
138
+ const PROMPT_CATALOG_COMMAND = { name: "prompts", noun: "Prompt", plural: "prompts", method: "prompts/list", resultKey: "prompts", usage: promptsUsage };
139
+ const RESOURCE_TEMPLATE_CATALOG_COMMAND = {
140
+ name: "resource-templates", noun: "Resource template", plural: "resource templates",
141
+ method: "resources/templates/list", resultKey: "resourceTemplates", usage: resourceTemplatesUsage
142
+ };
129
143
  const discoveryUsage = "Usage: codex-plugin-doctor doctor discover <url> --allow-network [--allow-local-network] [--json] [--bearer-token-env <NAME>]";
130
144
  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
145
  const ENVIRONMENT_VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
@@ -195,7 +209,7 @@ async function finishCatalogCommand(io, kind, status, output, items, cache, save
195
209
  io.writeStderr(`${kind.noun} catalog response was not saved because enumeration did not complete.`);
196
210
  return 2;
197
211
  }
198
- const saved = await saveMcpListResponse(savePath, kind.name, items, cache);
212
+ const saved = await saveMcpListResponse(savePath, kind.resultKey, items, cache);
199
213
  if (saved.kind !== "saved") {
200
214
  io.writeStderr({
201
215
  "path-blocked": `${kind.noun} catalog response was not saved because the path is not a local file path.`,
@@ -205,7 +219,7 @@ async function finishCatalogCommand(io, kind, status, output, items, cache, save
205
219
  }[saved.kind]);
206
220
  return 2;
207
221
  }
208
- io.writeStderr(`Saved a complete ${kind.method} response with ${saved.count} ${kind.name} (${saved.bytes} bytes).`);
222
+ io.writeStderr(`Saved a complete ${kind.method} response with ${saved.count} ${kind.plural} (${saved.bytes} bytes).`);
209
223
  return exitCode;
210
224
  }
211
225
  function finishToolCatalogCommand(io, snapshot, json, savePath) {
@@ -216,6 +230,10 @@ function finishPromptCatalogCommand(io, snapshot, json, savePath) {
216
230
  const report = snapshot.report;
217
231
  return finishCatalogCommand(io, PROMPT_CATALOG_COMMAND, report.status, json ? JSON.stringify(report, null, 2) : renderMcpPromptCatalogReport(report), snapshot.prompts, snapshot.cache, savePath);
218
232
  }
233
+ function finishResourceTemplateCatalogCommand(io, snapshot, json, savePath) {
234
+ const report = snapshot.report;
235
+ return finishCatalogCommand(io, RESOURCE_TEMPLATE_CATALOG_COMMAND, report.status, json ? JSON.stringify(report, null, 2) : renderMcpResourceTemplateCatalogReport(report), snapshot.templates, snapshot.cache, savePath);
236
+ }
219
237
  async function runHttpCatalogCommand(url, flags, io, env, kind, run) {
220
238
  const allowedFlags = new Set(["--allow-network", "--allow-local-network", "--json"]);
221
239
  const switches = [];
@@ -520,10 +538,13 @@ function printUsage(io) {
520
538
  io.writeStderr(discoveryUsage);
521
539
  io.writeStderr(toolsUsage);
522
540
  io.writeStderr(promptsUsage);
541
+ io.writeStderr(resourceTemplatesUsage);
523
542
  io.writeStderr(toolsFileUsage);
524
543
  io.writeStderr(toolsDiffUsage);
525
544
  io.writeStderr(promptsFileUsage);
526
545
  io.writeStderr(promptsDiffUsage);
546
+ io.writeStderr(resourceTemplatesFileUsage);
547
+ io.writeStderr(resourceTemplatesDiffUsage);
527
548
  io.writeStderr("Registry readiness: codex-plugin-doctor registry check <server.json|directory> [--json] [--output <path>] [--require-registry-readiness]\n"
528
549
  + " codex-plugin-doctor registry inspect <server-name> --allow-network [--json] [--output <path>] [--require-registry-readiness]\n"
529
550
  + "Registry publication preflight: codex-plugin-doctor registry preflight <server.json|directory> [--allow-network] [--json] [--output <path>] [--require-publish-ready]");
@@ -1487,10 +1508,10 @@ export async function runCli(args, io = defaultIo, options = {}) {
1487
1508
  (command === "doctor" &&
1488
1509
  (["runtime-plan", "runtime-policy"].includes(maybePath ?? "") ||
1489
1510
  (maybePath === "release-evidence" && remainingArgs[0] !== "verify") ||
1490
- ((maybePath === "tools" || maybePath === "prompts") && remainingArgs.includes("--server")))) ||
1511
+ (["tools", "prompts", "resource-templates"].includes(maybePath ?? "") && remainingArgs.includes("--server")))) ||
1491
1512
  (command === "init-ci" && args.includes("--tools-diff-server"));
1492
1513
  if (args.includes("--sandbox") && !runtimeSandboxSupported) {
1493
- io.writeStderr("--sandbox is supported only by single-package check, release check, runtime-plan, runtime-policy, release-evidence, doctor tools --server, doctor prompts --server, and init-ci --tools-diff-server.");
1514
+ io.writeStderr("--sandbox is supported only by single-package check, release check, runtime-plan, runtime-policy, release-evidence, doctor tools --server, doctor prompts --server, doctor resource-templates --server, and init-ci --tools-diff-server.");
1494
1515
  return 2;
1495
1516
  }
1496
1517
  if (command === "--version" || command === "-v" || command === "version") {
@@ -1572,6 +1593,24 @@ export async function runCli(args, io = defaultIo, options = {}) {
1572
1593
  }
1573
1594
  return runOfflineFileCommand(remainingArgs, io, promptsFileUsage, options.inspectMcpPromptFileImpl ?? inspectMcpPromptFile, renderMcpPromptFileReport);
1574
1595
  }
1596
+ if (maybePath === "resource-templates-diff") {
1597
+ if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
1598
+ io.writeStdout(`${resourceTemplatesDiffUsage}\nCompares two saved MCP resources/templates/list responses offline. Changes are classified heuristically as breaking, compatible, or unclassified; no resource is read and compatibility is not tested.\n--fail-on breaking exits 1 only for breaking or unclassified changes (default: any change or input warning).
1599
+ --show-names adds template names and the variable behind each reason; reports omit both, and never include URI templates, so keep the flag out of public output such as pull request comments.`);
1600
+ return 0;
1601
+ }
1602
+ return runOfflineDiffCommand(remainingArgs, io, resourceTemplatesDiffUsage, (beforePath, afterPath, showNames) => {
1603
+ const compare = options.compareMcpResourceTemplateFilesImpl ?? compareMcpResourceTemplateFiles;
1604
+ return showNames ? compare(beforePath, afterPath, { showNames: true }) : compare(beforePath, afterPath);
1605
+ }, (report, markdown) => markdown ? renderMcpResourceTemplateDiffMarkdown(report) : renderMcpResourceTemplateDiffReport(report));
1606
+ }
1607
+ if (maybePath === "resource-templates-file") {
1608
+ if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
1609
+ io.writeStdout(`${resourceTemplatesFileUsage}\nInspects one saved UTF-8 MCP resources/templates/list response. No server connection or resource read.`);
1610
+ return 0;
1611
+ }
1612
+ return runOfflineFileCommand(remainingArgs, io, resourceTemplatesFileUsage, options.inspectMcpResourceTemplateFileImpl ?? inspectMcpResourceTemplateFile, renderMcpResourceTemplateFileReport);
1613
+ }
1575
1614
  if (maybePath === "tools") {
1576
1615
  if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
1577
1616
  io.writeStdout(`${toolsUsage}\nTool catalog structure only; tools are not executed.\n--server starts the named stdio server from the package MCP configuration after explicit --runtime consent and stops it after reading the tool list.\n${forwardedEnvironmentHelp}\n--save-response writes a complete enumeration as one tools/list response for doctor tools-file and tools-diff; nothing is written when enumeration is incomplete.\n${bearerTokenHelp}`);
@@ -1607,6 +1646,21 @@ export async function runCli(args, io = defaultIo, options = {}) {
1607
1646
  }
1608
1647
  return runHttpCatalogCommand(target, flags, io, terminalContext.env, PROMPT_CATALOG_COMMAND, async (url, catalogOptions, json, savePath) => finishPromptCatalogCommand(io, await (options.captureMcpPromptCatalogImpl ?? captureMcpPromptCatalog)(url, catalogOptions), json, savePath));
1609
1648
  }
1649
+ if (maybePath === "resource-templates") {
1650
+ if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
1651
+ io.writeStdout(`${resourceTemplatesUsage}\nResource template catalog structure only; no resource is read.\n--server starts the named stdio server from the package MCP configuration after explicit --runtime consent and stops it after reading the resource template list.\n${forwardedEnvironmentHelp}\n--save-response writes a complete enumeration as one resources/templates/list response for doctor resource-templates-file and resource-templates-diff; nothing is written when enumeration is incomplete.\n${bearerTokenHelp}`);
1652
+ return 0;
1653
+ }
1654
+ const [target, ...flags] = remainingArgs;
1655
+ if (!target || target.startsWith("-")) {
1656
+ io.writeStderr(resourceTemplatesUsage);
1657
+ return 2;
1658
+ }
1659
+ if (flags.includes("--server")) {
1660
+ return runStdioCatalogCommand(target, flags, io, terminalContext.env, RESOURCE_TEMPLATE_CATALOG_COMMAND, async (server, captureOptions, json, savePath) => finishResourceTemplateCatalogCommand(io, await (options.captureStdioMcpResourceTemplateCatalogImpl ?? captureStdioMcpResourceTemplateCatalog)(server, captureOptions), json, savePath));
1661
+ }
1662
+ return runHttpCatalogCommand(target, flags, io, terminalContext.env, RESOURCE_TEMPLATE_CATALOG_COMMAND, async (url, catalogOptions, json, savePath) => finishResourceTemplateCatalogCommand(io, await (options.captureMcpResourceTemplateCatalogImpl ?? captureMcpResourceTemplateCatalog)(url, catalogOptions), json, savePath));
1663
+ }
1610
1664
  if (maybePath === "discover") {
1611
1665
  if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
1612
1666
  io.writeStdout(`${discoveryUsage}\nHTTP discovery only; runtime behavior is not tested.\n${bearerTokenHelp}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plugin-doctor",
3
- "version": "1.78.0",
3
+ "version": "1.79.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",