config-audit/commands/discover.md
Kjell Tore Guttormsen 0b763f25c1 fix(commands): router dogfood — five seam defects, plus the placeholder class
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
2026-08-09 21:18:05 +02:00

5.5 KiB

name description argument-hint allowed-tools model
config-audit:discover Phase 1 - Initialize session, auto-detect scope, and discover config files [current|repo|home|full] [--delta] Read, Write, Edit, Glob, Grep, Agent, AskUserQuestion, Bash opus

Config-Audit: Discover (Phase 1)

Initialize a new audit session and discover all Claude Code configuration files.

Usage

/config-audit discover              # Auto-detect scope
/config-audit discover current      # Force current directory scope
/config-audit discover repo         # Force git repository scope
/config-audit discover home         # Force home/global scope
/config-audit discover full         # Force full machine scope
/config-audit discover --delta      # Incremental re-scan (changed files only)

Implementation

Step 1: Initialize session and greet

Generate session ID (YYYYMMDD_HHmmss), create directories:

mkdir -p ~/.claude/config-audit/sessions/{session-id}/findings 2>/dev/null

Step 2: Determine scope

If the user provided a scope argument, use it. Otherwise, auto-detect:

  1. Run git rev-parse --show-toplevel 2>/dev/null
  2. If inside a git repo → repo scope
  3. If pwd is $HOMEhome scope
  4. Otherwise → current directory scope

Tell the user:

## Configuration Discovery

**Scope:** {Repository|Home|Current directory|Full machine} — `{path}`
Finding all Claude Code configuration files (CLAUDE.md, settings, hooks, rules, MCP servers)...

Step 3: Resolve paths

Scope What gets scanned
current Current directory + parent CLAUDE.md files up to root + ~/.claude/
repo Git repo root + ~/.claude/
home ~/.claude/ only
full ~/.claude/ (depth 10), managed paths, all dev dirs under $HOME

Step 4: Delta mode (if --delta)

If --delta flag:

  1. Find previous baseline from ~/.claude/config-audit/sessions/*/discovery.json
  2. If no previous: "No previous scan found. Running full discovery instead."
  3. Compare file mtimes/sizes to classify as changed/new/deleted/unchanged
  4. Only scan changed + new files

Step 5: Run discovery

Run the scan orchestrator silently to discover and scan files. Default mode emits humanized JSON — each finding in scan-results.json carries userImpactCategory, userActionLanguage, and relevanceContext alongside the v5.0.0 fields. Pass --raw through if the user requested it (produces v5.0.0 verbatim envelope; humanizer fields absent).

RAW_FLAG=""
if echo "$ARGUMENTS" | grep -q -- "--raw"; then RAW_FLAG="--raw"; fi
# Set to the flag itself for full/home scope, otherwise leave empty. Never pass
# a placeholder wrapped in square brackets: it does not start with a dash, so
# the orchestrator's arg loop takes it as the SCAN TARGET and silently scans a
# path that does not exist.
SCOPE_FLAGS=""   # e.g. SCOPE_FLAGS="--full-machine" or SCOPE_FLAGS="--global"
node ${CLAUDE_PLUGIN_ROOT}/scanners/scan-orchestrator.mjs "<target-path>" --output-file ~/.claude/config-audit/sessions/{session-id}/findings/scan-results.json $SCOPE_FLAGS $RAW_FLAG >/dev/null 2>/dev/null; echo $?

Check exit code: 0/1/2 → normal. 3 → "Discovery encountered an error. Try a narrower scope."

Step 6: Save scope and state

Write scope.yaml and state.yaml to session directory. Update state with all four fields .claude/rules/state-management.md requires: current_phase: "discover", completed_phases: [discover], next_phase: "analyze", and updated_at. The last two are what make an interrupted run resumable.

Step 7: Present summary

Read the scan results file using the Read tool. When you surface initial findings, group them by userImpactCategory and lead each line with userActionLanguage rather than raw severity prefiks — the humanizer already mapped severity to plain-language phrasing ("Fix this now", "Fix soon", "Fix when convenient", "Optional cleanup", "FYI") so the rest of the toolchain sees consistent wording.

Full scan:

### Discovery Complete

**{scope_type}** scope — found {total_files} configuration files:

| Type | Count |
|------|-------|
| CLAUDE.md | {n} |
| Settings | {n} |
| MCP configs | {n} |
| Rules | {n} |
| Hooks | {n} |
| Other | {n} |

Initial scan found {finding_count} items to review (grouped by impact: {comma-separated counts per userImpactCategory}).

**Next:** Run `/config-audit analyze` to generate your analysis report.

Delta scan:

### Delta Discovery Complete

Compared against baseline from {previous-session-id}:

| Status | Files |
|--------|-------|
| Changed | {n} |
| New | {n} |
| Deleted | {n} |
| Unchanged | {n} |

Only {changed+new} file(s) scanned (vs {total} full scan).

**Next:** Run `/config-audit analyze` to generate your analysis report.

Config File Patterns

Pattern Description
**/CLAUDE.md Project instructions
**/CLAUDE.local.md Local overrides
**/.claude/settings.json Project settings
**/.mcp.json MCP servers
**/.claude/rules/*.md Modular rules

For global: ~/.claude/CLAUDE.md, ~/.claude/settings.json, ~/.claude.json, ~/.claude/agents/*.md

Error Handling

  • If scanner fails, report to user in plain language and suggest narrower scope
  • If path doesn't exist, tell user and suggest alternatives
  • If git command fails for repo scope, silently fall back to current
  • If no config files found, explain: "No Claude Code configuration files found. Start with /config-audit feature-gap to see what's recommended."