Dogfooding `/config-audit` (the router) against the repo, fasit written before any run (docs/router-fasit.local.md, untouched). Every claim below is measured behaviour, not a reading of the source. 1. Bare `<target-path>` inside the step-3 fence is a shell REDIRECTION, not an argument. Measured in zsh: both CLIs failed before starting, no output file was written, and the echoed status was 1 — inside the band the router's own gate calls "continue normally". Quoting makes an unsubstituted placeholder reach argv, so it fails in the CLI where the exit code means something. Swept the whole class: 30 sites across 12 further command files, since a defect in one file is a class until the opposite is measured. New guard: command-placeholder-shell-safety.test.mjs. 2. The orchestrator's exit code was discarded. Two commands on one line share a single trailing `echo $?`, which reports only the last: measured, an orchestrator exit 3 echoed as posture's 0, so the "3 -> stop" gate could never fire. Both statuses are now captured and echoed. 3. "Running 12 configuration scanners" — the orchestrator registers 16. The new test binds the narrated count to the registry so the next scanner added cannot re-stale it silently. 4. The Area Breakdown table hardcoded 7 rows; posture emits 9 quality areas. Token Efficiency (a B on this repo) and Plugin Hygiene never reached the user. Rows added, and the row set is now asserted against lib/scoring.mjs. Label aligned: "MCP Servers" -> "MCP", as posture emits it. 5. Step 6 rendered "the headline line from the humanized stderr scorecard" and forbade deriving a replacement — while step 3 sent posture's stderr to /dev/null, as UX rule 2 requires, and the prose is absent from the JSON payload (measured). The slot could only be improvised. posture's stderr now goes to a file in the session dir, as commands/posture.md already did; the user still never sees raw scanner output. Also: `grep -q -- "--raw"` matched any argument CONTAINING --raw (measured on `--rawdog` and on a path with --raw in it) — anchored to whole arguments. SCOPE_FLAGS renamed SCOPE_FLAG, since zsh does not word-split and the plural invited the M-BUG-45 shape. command-shell-state-shape.test.mjs only recognised line-initial assignments, so it reported the idiomatic `node …; STATUS=$?` capture as never assigned. Widened to assignments after a separator; verified it still fails on a real cross-block reference before trusting it. Suite 1477 -> 1483, frozen v5.0.0 snapshots untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDAwy1ZXRpZxht1wyCeSbF
3.4 KiB
| name | description | argument-hint | allowed-tools | model |
|---|---|---|---|---|
| config-audit:plugin-health | Audit plugin configuration quality — validates structure, frontmatter, and cross-plugin coherence | [plugin-path] | Read, Glob, Grep, Bash | sonnet |
Config-Audit: Plugin Health
Audit Claude Code plugin structure and quality — validates plugin.json, CLAUDE.md, command/agent frontmatter, and detects cross-plugin conflicts.
Arguments
$ARGUMENTSmay contain a path to a specific plugin directory- If omitted: scans all plugins in the marketplace root
--raw: pass-through to the scanner; produces v5.0.0 verbatim envelope (bypasses the humanizer) for byte-stable diff tooling
Implementation
Step 1: Discover plugins and greet
If a specific path is given, scan only that plugin. Otherwise, find all plugins using Glob for **/.claude-plugin/plugin.json.
Tell the user:
## Plugin Health Check
Auditing {N} plugin(s) for structure, frontmatter quality, and cross-plugin conflicts...
Step 2: Run scanner
Run silently for each plugin. Default mode writes a humanized JSON payload to --output-file where each PLH finding carries userImpactCategory, userActionLanguage, and relevanceContext alongside the v5.0.0 fields. --raw is passed through verbatim when present, and prints the byte-stable v5.0.0 envelope on stdout instead.
RAW_FLAG=""
if echo "$ARGUMENTS" | grep -q -- "--raw"; then RAW_FLAG="--raw"; fi
node ${CLAUDE_PLUGIN_ROOT}/scanners/plugin-health-scanner.mjs "<path>" --output-file /tmp/config-audit-plugin-health.json $RAW_FLAG >/dev/null 2>/dev/null; echo $?
Read /tmp/config-audit-plugin-health.json with the Read tool. Exit codes 0, 1 and 2 are normal; only 3 is a real error.
The payload carries three things the report needs:
plugins[]— one row per plugin:name,declaredName,commandCount,agentCount,findingCount,score,grade. Use these for the table; never estimate a grade yourself.cross_plugin_findings[]— the namespace-collision and shared-command-name findings, already separated from the per-plugin ones (they also carrycrossPlugin: trueinfindings).findings[]— every finding, humanized.
Step 3: Present results
### Plugin Health Report
| Plugin | Grade | Commands | Agents | Status |
|--------|-------|----------|--------|--------|
| {plugins[].name} | {plugins[].grade} ({plugins[].score}) | {plugins[].commandCount} | {plugins[].agentCount} | {Good/Issues found} |
| ... | ... | ... | ... | ... |
{If cross-plugin issues:}
#### Cross-Plugin Issues ({count})
| Issue | Plugins | Recommendation |
|-------|---------|----------------|
| ... | ... | ... |
{If findings:}
#### Findings by Plugin
**{plugin-name}** ({finding_count} findings):
1. [{userActionLanguage}] {humanized title} ({id}) — {humanized recommendation}
2. ...
Group findings within each plugin by userImpactCategory (e.g., "Configuration mistake", "Conflict") and lead each line with userActionLanguage ("Fix this now", "Fix soon", "Optional cleanup"). The humanizer already produced the plain-language title/recommendation strings — render them verbatim, do not paraphrase.
Step 4: Suggest next steps
### What's next
- Fix structural issues based on recommendations above
- `/config-audit posture` — Full configuration posture assessment
- `/config-audit fix` — Auto-fix deterministic issues