codex-plugin-doctor 1.76.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 +21 -2
- package/dist/core/init-ci.d.ts +1 -0
- package/dist/core/init-ci.js +16 -2
- package/dist/core/mcp-prompt-catalog.d.ts +36 -0
- package/dist/core/mcp-prompt-catalog.js +53 -0
- package/dist/core/mcp-prompt-definition.d.ts +24 -0
- package/dist/core/mcp-prompt-definition.js +107 -0
- package/dist/core/mcp-prompt-diff.d.ts +43 -0
- package/dist/core/mcp-prompt-diff.js +132 -0
- package/dist/core/mcp-prompt-file.d.ts +31 -0
- package/dist/core/mcp-prompt-file.js +118 -0
- package/dist/core/mcp-prompt-impact.d.ts +21 -0
- package/dist/core/mcp-prompt-impact.js +93 -0
- package/dist/core/mcp-stdio-tool-catalog.d.ts +9 -3
- package/dist/core/mcp-stdio-tool-catalog.js +74 -55
- package/dist/core/mcp-tool-catalog.d.ts +44 -5
- package/dist/core/mcp-tool-catalog.js +76 -31
- package/dist/core/mcp-tool-diff.d.ts +1 -0
- package/dist/core/mcp-tool-diff.js +1 -1
- package/dist/core/mcp-tool-file.d.ts +20 -0
- package/dist/core/mcp-tool-file.js +1 -1
- package/dist/core/mcp-tool-response-file.d.ts +6 -4
- package/dist/core/mcp-tool-response-file.js +8 -8
- package/dist/core/output-contract.js +177 -0
- package/dist/reporting/quote-server-text.d.ts +1 -0
- package/dist/reporting/quote-server-text.js +13 -0
- package/dist/reporting/render-mcp-prompt-catalog-report.d.ts +2 -0
- package/dist/reporting/render-mcp-prompt-catalog-report.js +31 -0
- package/dist/reporting/render-mcp-prompt-diff-markdown.d.ts +2 -0
- package/dist/reporting/render-mcp-prompt-diff-markdown.js +39 -0
- package/dist/reporting/render-mcp-prompt-diff-report.d.ts +2 -0
- package/dist/reporting/render-mcp-prompt-diff-report.js +48 -0
- package/dist/reporting/render-mcp-prompt-file-report.d.ts +5 -0
- package/dist/reporting/render-mcp-prompt-file-report.js +33 -0
- package/dist/reporting/render-mcp-tool-diff-markdown.js +3 -16
- package/dist/run-cli.d.ts +7 -0
- package/dist/run-cli.js +223 -150
- package/package.json +1 -1
package/dist/run-cli.js
CHANGED
|
@@ -25,13 +25,20 @@ import { isValidBearerToken } from "./core/bounded-http-client.js";
|
|
|
25
25
|
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
|
+
import { captureMcpPromptCatalog, captureStdioMcpPromptCatalog } from "./core/mcp-prompt-catalog.js";
|
|
29
|
+
import { compareMcpPromptFiles } from "./core/mcp-prompt-diff.js";
|
|
30
|
+
import { inspectMcpPromptFile } from "./core/mcp-prompt-file.js";
|
|
28
31
|
import { inspectMcpToolFile, isBlockedOfflinePath } from "./core/mcp-tool-file.js";
|
|
29
|
-
import {
|
|
32
|
+
import { saveMcpListResponse } from "./core/mcp-tool-response-file.js";
|
|
30
33
|
import { compareMcpToolFiles } from "./core/mcp-tool-diff.js";
|
|
31
34
|
import { renderMcpToolDiffMarkdown } from "./reporting/render-mcp-tool-diff-markdown.js";
|
|
32
35
|
import { renderMcpToolDiffReport } from "./reporting/render-mcp-tool-diff-report.js";
|
|
33
36
|
import { renderMcpToolFileReport } from "./reporting/render-mcp-tool-file-report.js";
|
|
34
37
|
import { renderMcpToolCatalogReport } from "./reporting/render-mcp-tool-catalog-report.js";
|
|
38
|
+
import { renderMcpPromptCatalogReport } from "./reporting/render-mcp-prompt-catalog-report.js";
|
|
39
|
+
import { renderMcpPromptDiffMarkdown } from "./reporting/render-mcp-prompt-diff-markdown.js";
|
|
40
|
+
import { renderMcpPromptDiffReport } from "./reporting/render-mcp-prompt-diff-report.js";
|
|
41
|
+
import { renderMcpPromptFileReport } from "./reporting/render-mcp-prompt-file-report.js";
|
|
35
42
|
import { renderMcpDiscoveryReport } from "./reporting/render-mcp-discovery-report.js";
|
|
36
43
|
import { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
|
|
37
44
|
import { buildExternalValidationCorpusReport, ExternalCorpusManifestError } from "./core/external-validation-corpus.js";
|
|
@@ -113,15 +120,20 @@ class CliUsageError extends Error {
|
|
|
113
120
|
}
|
|
114
121
|
const toolsDiffUsage = "Usage: codex-plugin-doctor doctor tools-diff --before <path> --after <path> [--json|--markdown] [--fail-on any|breaking] [--show-names]";
|
|
115
122
|
const toolsFileUsage = "Usage: codex-plugin-doctor doctor tools-file <path> [--json]";
|
|
123
|
+
const promptsDiffUsage = "Usage: codex-plugin-doctor doctor prompts-diff --before <path> --after <path> [--json|--markdown] [--fail-on any|breaking] [--show-names]";
|
|
124
|
+
const promptsFileUsage = "Usage: codex-plugin-doctor doctor prompts-file <path> [--json]";
|
|
116
125
|
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>]";
|
|
126
|
+
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 };
|
|
117
129
|
const discoveryUsage = "Usage: codex-plugin-doctor doctor discover <url> --allow-network [--allow-local-network] [--json] [--bearer-token-env <NAME>]";
|
|
118
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.";
|
|
119
131
|
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. */
|
|
132
|
+
/** Reads init-ci --tools-diff-server, --baseline, --prompts-baseline, and --sandbox; options is null when none is given. */
|
|
121
133
|
function parseToolsDiffWorkflowOptions(flags) {
|
|
122
134
|
const values = new Map();
|
|
123
135
|
for (let index = 0; index < flags.length; index += 1) {
|
|
124
|
-
if (!["--tools-diff-server", "--baseline", "--sandbox"].includes(flags[index]))
|
|
136
|
+
if (!["--tools-diff-server", "--baseline", "--prompts-baseline", "--sandbox"].includes(flags[index]))
|
|
125
137
|
continue;
|
|
126
138
|
const value = flags[index + 1];
|
|
127
139
|
const present = value !== undefined && !value.startsWith("--");
|
|
@@ -132,7 +144,7 @@ function parseToolsDiffWorkflowOptions(flags) {
|
|
|
132
144
|
if (values.size === 0)
|
|
133
145
|
return { options: null };
|
|
134
146
|
if (!values.has("--tools-diff-server"))
|
|
135
|
-
return { error: "--baseline and --sandbox require --tools-diff-server." };
|
|
147
|
+
return { error: "--baseline, --prompts-baseline, and --sandbox require --tools-diff-server." };
|
|
136
148
|
// Control characters would break the generated workflow lines, GitHub evaluates "${{" in any
|
|
137
149
|
// workflow value, and the Action rejects values that start with "-".
|
|
138
150
|
const usable = (value) => value !== undefined && value.trim() !== "" && !value.startsWith("-") && !value.includes("${{")
|
|
@@ -140,16 +152,23 @@ function parseToolsDiffWorkflowOptions(flags) {
|
|
|
140
152
|
const server = values.get("--tools-diff-server");
|
|
141
153
|
if (!usable(server))
|
|
142
154
|
return { error: "--tools-diff-server requires a server name." };
|
|
143
|
-
// The workflow runs on Linux from the repository root, so
|
|
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("..");
|
|
144
158
|
const baseline = (values.has("--baseline") ? values.get("--baseline") : "mcp/tools-baseline.json")?.replaceAll("\\", "/");
|
|
145
|
-
if (!
|
|
146
|
-
|| path.posix.normalize(baseline).split("/").includes("..")) {
|
|
159
|
+
if (!relative(baseline))
|
|
147
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." };
|
|
148
167
|
}
|
|
149
168
|
const sandbox = values.get("--sandbox");
|
|
150
169
|
if (values.has("--sandbox") && sandbox !== "docker")
|
|
151
170
|
return { error: "--sandbox must be docker." };
|
|
152
|
-
return { options: { server, baseline, sandbox: sandbox === "docker" ? "docker" : null } };
|
|
171
|
+
return { options: { server, baseline, sandbox: sandbox === "docker" ? "docker" : null, promptsBaseline } };
|
|
153
172
|
}
|
|
154
173
|
const MAX_FORWARDED_ENVIRONMENT_VARIABLES = 32;
|
|
155
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.";
|
|
@@ -165,33 +184,90 @@ function resolveBearerToken(name, env) {
|
|
|
165
184
|
return { error: `Bearer token environment variable does not hold a valid bearer token: ${name}` };
|
|
166
185
|
return { token };
|
|
167
186
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
io.writeStdout(
|
|
171
|
-
|
|
172
|
-
:
|
|
173
|
-
const exitCode = report.status === "pass" ? 0
|
|
174
|
-
: report.status === "incomplete" || report.status === "blocked" ? 2 : 1;
|
|
187
|
+
// Writes the catalog report and, when asked, saves a complete enumeration as one list response.
|
|
188
|
+
async function finishCatalogCommand(io, kind, status, output, items, cache, savePath) {
|
|
189
|
+
io.writeStdout(output);
|
|
190
|
+
const exitCode = status === "pass" ? 0
|
|
191
|
+
: status === "incomplete" || status === "blocked" ? 2 : 1;
|
|
175
192
|
if (savePath === undefined)
|
|
176
193
|
return exitCode;
|
|
177
|
-
if (
|
|
178
|
-
io.writeStderr(
|
|
194
|
+
if (items === null || cache === null) {
|
|
195
|
+
io.writeStderr(`${kind.noun} catalog response was not saved because enumeration did not complete.`);
|
|
179
196
|
return 2;
|
|
180
197
|
}
|
|
181
|
-
const saved = await
|
|
198
|
+
const saved = await saveMcpListResponse(savePath, kind.name, items, cache);
|
|
182
199
|
if (saved.kind !== "saved") {
|
|
183
200
|
io.writeStderr({
|
|
184
|
-
"path-blocked":
|
|
185
|
-
"not-regular":
|
|
186
|
-
"too-large":
|
|
187
|
-
"write-failed":
|
|
201
|
+
"path-blocked": `${kind.noun} catalog response was not saved because the path is not a local file path.`,
|
|
202
|
+
"not-regular": `${kind.noun} catalog response was not saved because the existing target is not a regular file.`,
|
|
203
|
+
"too-large": `${kind.noun} catalog response was not saved because it exceeds the one MiB ${kind.name}-file limit.`,
|
|
204
|
+
"write-failed": `${kind.noun} catalog response could not be written.`
|
|
188
205
|
}[saved.kind]);
|
|
189
206
|
return 2;
|
|
190
207
|
}
|
|
191
|
-
io.writeStderr(`Saved a complete
|
|
208
|
+
io.writeStderr(`Saved a complete ${kind.method} response with ${saved.count} ${kind.name} (${saved.bytes} bytes).`);
|
|
192
209
|
return exitCode;
|
|
193
210
|
}
|
|
194
|
-
|
|
211
|
+
function finishToolCatalogCommand(io, snapshot, json, savePath) {
|
|
212
|
+
const report = snapshot.report;
|
|
213
|
+
return finishCatalogCommand(io, TOOL_CATALOG_COMMAND, report.status, json ? JSON.stringify(report, null, 2) : renderMcpToolCatalogReport(report), snapshot.tools, snapshot.cache, savePath);
|
|
214
|
+
}
|
|
215
|
+
function finishPromptCatalogCommand(io, snapshot, json, savePath) {
|
|
216
|
+
const report = snapshot.report;
|
|
217
|
+
return finishCatalogCommand(io, PROMPT_CATALOG_COMMAND, report.status, json ? JSON.stringify(report, null, 2) : renderMcpPromptCatalogReport(report), snapshot.prompts, snapshot.cache, savePath);
|
|
218
|
+
}
|
|
219
|
+
async function runHttpCatalogCommand(url, flags, io, env, kind, run) {
|
|
220
|
+
const allowedFlags = new Set(["--allow-network", "--allow-local-network", "--json"]);
|
|
221
|
+
const switches = [];
|
|
222
|
+
let savePath;
|
|
223
|
+
let tokenEnv;
|
|
224
|
+
let invalid = false;
|
|
225
|
+
for (let index = 0; index < flags.length; index += 1) {
|
|
226
|
+
const flag = flags[index];
|
|
227
|
+
if ((flag === "--save-response" && savePath === undefined) || (flag === "--bearer-token-env" && tokenEnv === undefined)) {
|
|
228
|
+
const value = flags[index + 1];
|
|
229
|
+
if (!value || value.startsWith("-")) {
|
|
230
|
+
invalid = true;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
if (flag === "--save-response")
|
|
234
|
+
savePath = value;
|
|
235
|
+
else
|
|
236
|
+
tokenEnv = value;
|
|
237
|
+
index += 1;
|
|
238
|
+
}
|
|
239
|
+
else if (allowedFlags.has(flag) && !switches.includes(flag)) {
|
|
240
|
+
switches.push(flag);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
invalid = true;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (invalid) {
|
|
248
|
+
io.writeStderr(`Invalid or duplicate ${kind.noun.toLowerCase()} catalog arguments. ${kind.usage}`);
|
|
249
|
+
return 2;
|
|
250
|
+
}
|
|
251
|
+
if (!switches.includes("--allow-network")) {
|
|
252
|
+
io.writeStderr(`doctor ${kind.name} requires explicit --allow-network consent.`);
|
|
253
|
+
return 2;
|
|
254
|
+
}
|
|
255
|
+
if (savePath !== undefined && isBlockedOfflinePath(savePath)) {
|
|
256
|
+
io.writeStderr(`doctor ${kind.name} --save-response requires a local file path.`);
|
|
257
|
+
return 2;
|
|
258
|
+
}
|
|
259
|
+
const bearer = resolveBearerToken(tokenEnv, env);
|
|
260
|
+
if ("error" in bearer) {
|
|
261
|
+
io.writeStderr(bearer.error);
|
|
262
|
+
return 2;
|
|
263
|
+
}
|
|
264
|
+
return run(url, {
|
|
265
|
+
allowNetwork: true,
|
|
266
|
+
allowLocalNetwork: switches.includes("--allow-local-network"),
|
|
267
|
+
...(bearer.token === undefined ? {} : { bearerToken: bearer.token })
|
|
268
|
+
}, switches.includes("--json"), savePath);
|
|
269
|
+
}
|
|
270
|
+
async function runStdioCatalogCommand(targetPath, flags, io, env, kind, run) {
|
|
195
271
|
const allowedSwitches = new Set(["--runtime", "--json", "--require-runtime-approval"]);
|
|
196
272
|
const switches = [];
|
|
197
273
|
const values = new Map();
|
|
@@ -231,11 +307,11 @@ async function runStdioToolCatalogCommand(targetPath, flags, io, options, env) {
|
|
|
231
307
|
const approvalDigest = values.get("--runtime-approval-digest");
|
|
232
308
|
const requireApproval = switches.includes("--require-runtime-approval");
|
|
233
309
|
if (invalid || serverName === undefined || (sandbox !== undefined && sandbox !== "docker")) {
|
|
234
|
-
io.writeStderr(`Invalid or duplicate
|
|
310
|
+
io.writeStderr(`Invalid or duplicate ${kind.noun.toLowerCase()} catalog arguments. ${kind.usage}`);
|
|
235
311
|
return 2;
|
|
236
312
|
}
|
|
237
313
|
if (!switches.includes("--runtime")) {
|
|
238
|
-
io.writeStderr(
|
|
314
|
+
io.writeStderr(`doctor ${kind.name} --server starts a local MCP server and requires explicit --runtime consent.`);
|
|
239
315
|
return 2;
|
|
240
316
|
}
|
|
241
317
|
if (approvalDigest !== undefined && !requireApproval) {
|
|
@@ -262,14 +338,14 @@ async function runStdioToolCatalogCommand(targetPath, flags, io, options, env) {
|
|
|
262
338
|
}
|
|
263
339
|
}
|
|
264
340
|
if (isBlockedOfflinePath(targetPath)) {
|
|
265
|
-
io.writeStderr(
|
|
341
|
+
io.writeStderr(`doctor ${kind.name} --server requires a local package path.`);
|
|
266
342
|
return 2;
|
|
267
343
|
}
|
|
268
344
|
if (savePath !== undefined && isBlockedOfflinePath(savePath)) {
|
|
269
|
-
io.writeStderr(
|
|
345
|
+
io.writeStderr(`doctor ${kind.name} --save-response requires a local file path.`);
|
|
270
346
|
return 2;
|
|
271
347
|
}
|
|
272
|
-
const resolved = await resolveMcpStdioServer(targetPath, serverName);
|
|
348
|
+
const resolved = await resolveMcpStdioServer(targetPath, serverName, kind.name);
|
|
273
349
|
if (!resolved.ok) {
|
|
274
350
|
io.writeStderr(resolved.message);
|
|
275
351
|
return 2;
|
|
@@ -290,8 +366,74 @@ async function runStdioToolCatalogCommand(targetPath, flags, io, options, env) {
|
|
|
290
366
|
return 1;
|
|
291
367
|
}
|
|
292
368
|
}
|
|
293
|
-
|
|
294
|
-
|
|
369
|
+
return run(resolved.server, sandbox !== "docker" ? {} : environment.length > 0 ? { sandbox: "docker", environment } : { sandbox: "docker" }, switches.includes("--json"), savePath);
|
|
370
|
+
}
|
|
371
|
+
// Runs tools-diff or prompts-diff, which share their options and exit codes.
|
|
372
|
+
async function runOfflineDiffCommand(args, io, usage, compare, render) {
|
|
373
|
+
let beforePath;
|
|
374
|
+
let afterPath;
|
|
375
|
+
let format;
|
|
376
|
+
let failOn;
|
|
377
|
+
let showNames = false;
|
|
378
|
+
let invalid = false;
|
|
379
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
380
|
+
const flag = args[index];
|
|
381
|
+
if (flag === "--show-names" && !showNames) {
|
|
382
|
+
showNames = true;
|
|
383
|
+
}
|
|
384
|
+
else if ((flag === "--json" || flag === "--markdown") && format === undefined) {
|
|
385
|
+
format = flag === "--json" ? "json" : "markdown";
|
|
386
|
+
}
|
|
387
|
+
else if (flag === "--fail-on" && failOn === undefined) {
|
|
388
|
+
const value = args[index + 1];
|
|
389
|
+
if (value !== "any" && value !== "breaking") {
|
|
390
|
+
invalid = true;
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
failOn = value;
|
|
394
|
+
index += 1;
|
|
395
|
+
}
|
|
396
|
+
else if ((flag === "--before" && beforePath === undefined) || (flag === "--after" && afterPath === undefined)) {
|
|
397
|
+
const value = args[index + 1];
|
|
398
|
+
if (!value || value.startsWith("-")) {
|
|
399
|
+
invalid = true;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
if (flag === "--before")
|
|
403
|
+
beforePath = value;
|
|
404
|
+
else
|
|
405
|
+
afterPath = value;
|
|
406
|
+
index += 1;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
invalid = true;
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (invalid || !beforePath || !afterPath) {
|
|
414
|
+
io.writeStderr(`Invalid comparison arguments. ${usage}`);
|
|
415
|
+
return 2;
|
|
416
|
+
}
|
|
417
|
+
const report = await compare(beforePath, afterPath, showNames);
|
|
418
|
+
io.writeStdout(format === "json" ? JSON.stringify(report, null, 2) : render(report, format === "markdown"));
|
|
419
|
+
if (report.status !== "pass" && report.status !== "warn")
|
|
420
|
+
return 2;
|
|
421
|
+
if (failOn === "breaking") {
|
|
422
|
+
return (report.comparison.breaking ?? 0) + (report.comparison.unclassified ?? 0) > 0 ? 1 : 0;
|
|
423
|
+
}
|
|
424
|
+
return report.status === "pass" ? 0 : 1;
|
|
425
|
+
}
|
|
426
|
+
// Runs tools-file or prompts-file, which share their options and exit codes.
|
|
427
|
+
async function runOfflineFileCommand(args, io, usage, inspect, render) {
|
|
428
|
+
const [filePath, ...flags] = args;
|
|
429
|
+
if (!filePath || filePath.startsWith("-") || flags.some((flag) => flag !== "--json") || flags.length > 1) {
|
|
430
|
+
io.writeStderr(`Invalid file inspection arguments. ${usage}`);
|
|
431
|
+
return 2;
|
|
432
|
+
}
|
|
433
|
+
const report = await inspect(filePath);
|
|
434
|
+
io.writeStdout(flags.includes("--json") ? JSON.stringify(report, null, 2) : render(report));
|
|
435
|
+
return report.status === "pass" ? 0
|
|
436
|
+
: report.status === "warn" || report.status === "fail" ? 1 : 2;
|
|
295
437
|
}
|
|
296
438
|
function parseRuntimeSandbox(flags, options = { requireRuntime: true }) {
|
|
297
439
|
const index = flags.indexOf("--sandbox");
|
|
@@ -374,11 +516,14 @@ function parseRemoteNetworkFlags(flags, runtime) {
|
|
|
374
516
|
return { allowNetwork, allowLocalNetwork, allowSessionLifecycle, requireRemoteReliability };
|
|
375
517
|
}
|
|
376
518
|
function printUsage(io) {
|
|
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");
|
|
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");
|
|
378
520
|
io.writeStderr(discoveryUsage);
|
|
379
521
|
io.writeStderr(toolsUsage);
|
|
522
|
+
io.writeStderr(promptsUsage);
|
|
380
523
|
io.writeStderr(toolsFileUsage);
|
|
381
524
|
io.writeStderr(toolsDiffUsage);
|
|
525
|
+
io.writeStderr(promptsFileUsage);
|
|
526
|
+
io.writeStderr(promptsDiffUsage);
|
|
382
527
|
io.writeStderr("Registry readiness: codex-plugin-doctor registry check <server.json|directory> [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
383
528
|
+ " codex-plugin-doctor registry inspect <server-name> --allow-network [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
384
529
|
+ "Registry publication preflight: codex-plugin-doctor registry preflight <server.json|directory> [--allow-network] [--json] [--output <path>] [--require-publish-ready]");
|
|
@@ -1342,10 +1487,10 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1342
1487
|
(command === "doctor" &&
|
|
1343
1488
|
(["runtime-plan", "runtime-policy"].includes(maybePath ?? "") ||
|
|
1344
1489
|
(maybePath === "release-evidence" && remainingArgs[0] !== "verify") ||
|
|
1345
|
-
(maybePath === "tools" && remainingArgs.includes("--server")))) ||
|
|
1490
|
+
((maybePath === "tools" || maybePath === "prompts") && remainingArgs.includes("--server")))) ||
|
|
1346
1491
|
(command === "init-ci" && args.includes("--tools-diff-server"));
|
|
1347
1492
|
if (args.includes("--sandbox") && !runtimeSandboxSupported) {
|
|
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.");
|
|
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.");
|
|
1349
1494
|
return 2;
|
|
1350
1495
|
}
|
|
1351
1496
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
@@ -1397,78 +1542,35 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1397
1542
|
--show-names adds tool names and the JSON Pointer of each reason; reports omit both by default, so keep it out of public output such as pull request comments.`);
|
|
1398
1543
|
return 0;
|
|
1399
1544
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1411
|
-
else if ((flag === "--json" || flag === "--markdown") && format === undefined) {
|
|
1412
|
-
format = flag === "--json" ? "json" : "markdown";
|
|
1413
|
-
}
|
|
1414
|
-
else if (flag === "--fail-on" && failOn === undefined) {
|
|
1415
|
-
const value = remainingArgs[index + 1];
|
|
1416
|
-
if (value !== "any" && value !== "breaking") {
|
|
1417
|
-
invalid = true;
|
|
1418
|
-
break;
|
|
1419
|
-
}
|
|
1420
|
-
failOn = value;
|
|
1421
|
-
index += 1;
|
|
1422
|
-
}
|
|
1423
|
-
else if ((flag === "--before" && beforePath === undefined) || (flag === "--after" && afterPath === undefined)) {
|
|
1424
|
-
const value = remainingArgs[index + 1];
|
|
1425
|
-
if (!value || value.startsWith("-")) {
|
|
1426
|
-
invalid = true;
|
|
1427
|
-
break;
|
|
1428
|
-
}
|
|
1429
|
-
if (flag === "--before")
|
|
1430
|
-
beforePath = value;
|
|
1431
|
-
else
|
|
1432
|
-
afterPath = value;
|
|
1433
|
-
index += 1;
|
|
1434
|
-
}
|
|
1435
|
-
else {
|
|
1436
|
-
invalid = true;
|
|
1437
|
-
break;
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
if (invalid || !beforePath || !afterPath) {
|
|
1441
|
-
io.writeStderr(`Invalid comparison arguments. ${toolsDiffUsage}`);
|
|
1442
|
-
return 2;
|
|
1443
|
-
}
|
|
1444
|
-
const compare = options.compareMcpToolFilesImpl ?? compareMcpToolFiles;
|
|
1445
|
-
const report = await (showNames ? compare(beforePath, afterPath, { showNames: true }) : compare(beforePath, afterPath));
|
|
1446
|
-
io.writeStdout(format === "json" ? JSON.stringify(report, null, 2)
|
|
1447
|
-
: format === "markdown" ? renderMcpToolDiffMarkdown(report)
|
|
1448
|
-
: renderMcpToolDiffReport(report));
|
|
1449
|
-
if (report.status !== "pass" && report.status !== "warn")
|
|
1450
|
-
return 2;
|
|
1451
|
-
if (failOn === "breaking") {
|
|
1452
|
-
return (report.comparison.breaking ?? 0) + (report.comparison.unclassified ?? 0) > 0 ? 1 : 0;
|
|
1545
|
+
return runOfflineDiffCommand(remainingArgs, io, toolsDiffUsage, (beforePath, afterPath, showNames) => {
|
|
1546
|
+
const compare = options.compareMcpToolFilesImpl ?? compareMcpToolFiles;
|
|
1547
|
+
return showNames ? compare(beforePath, afterPath, { showNames: true }) : compare(beforePath, afterPath);
|
|
1548
|
+
}, (report, markdown) => markdown ? renderMcpToolDiffMarkdown(report) : renderMcpToolDiffReport(report));
|
|
1549
|
+
}
|
|
1550
|
+
if (maybePath === "prompts-diff") {
|
|
1551
|
+
if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
|
|
1552
|
+
io.writeStdout(`${promptsDiffUsage}\nCompares two saved MCP prompts/list responses offline. Changes are classified heuristically as breaking, compatible, or unclassified; prompts are not retrieved and compatibility is not tested.\n--fail-on breaking exits 1 only for breaking or unclassified changes (default: any change or input warning).
|
|
1553
|
+
--show-names adds prompt names and the argument behind each reason; reports omit both by default, so keep it out of public output such as pull request comments.`);
|
|
1554
|
+
return 0;
|
|
1453
1555
|
}
|
|
1454
|
-
return
|
|
1556
|
+
return runOfflineDiffCommand(remainingArgs, io, promptsDiffUsage, (beforePath, afterPath, showNames) => {
|
|
1557
|
+
const compare = options.compareMcpPromptFilesImpl ?? compareMcpPromptFiles;
|
|
1558
|
+
return showNames ? compare(beforePath, afterPath, { showNames: true }) : compare(beforePath, afterPath);
|
|
1559
|
+
}, (report, markdown) => markdown ? renderMcpPromptDiffMarkdown(report) : renderMcpPromptDiffReport(report));
|
|
1455
1560
|
}
|
|
1456
1561
|
if (maybePath === "tools-file") {
|
|
1457
1562
|
if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
|
|
1458
1563
|
io.writeStdout(`${toolsFileUsage}\nInspects one saved UTF-8 MCP tools/list response. No server connection or tool execution.`);
|
|
1459
1564
|
return 0;
|
|
1460
1565
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1566
|
+
return runOfflineFileCommand(remainingArgs, io, toolsFileUsage, options.inspectMcpToolFileImpl ?? inspectMcpToolFile, renderMcpToolFileReport);
|
|
1567
|
+
}
|
|
1568
|
+
if (maybePath === "prompts-file") {
|
|
1569
|
+
if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
|
|
1570
|
+
io.writeStdout(`${promptsFileUsage}\nInspects one saved UTF-8 MCP prompts/list response. No server connection or prompt retrieval.`);
|
|
1571
|
+
return 0;
|
|
1465
1572
|
}
|
|
1466
|
-
|
|
1467
|
-
io.writeStdout(flags.includes("--json")
|
|
1468
|
-
? JSON.stringify(report, null, 2)
|
|
1469
|
-
: renderMcpToolFileReport(report));
|
|
1470
|
-
return report.status === "pass" ? 0
|
|
1471
|
-
: report.status === "warn" || report.status === "fail" ? 1 : 2;
|
|
1573
|
+
return runOfflineFileCommand(remainingArgs, io, promptsFileUsage, options.inspectMcpPromptFileImpl ?? inspectMcpPromptFile, renderMcpPromptFileReport);
|
|
1472
1574
|
}
|
|
1473
1575
|
if (maybePath === "tools") {
|
|
1474
1576
|
if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
|
|
@@ -1481,61 +1583,29 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1481
1583
|
return 2;
|
|
1482
1584
|
}
|
|
1483
1585
|
if (flags.includes("--server")) {
|
|
1484
|
-
return
|
|
1586
|
+
return runStdioCatalogCommand(url, flags, io, terminalContext.env, TOOL_CATALOG_COMMAND, async (server, captureOptions, json, savePath) => finishToolCatalogCommand(io, await (options.captureStdioMcpToolCatalogImpl ?? captureStdioMcpToolCatalog)(server, captureOptions), json, savePath));
|
|
1485
1587
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
break;
|
|
1498
|
-
}
|
|
1499
|
-
if (flag === "--save-response")
|
|
1500
|
-
savePath = value;
|
|
1501
|
-
else
|
|
1502
|
-
tokenEnv = value;
|
|
1503
|
-
index += 1;
|
|
1504
|
-
}
|
|
1505
|
-
else if (allowedFlags.has(flag) && !switches.includes(flag)) {
|
|
1506
|
-
switches.push(flag);
|
|
1507
|
-
}
|
|
1508
|
-
else {
|
|
1509
|
-
invalid = true;
|
|
1510
|
-
break;
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
if (invalid) {
|
|
1514
|
-
io.writeStderr(`Invalid or duplicate tool catalog arguments. ${toolsUsage}`);
|
|
1515
|
-
return 2;
|
|
1516
|
-
}
|
|
1517
|
-
if (!switches.includes("--allow-network")) {
|
|
1518
|
-
io.writeStderr("doctor tools requires explicit --allow-network consent.");
|
|
1519
|
-
return 2;
|
|
1588
|
+
return runHttpCatalogCommand(url, flags, io, terminalContext.env, TOOL_CATALOG_COMMAND, async (target, catalogOptions, json, savePath) => {
|
|
1589
|
+
const snapshot = savePath === undefined
|
|
1590
|
+
? { report: await (options.inspectMcpToolCatalogImpl ?? inspectMcpToolCatalog)(target, catalogOptions), tools: null, cache: null }
|
|
1591
|
+
: await (options.captureMcpToolCatalogImpl ?? captureMcpToolCatalog)(target, catalogOptions);
|
|
1592
|
+
return finishToolCatalogCommand(io, snapshot, json, savePath);
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
if (maybePath === "prompts") {
|
|
1596
|
+
if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
|
|
1597
|
+
io.writeStdout(`${promptsUsage}\nPrompt catalog structure only; prompts are not retrieved.\n--server starts the named stdio server from the package MCP configuration after explicit --runtime consent and stops it after reading the prompt list.\n${forwardedEnvironmentHelp}\n--save-response writes a complete enumeration as one prompts/list response for doctor prompts-file and prompts-diff; nothing is written when enumeration is incomplete.\n${bearerTokenHelp}`);
|
|
1598
|
+
return 0;
|
|
1520
1599
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1600
|
+
const [target, ...flags] = remainingArgs;
|
|
1601
|
+
if (!target || target.startsWith("-")) {
|
|
1602
|
+
io.writeStderr(promptsUsage);
|
|
1523
1603
|
return 2;
|
|
1524
1604
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
io.writeStderr(bearer.error);
|
|
1528
|
-
return 2;
|
|
1605
|
+
if (flags.includes("--server")) {
|
|
1606
|
+
return runStdioCatalogCommand(target, flags, io, terminalContext.env, PROMPT_CATALOG_COMMAND, async (server, captureOptions, json, savePath) => finishPromptCatalogCommand(io, await (options.captureStdioMcpPromptCatalogImpl ?? captureStdioMcpPromptCatalog)(server, captureOptions), json, savePath));
|
|
1529
1607
|
}
|
|
1530
|
-
|
|
1531
|
-
allowNetwork: true,
|
|
1532
|
-
allowLocalNetwork: switches.includes("--allow-local-network"),
|
|
1533
|
-
...(bearer.token === undefined ? {} : { bearerToken: bearer.token })
|
|
1534
|
-
};
|
|
1535
|
-
const snapshot = savePath === undefined
|
|
1536
|
-
? { report: await (options.inspectMcpToolCatalogImpl ?? inspectMcpToolCatalog)(url, catalogOptions), tools: null, cache: null }
|
|
1537
|
-
: await (options.captureMcpToolCatalogImpl ?? captureMcpToolCatalog)(url, catalogOptions);
|
|
1538
|
-
return finishToolCatalogCommand(io, snapshot, switches.includes("--json"), savePath);
|
|
1608
|
+
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));
|
|
1539
1609
|
}
|
|
1540
1610
|
if (maybePath === "discover") {
|
|
1541
1611
|
if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
|
|
@@ -2819,13 +2889,16 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2819
2889
|
return 2;
|
|
2820
2890
|
}
|
|
2821
2891
|
if (toolsDiff.options !== null) {
|
|
2822
|
-
const { server, baseline, sandbox } = toolsDiff.options;
|
|
2892
|
+
const { server, baseline, sandbox, promptsBaseline } = toolsDiff.options;
|
|
2823
2893
|
const toolsResult = await initToolsDiffWorkflow(targetPath, toolsDiff.options);
|
|
2824
2894
|
io.writeStdout([
|
|
2825
2895
|
"Initialized Codex Plugin Doctor tool diff workflow",
|
|
2826
2896
|
`Root: ${toolsResult.rootPath}`,
|
|
2827
2897
|
`Workflow: ${toolsResult.workflowPath}`,
|
|
2828
|
-
`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
|
+
])
|
|
2829
2902
|
].join("\n"));
|
|
2830
2903
|
return 0;
|
|
2831
2904
|
}
|
package/package.json
CHANGED