@docker-doctor/cli 0.5.1 → 0.5.3
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/dist/cli.cjs +37 -15
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +37 -15
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +15 -13
- package/dist/index.d.mts +15 -13
- package/dist/index.mjs +1 -1
- package/dist/{src-NqSwkGiS.mjs → src-CklEIXTX.mjs} +312 -91
- package/dist/src-CklEIXTX.mjs.map +1 -0
- package/dist/{src-DYLANvt2.cjs → src-D4rVJg36.cjs} +312 -91
- package/dist/src-D4rVJg36.cjs.map +1 -0
- package/package.json +4 -4
- package/skill/docker-doctor/references/explain.md +6 -5
- package/dist/src-DYLANvt2.cjs.map +0 -1
- package/dist/src-NqSwkGiS.mjs.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_src = require('./src-
|
|
2
|
+
const require_src = require('./src-D4rVJg36.cjs');
|
|
3
3
|
let node_fs_promises = require("node:fs/promises");
|
|
4
4
|
node_fs_promises = require_src.__toESM(node_fs_promises, 1);
|
|
5
5
|
let node_path = require("node:path");
|
|
@@ -581,9 +581,17 @@ const writeDiagnosticsDirectory = async (diagnostics, report, rootDir) => {
|
|
|
581
581
|
recursive: true
|
|
582
582
|
});
|
|
583
583
|
await node_fs_promises.default.mkdir(dir, { recursive: true });
|
|
584
|
+
const sanitizedReport = {
|
|
585
|
+
...report,
|
|
586
|
+
diagnostics: report.diagnostics.map((d) => ({
|
|
587
|
+
...d,
|
|
588
|
+
file: sanitizePath(d.file),
|
|
589
|
+
message: sanitizeMessage(d.message)
|
|
590
|
+
}))
|
|
591
|
+
};
|
|
584
592
|
await node_fs_promises.default.writeFile(node_path.default.join(dir, "diagnostics.json"), JSON.stringify({
|
|
585
593
|
note: TRUST_BOUNDARY_NOTE,
|
|
586
|
-
...
|
|
594
|
+
...sanitizedReport
|
|
587
595
|
}, null, 2), "utf-8");
|
|
588
596
|
const writes = [];
|
|
589
597
|
for (const [rule, ruleDiagnostics] of groupDiagnosticsByRule(diagnostics)) {
|
|
@@ -656,7 +664,7 @@ const buildHandoffPayload = (input) => {
|
|
|
656
664
|
];
|
|
657
665
|
for (const [index, [rule, ruleDiagnostics]] of groups.entries()) {
|
|
658
666
|
const [first] = ruleDiagnostics;
|
|
659
|
-
const category = require_src.findRule(rule)?.category ?? "General";
|
|
667
|
+
const category = first.category ?? require_src.findRule(rule)?.category ?? "General";
|
|
660
668
|
const countBadge = ruleDiagnostics.length > 1 ? ` (×${ruleDiagnostics.length})` : "";
|
|
661
669
|
lines.push(`${index + 1}. ${SEVERITY_LABEL[first.severity]} ${category}: ${sanitizeMessage(first.message)} [${rule}]${countBadge}`, ` Fix: ${first.help}`);
|
|
662
670
|
const files = [...new Set(ruleDiagnostics.map((d) => d.file))];
|
|
@@ -791,7 +799,7 @@ const printDiagnostics = (diagnostics, verbose, fileContents, categoryIssueCount
|
|
|
791
799
|
console.log(`\n Run ${chalk.cyan("docker-doctor --verbose")} to list every error and warning`);
|
|
792
800
|
const ruleCounts = {};
|
|
793
801
|
for (const d of diagnostics) ruleCounts[d.rule] = (ruleCounts[d.rule] || 0) + 1;
|
|
794
|
-
const migrationRules = Object.entries(ruleCounts).filter(([
|
|
802
|
+
const migrationRules = Object.entries(ruleCounts).filter(([, count]) => count >= 5);
|
|
795
803
|
if (migrationRules.length > 0) {
|
|
796
804
|
console.log();
|
|
797
805
|
console.log(` ${chalk.yellow("⚠ Migration-scale change: sample before you sweep")}`);
|
|
@@ -918,7 +926,7 @@ const formatTerminal = async (diagnostics, score, label, project, verbose = fals
|
|
|
918
926
|
let warningsCount = 0;
|
|
919
927
|
let errorsCount = 0;
|
|
920
928
|
for (const d of diagnostics) {
|
|
921
|
-
const category = require_src.findRule(d.rule)?.category
|
|
929
|
+
const category = d.category ?? require_src.findRule(d.rule)?.category ?? "Best Practices";
|
|
922
930
|
categoryIssueCounts[category] = (categoryIssueCounts[category] || 0) + 1;
|
|
923
931
|
if (d.severity === "warning") warningsCount += 1;
|
|
924
932
|
else if (d.severity === "error") errorsCount += 1;
|
|
@@ -1183,9 +1191,14 @@ const runInteractiveWizard = async (context) => {
|
|
|
1183
1191
|
console.log(` Inputs and gating: ${chalk.cyan("https://docker-doctor.vercel.app/docs/guides/github-actions")}`);
|
|
1184
1192
|
}
|
|
1185
1193
|
}
|
|
1186
|
-
if (context.diagnostics.length === 0) return;
|
|
1194
|
+
if (context.diagnostics.length === 0) return true;
|
|
1187
1195
|
await runAgentHandoff(context);
|
|
1188
|
-
|
|
1196
|
+
return true;
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1199
|
+
console.error(`\n ${chalk.yellow("⚠")} Post-scan step failed: ${msg}`);
|
|
1200
|
+
return false;
|
|
1201
|
+
}
|
|
1189
1202
|
};
|
|
1190
1203
|
const runRulesEngine = async (rootDir, project, rulesConfig, categoriesConfig, projectFilesList, fileContents, options, setStatus) => {
|
|
1191
1204
|
const diagnostics = [];
|
|
@@ -1200,7 +1213,7 @@ const runRulesEngine = async (rootDir, project, rulesConfig, categoriesConfig, p
|
|
|
1200
1213
|
try {
|
|
1201
1214
|
const content = await node_fs_promises.default.readFile(fullPath, "utf-8");
|
|
1202
1215
|
fileContents[df] = content;
|
|
1203
|
-
const instructions = require_src.parseDockerfile(content);
|
|
1216
|
+
const instructions = require_src.parseDockerfile(content, df);
|
|
1204
1217
|
return require_src.runDockerfileRules(instructions, df, projectFilesList, rulesConfig, categoriesConfig);
|
|
1205
1218
|
} catch (error) {
|
|
1206
1219
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -1241,6 +1254,7 @@ const runRulesEngine = async (rootDir, project, rulesConfig, categoriesConfig, p
|
|
|
1241
1254
|
};
|
|
1242
1255
|
};
|
|
1243
1256
|
const SCAN_FAILURE_EXIT_CODE = 2;
|
|
1257
|
+
const POST_SCAN_FAILURE_EXIT_CODE = 1;
|
|
1244
1258
|
const WHITESPACE_RUN = /\s+/gu;
|
|
1245
1259
|
const flattenMessage = (message) => message.replaceAll(WHITESPACE_RUN, " ").trim();
|
|
1246
1260
|
const scanExitCode = (scanIncomplete, failing) => {
|
|
@@ -1336,12 +1350,14 @@ program.argument("[dir]", "directory to scan", ".").option("-v, --verbose", "sho
|
|
|
1336
1350
|
return;
|
|
1337
1351
|
}
|
|
1338
1352
|
await formatTerminal(diagnostics, score, label, project, options.verbose, fileContents);
|
|
1339
|
-
|
|
1353
|
+
let wizardOk = true;
|
|
1354
|
+
if (process.stdout.isTTY && process.stdin.isTTY) wizardOk = await runInteractiveWizard({
|
|
1340
1355
|
diagnostics,
|
|
1341
1356
|
report: require_src.toJsonReport(diagnostics, score, label, project),
|
|
1342
1357
|
rootDir
|
|
1343
1358
|
});
|
|
1344
|
-
|
|
1359
|
+
const exitCode = scanExitCode(scanIncomplete, hasErrors);
|
|
1360
|
+
process.exitCode = wizardOk ? exitCode : Math.max(POST_SCAN_FAILURE_EXIT_CODE, exitCode);
|
|
1345
1361
|
} finally {
|
|
1346
1362
|
if (spinnerInterval !== null) {
|
|
1347
1363
|
clearInterval(spinnerInterval);
|
|
@@ -1351,7 +1367,7 @@ program.argument("[dir]", "directory to scan", ".").option("-v, --verbose", "sho
|
|
|
1351
1367
|
} catch (error) {
|
|
1352
1368
|
const msg = error instanceof Error ? error.message : String(error);
|
|
1353
1369
|
console.error(`Error: ${msg}`);
|
|
1354
|
-
process.
|
|
1370
|
+
process.exitCode = 1;
|
|
1355
1371
|
}
|
|
1356
1372
|
});
|
|
1357
1373
|
const CURATED_INSTALL_AGENTS = [
|
|
@@ -1385,10 +1401,14 @@ const resolveInstallAgents = async (requested) => {
|
|
|
1385
1401
|
program.command("install").description("install the Docker Doctor agent skill for your coding agents").option("-a, --agent <agents...>", "agent id(s) to install for (e.g. claude-code codex cursor)").option("-g, --global", "install into each agent's global skills directory (e.g. ~/.claude/skills) instead of the current project").action(async (options) => {
|
|
1386
1402
|
if (!getSkillSourceDirectory()) {
|
|
1387
1403
|
console.error("Bundled skill not found — this looks like a broken installation.");
|
|
1388
|
-
process.
|
|
1404
|
+
process.exitCode = 1;
|
|
1405
|
+
return;
|
|
1389
1406
|
}
|
|
1390
1407
|
const agents = await resolveInstallAgents(options.agent);
|
|
1391
|
-
if (agents === null)
|
|
1408
|
+
if (agents === null) {
|
|
1409
|
+
process.exitCode = 1;
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1392
1412
|
if (agents.length === 0) {
|
|
1393
1413
|
console.log("Nothing selected — skipped.");
|
|
1394
1414
|
return;
|
|
@@ -1399,7 +1419,8 @@ program.command("install").description("install the Docker Doctor agent skill fo
|
|
|
1399
1419
|
});
|
|
1400
1420
|
if (!result) {
|
|
1401
1421
|
console.error("Failed to install the skill.");
|
|
1402
|
-
process.
|
|
1422
|
+
process.exitCode = 1;
|
|
1423
|
+
return;
|
|
1403
1424
|
}
|
|
1404
1425
|
for (const installed of result.installed) console.log(` ${chalk.green("✔")} ${agentDisplayName(installed.agent)} → ${installed.path}`);
|
|
1405
1426
|
for (const failed of result.failed) console.log(` ${chalk.red("✖")} ${agentDisplayName(failed.agent)}: ${failed.error}`);
|
|
@@ -1423,7 +1444,8 @@ rules.command("explain <rule>").description("explain a specific rule in detail")
|
|
|
1423
1444
|
const rule = require_src.findRule(ruleKey);
|
|
1424
1445
|
if (!rule) {
|
|
1425
1446
|
console.error(`Rule '${ruleKey}' not found.`);
|
|
1426
|
-
process.
|
|
1447
|
+
process.exitCode = 1;
|
|
1448
|
+
return;
|
|
1427
1449
|
}
|
|
1428
1450
|
console.log(`\nRule: ${rule.key}`);
|
|
1429
1451
|
console.log(`Category: ${rule.category}`);
|