codex-plugin-doctor 1.44.0 → 1.45.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 +2 -2
- package/dist/core/config-validate.d.ts +13 -0
- package/dist/core/config-validate.js +133 -0
- package/dist/core/shell-completion.d.ts +2 -0
- package/dist/core/shell-completion.js +97 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/run-cli.js +30 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -406,9 +406,9 @@ jobs:
|
|
|
406
406
|
runs-on: ubuntu-latest
|
|
407
407
|
steps:
|
|
408
408
|
- uses: actions/checkout@v5
|
|
409
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
409
|
+
- uses: Esquetta/CodexPluginDoctor@v1.45.0
|
|
410
410
|
with:
|
|
411
|
-
version: "1.
|
|
411
|
+
version: "1.45.0"
|
|
412
412
|
path: .
|
|
413
413
|
runtime: "true"
|
|
414
414
|
policy: codex-publish
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ConfigValidationFinding {
|
|
2
|
+
field: string;
|
|
3
|
+
message: string;
|
|
4
|
+
severity: "fail" | "warn";
|
|
5
|
+
}
|
|
6
|
+
export interface ConfigValidationReport {
|
|
7
|
+
configPath: string;
|
|
8
|
+
status: "pass" | "fail";
|
|
9
|
+
findings: ConfigValidationFinding[];
|
|
10
|
+
}
|
|
11
|
+
export declare function validateConfigFile(configPath: string): Promise<ConfigValidationReport>;
|
|
12
|
+
export declare function renderConfigValidation(report: ConfigValidationReport): string;
|
|
13
|
+
export declare function renderConfigValidationJson(report: ConfigValidationReport): string;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const knownFields = ["ignoreRules", "failOnWarnings", "suppressions"];
|
|
4
|
+
export async function validateConfigFile(configPath) {
|
|
5
|
+
const resolvedPath = path.resolve(configPath);
|
|
6
|
+
const findings = [];
|
|
7
|
+
try {
|
|
8
|
+
await stat(resolvedPath);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return {
|
|
12
|
+
configPath: resolvedPath,
|
|
13
|
+
status: "fail",
|
|
14
|
+
findings: [
|
|
15
|
+
{ field: "file", message: `Config file not found: ${resolvedPath}`, severity: "fail" }
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
let parsed;
|
|
20
|
+
try {
|
|
21
|
+
parsed = JSON.parse(await readFile(resolvedPath, "utf8"));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return {
|
|
25
|
+
configPath: resolvedPath,
|
|
26
|
+
status: "fail",
|
|
27
|
+
findings: [
|
|
28
|
+
{ field: "json", message: `Invalid JSON: ${error.message}`, severity: "fail" }
|
|
29
|
+
]
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
33
|
+
return {
|
|
34
|
+
configPath: resolvedPath,
|
|
35
|
+
status: "fail",
|
|
36
|
+
findings: [
|
|
37
|
+
{ field: "root", message: "Config must be a JSON object.", severity: "fail" }
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const config = parsed;
|
|
42
|
+
for (const key of Object.keys(config)) {
|
|
43
|
+
if (!knownFields.includes(key)) {
|
|
44
|
+
findings.push({
|
|
45
|
+
field: key,
|
|
46
|
+
message: `Unknown field: "${key}". Known fields: ${knownFields.join(", ")}`,
|
|
47
|
+
severity: "warn"
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if ("ignoreRules" in config) {
|
|
52
|
+
const ignoreRules = config.ignoreRules;
|
|
53
|
+
if (!Array.isArray(ignoreRules)) {
|
|
54
|
+
findings.push({ field: "ignoreRules", message: "Must be an array of strings.", severity: "fail" });
|
|
55
|
+
}
|
|
56
|
+
else if (!ignoreRules.every((r) => typeof r === "string")) {
|
|
57
|
+
findings.push({ field: "ignoreRules", message: "All entries must be strings.", severity: "fail" });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if ("failOnWarnings" in config) {
|
|
61
|
+
if (typeof config.failOnWarnings !== "boolean") {
|
|
62
|
+
findings.push({ field: "failOnWarnings", message: "Must be a boolean.", severity: "fail" });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if ("suppressions" in config) {
|
|
66
|
+
const suppressions = config.suppressions;
|
|
67
|
+
if (!Array.isArray(suppressions)) {
|
|
68
|
+
findings.push({ field: "suppressions", message: "Must be an array.", severity: "fail" });
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
for (let i = 0; i < suppressions.length; i += 1) {
|
|
72
|
+
const entry = suppressions[i];
|
|
73
|
+
if (!entry || typeof entry !== "object") {
|
|
74
|
+
findings.push({ field: `suppressions[${i}]`, message: "Must be an object.", severity: "fail" });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (typeof entry.fingerprint !== "string" || !/^[a-f0-9]{64}$/.test(entry.fingerprint)) {
|
|
78
|
+
findings.push({
|
|
79
|
+
field: `suppressions[${i}].fingerprint`,
|
|
80
|
+
message: "Must be a 64-character lowercase hex string.",
|
|
81
|
+
severity: "fail"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (typeof entry.reason !== "string" || entry.reason.trim().length === 0) {
|
|
85
|
+
findings.push({
|
|
86
|
+
field: `suppressions[${i}].reason`,
|
|
87
|
+
message: "Must be a non-empty string.",
|
|
88
|
+
severity: "fail"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (typeof entry.expiresAt !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(entry.expiresAt)) {
|
|
92
|
+
findings.push({
|
|
93
|
+
field: `suppressions[${i}].expiresAt`,
|
|
94
|
+
message: "Must be a YYYY-MM-DD date string.",
|
|
95
|
+
severity: "fail"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const hasFail = findings.some((f) => f.severity === "fail");
|
|
102
|
+
return {
|
|
103
|
+
configPath: resolvedPath,
|
|
104
|
+
status: hasFail ? "fail" : findings.length === 0 ? "pass" : "warn",
|
|
105
|
+
findings
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export function renderConfigValidation(report) {
|
|
109
|
+
const lines = [
|
|
110
|
+
"Codex Plugin Doctor Config Validation",
|
|
111
|
+
"=====================================",
|
|
112
|
+
`Path: ${report.configPath}`,
|
|
113
|
+
`Status: ${report.status.toUpperCase()}`,
|
|
114
|
+
""
|
|
115
|
+
];
|
|
116
|
+
if (report.findings.length === 0) {
|
|
117
|
+
lines.push("Config is valid.");
|
|
118
|
+
return lines.join("\n");
|
|
119
|
+
}
|
|
120
|
+
for (const finding of report.findings) {
|
|
121
|
+
const tag = finding.severity === "fail" ? "FAIL" : "WARN";
|
|
122
|
+
lines.push(`${tag} ${finding.field}: ${finding.message}`);
|
|
123
|
+
}
|
|
124
|
+
return lines.join("\n");
|
|
125
|
+
}
|
|
126
|
+
export function renderConfigValidationJson(report) {
|
|
127
|
+
return JSON.stringify({
|
|
128
|
+
schemaVersion: "1.0.0",
|
|
129
|
+
configPath: report.configPath,
|
|
130
|
+
status: report.status,
|
|
131
|
+
findings: report.findings
|
|
132
|
+
}, null, 2);
|
|
133
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const topLevelCommands = [
|
|
2
|
+
"check",
|
|
3
|
+
"audit",
|
|
4
|
+
"mcp",
|
|
5
|
+
"security",
|
|
6
|
+
"compat",
|
|
7
|
+
"suppress",
|
|
8
|
+
"fix",
|
|
9
|
+
"history",
|
|
10
|
+
"watch",
|
|
11
|
+
"doctor",
|
|
12
|
+
"init",
|
|
13
|
+
"init-ci",
|
|
14
|
+
"init-git-hooks",
|
|
15
|
+
"self-test",
|
|
16
|
+
"list",
|
|
17
|
+
"explain",
|
|
18
|
+
"config"
|
|
19
|
+
];
|
|
20
|
+
function bashCompletion() {
|
|
21
|
+
return [
|
|
22
|
+
"_codex_plugin_doctor() {",
|
|
23
|
+
" local cur prev",
|
|
24
|
+
" COMPREPLY=()",
|
|
25
|
+
" cur=\"${COMP_WORDS[COMP_CWORD]}\"",
|
|
26
|
+
" prev=\"${COMP_WORDS[COMP_CWORD-1]}\"",
|
|
27
|
+
"",
|
|
28
|
+
` local commands="${topLevelCommands.join(" ")}"`,
|
|
29
|
+
"",
|
|
30
|
+
" case \"${prev}\" in",
|
|
31
|
+
" codex-plugin-doctor)",
|
|
32
|
+
" COMPREPLY=( $(compgen -W \"${commands}\" -- \"${cur}\") )",
|
|
33
|
+
" return 0",
|
|
34
|
+
" ;;",
|
|
35
|
+
" esac",
|
|
36
|
+
"",
|
|
37
|
+
" case \"${cur}\" in",
|
|
38
|
+
" --*)",
|
|
39
|
+
" local flags=\"--json --output --runtime --policy --help\"",
|
|
40
|
+
" COMPREPLY=( $(compgen -W \"${flags}\" -- \"${cur}\") )",
|
|
41
|
+
" return 0",
|
|
42
|
+
" ;;",
|
|
43
|
+
" *)",
|
|
44
|
+
" COMPREPLY=( $(compgen -W \"${commands}\" -- \"${cur}\") )",
|
|
45
|
+
" return 0",
|
|
46
|
+
" ;;",
|
|
47
|
+
" esac",
|
|
48
|
+
"}",
|
|
49
|
+
"",
|
|
50
|
+
"complete -F _codex_plugin_doctor codex-plugin-doctor",
|
|
51
|
+
""
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
54
|
+
function zshCompletion() {
|
|
55
|
+
return [
|
|
56
|
+
"#compdef codex-plugin-doctor",
|
|
57
|
+
"",
|
|
58
|
+
"_codex_plugin_doctor() {",
|
|
59
|
+
" local context state line",
|
|
60
|
+
" typeset -A opt_args",
|
|
61
|
+
"",
|
|
62
|
+
` local commands=(${topLevelCommands.join(" ")})`,
|
|
63
|
+
"",
|
|
64
|
+
" _arguments -C \\",
|
|
65
|
+
" '1:command:(${commands})' \\",
|
|
66
|
+
" '*--json[Output as JSON]' \\",
|
|
67
|
+
" '*--output[Write to file]:file:_files' \\",
|
|
68
|
+
" '*--runtime[Enable runtime probes]' \\",
|
|
69
|
+
" '*--policy[Apply policy]:policy:(codex-publish mcp-strict security)'",
|
|
70
|
+
"}",
|
|
71
|
+
"",
|
|
72
|
+
"_codex_plugin_doctor \"$@\"",
|
|
73
|
+
""
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
function fishCompletion() {
|
|
77
|
+
return [
|
|
78
|
+
"complete -c codex-plugin-doctor -f",
|
|
79
|
+
`complete -c codex-plugin-doctor -n "not __fish_seen_subcommand_from ${topLevelCommands.join(" ")}" -a "${topLevelCommands.join(" ")}"`,
|
|
80
|
+
"complete -c codex-plugin-doctor -s h -l help -d 'Show help'",
|
|
81
|
+
"complete -c codex-plugin-doctor -l json -d 'Output as JSON'",
|
|
82
|
+
"complete -c codex-plugin-doctor -l output -d 'Write to file' -r",
|
|
83
|
+
"complete -c codex-plugin-doctor -l runtime -d 'Enable runtime probes'",
|
|
84
|
+
"complete -c codex-plugin-doctor -l policy -d 'Apply policy' -x -a 'codex-publish mcp-strict security'",
|
|
85
|
+
""
|
|
86
|
+
].join("\n");
|
|
87
|
+
}
|
|
88
|
+
export function generateCompletion(shell) {
|
|
89
|
+
switch (shell) {
|
|
90
|
+
case "bash":
|
|
91
|
+
return bashCompletion();
|
|
92
|
+
case "zsh":
|
|
93
|
+
return zshCompletion();
|
|
94
|
+
case "fish":
|
|
95
|
+
return fishCompletion();
|
|
96
|
+
}
|
|
97
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,4 +27,6 @@ export { buildGenericMcpDoctor, renderGenericMcpDoctor, renderGenericMcpDoctorJs
|
|
|
27
27
|
export { watchPlugin, type WatchPluginOptions, type WatchPluginResult } from "./core/watch-plugin.js";
|
|
28
28
|
export { buildDepAudit, buildDepAuditRecommendations, renderDepAudit, renderDepAuditJson, renderDepAuditSarif, type DepAuditRecommendation, type DepAuditReport, type DepAuditVulnerability } from "./core/dep-audit.js";
|
|
29
29
|
export { initGitHooks, removeGitHooks, type InitGitHooksResult, type RemoveGitHooksResult } from "./core/init-git-hooks.js";
|
|
30
|
+
export { generateCompletion, type CompletionShell } from "./core/shell-completion.js";
|
|
31
|
+
export { validateConfigFile, renderConfigValidation, renderConfigValidationJson, type ConfigValidationFinding, type ConfigValidationReport } from "./core/config-validate.js";
|
|
30
32
|
export declare function runCheck(targetPath: string, options?: CheckOptions): Promise<CheckResult>;
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,8 @@ export { buildGenericMcpDoctor, renderGenericMcpDoctor, renderGenericMcpDoctorJs
|
|
|
27
27
|
export { watchPlugin } from "./core/watch-plugin.js";
|
|
28
28
|
export { buildDepAudit, buildDepAuditRecommendations, renderDepAudit, renderDepAuditJson, renderDepAuditSarif } from "./core/dep-audit.js";
|
|
29
29
|
export { initGitHooks, removeGitHooks } from "./core/init-git-hooks.js";
|
|
30
|
+
export { generateCompletion } from "./core/shell-completion.js";
|
|
31
|
+
export { validateConfigFile, renderConfigValidation, renderConfigValidationJson } from "./core/config-validate.js";
|
|
30
32
|
export async function runCheck(targetPath, options = {}) {
|
|
31
33
|
return validatePlugin(targetPath, options);
|
|
32
34
|
}
|
package/dist/run-cli.js
CHANGED
|
@@ -34,6 +34,8 @@ import { initCiWorkflow } from "./core/init-ci.js";
|
|
|
34
34
|
import { watchPlugin } from "./core/watch-plugin.js";
|
|
35
35
|
import { buildDepAudit, renderDepAudit, renderDepAuditJson, renderDepAuditSarif } from "./core/dep-audit.js";
|
|
36
36
|
import { initGitHooks, removeGitHooks } from "./core/init-git-hooks.js";
|
|
37
|
+
import { generateCompletion } from "./core/shell-completion.js";
|
|
38
|
+
import { renderConfigValidation, renderConfigValidationJson, validateConfigFile } from "./core/config-validate.js";
|
|
37
39
|
import { initPluginPackage, initPluginTemplates, isInitPluginTemplate } from "./core/init-plugin.js";
|
|
38
40
|
import { runCheck } from "./index.js";
|
|
39
41
|
import { buildGenericMcpDoctor, renderGenericMcpDoctor, renderGenericMcpDoctorJson } from "./mcp/generic-mcp-doctor.js";
|
|
@@ -79,7 +81,7 @@ const defaultIo = {
|
|
|
79
81
|
}
|
|
80
82
|
};
|
|
81
83
|
function printUsage(io) {
|
|
82
|
-
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] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>]\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> [--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|runtime-plan <path> [--json|--markdown] [--output <path>]|runtime-policy <path> [--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 [--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 [--upload]|mcp <path>|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 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");
|
|
84
|
+
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] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>]\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> [--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|runtime-plan <path> [--json|--markdown] [--output <path>]|runtime-policy <path> [--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 [--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 [--upload]|mcp <path>|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 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");
|
|
83
85
|
io.writeStderr("Suppression governance: codex-plugin-doctor suppress list <path> [--fail-on-expired] [--fail-on-invalid] [--warn-expiring-within-days <days>]\n codex-plugin-doctor suppress prune <path> [--apply] [--json]");
|
|
84
86
|
io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
|
|
85
87
|
}
|
|
@@ -2220,6 +2222,33 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2220
2222
|
io.writeStdout(report);
|
|
2221
2223
|
return matrixExitCode(matrix);
|
|
2222
2224
|
}
|
|
2225
|
+
if (command === "completion") {
|
|
2226
|
+
const shell = maybePath;
|
|
2227
|
+
if (!shell || !["bash", "zsh", "fish"].includes(shell)) {
|
|
2228
|
+
io.writeStderr("Usage: codex-plugin-doctor completion bash|zsh|fish");
|
|
2229
|
+
return 2;
|
|
2230
|
+
}
|
|
2231
|
+
io.writeStdout(generateCompletion(shell));
|
|
2232
|
+
return 0;
|
|
2233
|
+
}
|
|
2234
|
+
if (command === "config") {
|
|
2235
|
+
const subCommand = maybePath;
|
|
2236
|
+
if (subCommand !== "validate") {
|
|
2237
|
+
io.writeStderr("Usage: codex-plugin-doctor config validate <path> [--json]");
|
|
2238
|
+
return 2;
|
|
2239
|
+
}
|
|
2240
|
+
const configPath = remainingArgs[0] && !remainingArgs[0].startsWith("--") ? remainingArgs[0] : ".codex-doctor.json";
|
|
2241
|
+
const configFlags = remainingArgs[0] && remainingArgs[0].startsWith("--")
|
|
2242
|
+
? remainingArgs
|
|
2243
|
+
: remainingArgs.slice(1);
|
|
2244
|
+
const jsonOutput = configFlags.includes("--json");
|
|
2245
|
+
const report = await validateConfigFile(configPath);
|
|
2246
|
+
const output = jsonOutput
|
|
2247
|
+
? renderConfigValidationJson(report)
|
|
2248
|
+
: renderConfigValidation(report);
|
|
2249
|
+
io.writeStdout(output);
|
|
2250
|
+
return report.status === "fail" ? 1 : 0;
|
|
2251
|
+
}
|
|
2223
2252
|
if (command === "suppress") {
|
|
2224
2253
|
const parsedSuppressCommand = parseSuppressCommand([maybePath ?? "", ...remainingArgs]);
|
|
2225
2254
|
if ("message" in parsedSuppressCommand) {
|
package/package.json
CHANGED