melodic-software

claude-code-plugins

"Audit Claude Code configuration files — settings.json, settings.local.json, .mcp.json, hooks, plugins, permissions, environment variables — for correctness, security, and drift against current official docs. Use when: 'audit settings', 'check config', 'check for config drift', after a Claude Code update, or when permissions, hooks, plugins, or MCP servers may be misconfigured; pass --fix to apply auto-correctable findings with confirmation."

melodic-software 12 Updated 3w ago

Resources

5
GitHub

Install

npx skillscat add melodic-software/claude-code-plugins/plugins-claude-config-skills-audit

Install via the SkillsCat registry.

SKILL.md

Pre-computed context

Claude Code version: !claude --version 2>/dev/null || echo "unknown"

Purpose

Periodic audit of Claude Code configuration files against current official documentation and the
consuming project's own conventions. Answers: "Are our settings correct, secure, complete, and up to
date?"

Adapting to your environment (graceful degrade)

This skill is self-contained. Where a phase names an adjacent capability — a Claude Code issue-tracking
skill, an environment-bootstrap checker — treat it as optional: if your setup provides an equivalent
skill or tool, use it; otherwise follow the inline guidance here, which stands on its own.
Project-specific conventions (required permission patterns beyond the baseline, documented reasons for
disabled servers, launcher-script wrappers) come from the consuming repo's own CLAUDE.md and
.claude/rules/ — read them when present; this skill does not assume them.

Two adjacent skills cover neighboring questions: the sibling audit-automation-gaps skill asks whether the
configured automation SET is the right set (landscape gaps); the audit skill in the claude-memory
plugin audits the instruction layer (CLAUDE.md / rules / auto-memory). This skill asks whether the
configuration FILES are correct against upstream truth.

Arguments

Parse $ARGUMENTS for:

  • --fix: Apply corrections automatically (with user confirmation per fix). Without this flag, report-only mode
  • Scope filter: Limit audit to a single category. If omitted, run all categories
    • permissions — deny/ask/allow rules, security gaps
    • mcp — MCP server definitions, commands, env vars, connectivity
    • hooks — hook scripts exist, timeouts, matchers
    • plugins — enabled/disabled status, marketplace availability
    • issues — recheck known GitHub issues only
    • all — run everything (default)

Config Files

File How to read Notes
.claude/settings.json Read tool or jq Project-level, checked in
.claude/settings.local.json jq via Bash only Commonly deny-listed for the Read tool because it holds tokens. Parse structure/key counts only. Never echo secret values
.mcp.json Read tool or jq Project-level MCP server definitions
~/.claude/settings.json Read tool User-level defaults (optional — check if exists)
start-directory .claude/settings.local.json check-structure.sh (structure only) Only when the session's start directory is not the repository root AND a copy is there. A pre-v2.1.211 Claude Code wrote the file to the start directory and the current one still reads what it left; the repository-root copy wins on a shared key, but permission rules from both files stay in effect
managed-settings.json + managed-settings.d/ check-structure.sh (structure only) Machine-scope managed policy, the highest-precedence layer. OS-specific path resolved by the script (macOS /Library/Application Support/ClaudeCode/, Linux/WSL /etc/claude-code/, Windows %ProgramFiles%\ClaudeCode\). Findings on it are report-only routing — managed policy is the administrator's, never edited by --fix
managed policy outside the filesystem not read The Windows HKLM/HKCU\SOFTWARE\Policies\ClaudeCode policy keys and the macOS com.anthropic.claudecode managed-preferences domain. check-structure.sh names them so an absent managed-settings.json is never read as "no managed policy deployed", but it does not read them

Reading settings.local.json safely

Even when no deny rule blocks it, treat settings.local.json as secret-bearing: run
bash "${CLAUDE_PLUGIN_ROOT}/skills/audit/scripts/check-structure.sh" for key counts and
validity, never dump its contents. Supplemental jq recipes: context/procedures.md
"Reading settings.local.json safely".

The counts are not guaranteed. Where the project's configuration blocks the read — a sandbox
denyRead merged from the baseline Read deny, or filesystem permissions — the script reports
Readable: no and a not inspectable note instead of failing. Record the file as not inspectable,
carry that into the report, and do not reach for another reader to get the counts anyway.


Track progress

For any full audit run (Phases 1-5), keep a phase checklist and tick each phase as it completes —
either in-response or by copying ${CLAUDE_PLUGIN_ROOT}/skills/audit/templates/checklist.md
into wherever the consuming repo keeps working task notes. Phase 5 is SKIPPED in default report-only
mode.


Phase 1: Load & Parse

Read all config files and validate basic structure.

Record the installed Claude Code version (claude --version) — Phase 3.2 compares issue-fix versions
against it. Then run bash "${CLAUDE_PLUGIN_ROOT}/skills/audit/scripts/check-structure.sh"
before the table below.

1.0 Hook inventory

Run bash "${CLAUDE_PLUGIN_ROOT}/skills/audit/scripts/check-hook-coverage.sh" and keep its output for
the rest of the run. It enumerates settings-declared hooks and the hooks shipped by every enabled
plugin — resolved through the installed-plugin registry, so no version-directory guessing is
involved — plus the levers (disableAllHooks, allowManagedHooksOnly,
strictPluginOnlyCustomization) that switch hooks off wholesale.

Two categories depend on it and neither could take this inventory before:

  • Category D writes rules for ${CLAUDE_PLUGIN_ROOT} / ${CLAUDE_PLUGIN_DATA} placeholders, which
    only ever appear in a plugin-provided hook.
  • Category B's third narrowing demotes a missing baseline deny to info when a live PreToolUse
    hook already blocks the family. That narrowing is only takeable against a real inventory.

Read the exit code, not just the table. 0 means the inventory is complete. 1 means it is
partial — some enabled plugin did not resolve, or a hook config did not parse — and the "Not
enumerated" block names which. For anything a partial inventory could not see, keep the conditional
posture in required-permissions.md "Fail open where the inventory
is incomplete". 2 is fatal (no readable settings scope, or jq missing); treat it as no inventory at
all rather than as an empty one.

The script never runs a hook and never decides whether a hook covers a family — that judgment stays
with Category B, against the three preconditions in "Narrowing the baseline".

1.1 JSON validity

Valid JSON: no blocks further analysis.

1.2 Structure inventory

Map script output into the summary table:

File Valid JSON Keys Deny Ask Allow Hooks MCP Servers Plugins

1.3 Baseline snapshot

Record script counts for before/after comparison if --fix is used:

  • Permission rule counts (deny/ask/allow)
  • MCP server count (active vs disabled)
  • Hook count by event type
  • Plugin count (enabled vs disabled)

Phase 2: Validate

Load the audit checklist: audit-checklist.md

Run each category's checks. Record findings with severity ratings.

Nine categories — names + the question each answers below; full per-check criteria in
context/validation-categories.md
(read it when running Phase 2).

  • A — Schema & Structure: $schema present, no unknown keys, mcpServers not in settings.local.json
  • B — Permissions: baseline deny/ask patterns present (list in reference/required-permissions.md, plus any additional patterns the consuming repo's own rules declare as required), deny-rules-in-settings.json-only (bug #8961)
  • C — MCP Servers: commands resolve, ${VAR} syntax, disabledMcpjsonServers match, documented disable reasons
  • D — Hooks: paths resolve + readable, timeout in seconds and sane, valid matchers, quoted path placeholders in shell form, exec-form command resolvable on Windows, no duplicates, valid events
  • E — Plugins: static checks (marketplace membership) + live upstream drift detection (scripts/check-plugin-drift.sh — ORPHAN/NEW/RENAME modes, auto-fix policy table in the context file)
  • F — Environment Variables: documented/justified vars, secrets in settings.local.json only, forward-slash paths
  • G — Skill-listing budget: overflow check by a route this run can actually take (/doctor interactively, --debug headless), the budget constant it was measured against, and trim levers scoped to the roster's composition (skillOverrides reaches project and user skills; plugin skills are managed through /plugin)
  • H — Model and effort settings: effortLevel, fallbackModel, availableModels, enforceAvailableModels — values the harness accepts into the file but does not apply as written
  • I — Deep-link registration: disableDeepLinkRegistration — the one documented value that takes effect, and a visible attempt at an enforcement requirement lodged in a scope that cannot enforce it

Phase 3: Research & Recheck

External verification against current documentation.

Read every page in this phase verbatim, not through a summarizer. These pages are long — settings
and env-vars are hundreds of KB — and a summarizing fetch truncates, then reports the rows past the
cutoff as absent. That false negative has already been observed on the settings page: three keys
reported NOT FOUND that raw curl + grep found. So for each fetch below,
curl https://code.claude.com/docs/en/<page>.md to a file and grep the file, per the
fetch route.
A truncated read supports NO finding — say so and move on, in either direction: neither "the key is
gone" nor "the key is unchanged" is reportable from a read that may have been cut.

3.1 Official docs check

Fetch code.claude.com/docs/en/settings and compare:

  • Are there new settings the project should consider adopting?
  • Have any settings in use been deprecated or renamed?
  • Has permission rule syntax changed?

3.2 GitHub issue recheck

For each issue in known-issues.md, check live status
(gh issue view <number> -R anthropics/claude-code --json state,title — or the consuming repo's own
Claude Code issue-tracking skill if it has one). For any issue whose upstream fix has shipped at or
below the installed Claude Code version, confirm the settings-specific workaround is still needed and
recommend retiring it if not.

3.3 Model configuration verification

MANDATORY for any category H finding: fetch
code.claude.com/docs/en/model-config and confirm the
behavior the finding rests on. Do NOT report a category H finding from this checklist's wording
alone — the accepted effortLevel values, the fallback-chain cap, and the allowlist wildcard rule
are all upstream-owned and move with the harness.

3.4 Permission syntax verification

Fetch code.claude.com/docs/en/permissions and verify:

  • The project's permission patterns match current documented syntax
  • No new permission types added (e.g., new tool types)
  • Wildcard behavior unchanged

Phase 4: Report

Present all findings as a severity-rated GFM table:

# Category Severity Finding Current Recommended
1 B — Permissions error Deny rule placed in settings.local.json, where bug #8961 leaves it inert settings.local.jsonpermissions.deny: ["Bash(rm -rf:*)"] Move the rule into settings.json; keep settings.local.json deny empty

Severity guide

Severity Criteria
error Security gap, broken config, or enforcement bypass
warning Deprecated syntax, missing best practice, stale issue status
info Enhancement opportunity, new feature available

Interactive checkpoint

If --fix flag is set: Present findings table, then ask user which items to fix. Do NOT proceed without their response.

If report-only (no --fix): Present findings table and summary. Note which items could be auto-fixed.

"Which findings would you like me to fix? Reply with numbers, 'all', or 'skip'."


Phase 5: Fix (only with --fix flag)

For each user-approved fix:

  1. Make the edit
  2. Validate JSON with jq after each edit
  3. Report what changed

After all fixes:

  • Present before/after comparison (rule counts, server counts, etc.)
  • Verify all config files are still valid JSON

Fixes the skill can apply

Auto-fixable (add $schema, move deny rules from local to project, plugin orphan-removal +
new-as-false via scripts/fix-plugin-drift.sh --yes) vs judgment-required (adding a baseline deny
rule, new settings from docs, permission restructure, MCP config, orphan-true removal, heuristic
rename) — full matrix in context/procedures.md "Phase 5 — fixes the skill can
apply". Adding and moving a deny rule are graded apart on purpose: moving one is #8961 placement, while
adding one has to be checked against hook coverage that may already hold and against an ask-gate the
addition would suppress.


Required permission patterns

Category B (Phase 2) checks that the project's .claude/settings.json contains a security baseline of
deny/ask patterns: secret-file Read denies, destructive-git Bash denies, and a git push ask-gate. The
concrete list is in reference/required-permissions.md. Projects
with a stricter posture declare their additional required patterns in their own rules files — when the
consuming repo documents such a list, include it in the Category B check.

Report the secret-file Read denies with their scope, not as protection: a Read(...) deny covers the
built-in file tools and the Bash file commands Claude Code recognizes, but not a subprocess that opens
the path itself. "Scope of a Read deny" in
reference/required-permissions.md carries the covered /
not-covered split, the ranked remedies, and the platform limit — carry it into the finding rather than
implying the file is unreachable.

CC settings schema, MCP server shape, hook event names, and permission glob syntax are upstream
invariants resolved against their own official pages when a check needs them, rather than asserted
as fixed patterns here.

Post-Audit

Consumer workaround inventory

When a settings-affecting upstream issue drives a project-specific workaround (or one becomes
obsolete), record that in the consuming repo's own conventions/rules files — the plugin's bundled
known-issues.md tracks only broadly-applicable upstream issues and is
refreshed via plugin updates, not per-consumer edits.

Suggest frequency

Based on findings count:

  • 0 findings: "Config is clean. Recheck after next Claude Code major update."
  • 1-3 findings: "Minor drift. Monthly recheck recommended."
  • 4+ findings: "Significant drift detected. Consider running after every CC update."