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:
Kjell Tore Guttormsen 2026-05-01 17:47:09 +02:00
commit 5eecb968d8
7 changed files with 416 additions and 25 deletions

View file

@ -13,6 +13,7 @@ import { join, basename, resolve } from 'node:path';
import { finding, scannerResult, resetCounter } from './lib/output.mjs';
import { SEVERITY } from './lib/severity.mjs';
import { parseFrontmatter } from './lib/yaml-parser.mjs';
import { humanizeFindings } from './lib/humanizer.mjs';
const SCANNER = 'PLH';
@ -420,10 +421,13 @@ async function main() {
const args = process.argv.slice(2);
let targetPath = '.';
let jsonMode = false;
let rawMode = false;
for (let i = 0; i < args.length; i++) {
if (args[i] === '--json') {
jsonMode = true;
} else if (args[i] === '--raw') {
rawMode = true;
} else if (!args[i].startsWith('-')) {
targetPath = args[i];
}
@ -434,13 +438,15 @@ async function main() {
const result = await scan(targetPath);
if (jsonMode) {
if (jsonMode || rawMode) {
// --json and --raw both write the v5.0.0-shape result (byte-identical).
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
} else {
// Brief summary
const count = result.findings.length;
// Default mode humanizes finding titles before writing the brief summary.
const findings = humanizeFindings(result.findings);
const count = findings.length;
process.stderr.write(`Findings: ${count}\n`);
for (const f of result.findings) {
for (const f of findings) {
process.stderr.write(` [${f.severity}] ${f.title}\n`);
}
}