feat(humanizer): wire humanizer into 6 remaining CLIs with --raw
Adds --raw flag to all 6 remaining CLIs and wires humanization into the
default rendering path. --json and --raw both bypass humanization for
v5.0.0 byte-equal output; default mode humanizes findings/diff/prose.
token-hotspots-cli: humanizes payload.findings before stdout JSON write.
plugin-health-scanner: humanizes finding titles in stderr brief summary;
--json/--raw write byte-identical v5.0.0-shape result to stdout.
drift-cli: humanizes diff.{newFindings,resolvedFindings,unchangedFindings,
movedFindings} before formatDiffReport; --raw applies to save and list
modes too. Baselines remain raw v5.0.0 on disk.
fix-cli: humanizes manual-finding titles in stderr fix-plan prose; both
--json and --raw produce identical machine-readable JSON to stdout.
manifest, whats-active: --raw is a no-op (no findings, inventory only)
but parsed for CLI surface consistency.
Decision on missing --output-file flag for drift-cli/fix-cli/plugin-health:
deferred. SC-6/SC-7 tests in Wave 4 will use stdout-redirect (the simpler
Alt B path) since these CLIs already write JSON to stdout in machine modes.
Test cli-humanizer.test.mjs covers all 6 CLIs. Three CLIs that read
environment state (plugin-health, manifest, whats-active) verify
mode-equivalence (--json == --raw) instead of frozen-snapshot byte-equal,
because their output reflects current marketplace state which drifts as
plugins are added since the Wave 0 capture.
Wave 3 / Step 7 of v5.1.0 humanizer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
3041c90115
commit
5eecb968d8
7 changed files with 416 additions and 25 deletions
|
|
@ -12,12 +12,14 @@ import { resolve } from 'node:path';
|
|||
import { runAllScanners } from './scan-orchestrator.mjs';
|
||||
import { planFixes, applyFixes, verifyFixes } from './fix-engine.mjs';
|
||||
import { createBackup } from './lib/backup.mjs';
|
||||
import { humanizeFinding } from './lib/humanizer.mjs';
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
let targetPath = '.';
|
||||
let apply = false;
|
||||
let jsonMode = false;
|
||||
let rawMode = false;
|
||||
let includeGlobal = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
|
|
@ -25,6 +27,8 @@ async function main() {
|
|||
apply = true;
|
||||
} else if (args[i] === '--json') {
|
||||
jsonMode = true;
|
||||
} else if (args[i] === '--raw') {
|
||||
rawMode = true;
|
||||
} else if (args[i] === '--global') {
|
||||
includeGlobal = true;
|
||||
} else if (!args[i].startsWith('-')) {
|
||||
|
|
@ -32,9 +36,12 @@ async function main() {
|
|||
}
|
||||
}
|
||||
|
||||
// Whether to suppress prose stderr (true for both --json and --raw machine paths).
|
||||
const machineMode = jsonMode || rawMode;
|
||||
|
||||
const resolvedPath = resolve(targetPath);
|
||||
|
||||
if (!jsonMode) {
|
||||
if (!machineMode) {
|
||||
process.stderr.write(`Config-Audit Fix CLI v2.1.0\n`);
|
||||
process.stderr.write(`Target: ${resolvedPath}\n`);
|
||||
process.stderr.write(`Mode: ${apply ? 'APPLY' : 'DRY-RUN'}\n\n`);
|
||||
|
|
@ -47,7 +54,7 @@ async function main() {
|
|||
// 2. Plan fixes
|
||||
const { fixes, skipped, manual } = planFixes(envelope);
|
||||
|
||||
if (!jsonMode) {
|
||||
if (!machineMode) {
|
||||
process.stderr.write(`\n`);
|
||||
process.stderr.write(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
|
||||
process.stderr.write(` Config-Audit Fix Plan\n`);
|
||||
|
|
@ -63,9 +70,20 @@ async function main() {
|
|||
}
|
||||
|
||||
if (manual.length > 0) {
|
||||
// Default mode humanizes the manual-finding titles for the prose render.
|
||||
// The JSON `manual` array (later in this function) keeps v5.0.0 verbatim.
|
||||
process.stderr.write(`\n Manual (${manual.length}):\n`);
|
||||
for (let i = 0; i < manual.length; i++) {
|
||||
process.stderr.write(` ${fixes.length + i + 1}. [${manual[i].findingId}] ${manual[i].title}\n`);
|
||||
const m = manual[i];
|
||||
const title = humanizeFinding({
|
||||
id: m.findingId,
|
||||
scanner: typeof m.findingId === 'string' ? m.findingId.split('-')[1] || '' : '',
|
||||
severity: m.severity || 'info',
|
||||
title: m.title,
|
||||
description: m.description || '',
|
||||
recommendation: m.recommendation || '',
|
||||
}).title;
|
||||
process.stderr.write(` ${fixes.length + i + 1}. [${m.findingId}] ${title}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +102,7 @@ async function main() {
|
|||
let backupId = null;
|
||||
|
||||
if (fixes.length === 0) {
|
||||
if (jsonMode) {
|
||||
if (machineMode) {
|
||||
const output = { planned: [], applied: [], failed: [], verified: [], regressions: [], manual, backupId: null };
|
||||
process.stdout.write(JSON.stringify(output, null, 2) + '\n');
|
||||
}
|
||||
|
|
@ -97,7 +115,7 @@ async function main() {
|
|||
const backup = createBackup(filesToBackup);
|
||||
backupId = backup.backupId;
|
||||
|
||||
if (!jsonMode) {
|
||||
if (!machineMode) {
|
||||
process.stderr.write(`\n Backup created: ${backup.backupPath}\n`);
|
||||
process.stderr.write(` Applying ${fixes.length} fixes...\n\n`);
|
||||
}
|
||||
|
|
@ -106,7 +124,7 @@ async function main() {
|
|||
applied = result.applied;
|
||||
failed = result.failed;
|
||||
|
||||
if (!jsonMode) {
|
||||
if (!machineMode) {
|
||||
process.stderr.write(` Results: ${applied.length} applied, ${failed.length} failed\n`);
|
||||
if (failed.length > 0) {
|
||||
for (const f of failed) {
|
||||
|
|
@ -117,7 +135,7 @@ async function main() {
|
|||
|
||||
// 4. Verify
|
||||
if (applied.length > 0) {
|
||||
if (!jsonMode) {
|
||||
if (!machineMode) {
|
||||
process.stderr.write(`\n Verifying...\n`);
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +143,7 @@ async function main() {
|
|||
verified = verification.verified;
|
||||
regressions = verification.regressions;
|
||||
|
||||
if (!jsonMode) {
|
||||
if (!machineMode) {
|
||||
process.stderr.write(` Verified: ${verified.length}/${applied.length}\n`);
|
||||
if (regressions.length > 0) {
|
||||
process.stderr.write(` Regressions: ${regressions.join(', ')}\n`);
|
||||
|
|
@ -138,13 +156,13 @@ async function main() {
|
|||
const result = await applyFixes(fixes, { dryRun: true });
|
||||
applied = result.applied;
|
||||
|
||||
if (!jsonMode) {
|
||||
if (!machineMode) {
|
||||
process.stderr.write(`\n Dry-run complete. Pass --apply to execute.\n`);
|
||||
}
|
||||
}
|
||||
|
||||
// JSON output
|
||||
if (jsonMode) {
|
||||
// JSON output (both --json and --raw write byte-equal v5.0.0-shape stdout)
|
||||
if (machineMode) {
|
||||
const output = {
|
||||
planned: fixes.map(f => ({
|
||||
findingId: f.findingId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue