agentdoctor lints AI coding-agent configuration — permissions, hooks, MCP servers, skills, memory files — and reports what is broken, dangerous, or expensive, with line numbers and fixes. Zero dependencies. No telemetry. MIT.
agentdoctor scanned 1 config file in /work/api
.claude/settings.json
3:15 error "Bash(*)" auto-approves every shell command, including ones you have not seen.
| Bash(*)
-> Replace the wildcard with the specific commands you actually want
unattended, e.g. "Bash(npm test:*)" or "Bash(git status)". A blanket allow
means any command the model proposes runs without asking you.
security/unrestricted-bash
4:14 error "bash(curl:*)" targets unknown tool "bash". Did you mean "Bash"? This deny rule currently blocks nothing.
| bash(curl:*)
-> Tool names are case-sensitive. A rule naming a tool that does not exist
never matches anything, so a deny rule written this way protects nothing.
correctness/permission-unknown-tool
7:21 error "PostToolUsee" is not a hook event. Did you mean "PostToolUse"? As written, this hook never runs.
-> Valid events: PreToolUse, PostToolUse, UserPromptSubmit, Notification, Stop,
SubagentStop, PreCompact, SessionStart, SessionEnd. Events are
case-sensitive and a misspelled one never fires.
correctness/unknown-hook-event
1 info No deny rules cover .env, SSH keys or cloud credentials.
-> Add a deny list such as ["Read(./.env*)", "Read(**/.ssh/**)",
"Read(**/*.pem)", "Read(**/.aws/credentials)"]. Deny rules are the only
guardrail that survives an accepted prompt, since they are checked before
anything runs.
security/missing-secret-denies
2:18 info This project defines deny rules, but any contributor can still start a session in bypassPermissions mode and skip them.
-> Set permissions.disableBypassPermissionsMode to "disable" in committed
project settings to stop anyone opting out of prompts in this repo.
security/bypass-mode-not-locked
Summary Grade F 3 errors, 2 info (72 rules, 11ms)
Share the grade with `agentdoctor --share`, or gate it in CI with `--init-ci`.
"SubAgentStop"One capital letter off. Valid JSON. The hook never fires, and nothing tells you.
"bash(curl:*)"A deny rule naming a tool that doesn’t exist blocks nothing — while looking exactly like a guardrail.
"Bash(*)"In an allow list, every command the model proposes runs without asking you. Including the ones you haven’t seen.
The bar for a rule is that it catches a failure that actually happens and stays quiet on legitimate config — a correctly configured project reports nothing, asserted by a fixture in the test suite. False positives are treated as more severe than missed findings, because a linter that cries wolf gets uninstalled and then catches nothing at all. 72 rules today, each with a test.
Pre-approved destructive commands, curl | sh hooks, committed credentials (always redacted), unpinned MCP packages, loader-hijacking env vars, world-writable config.
Invalid JSON that silently voids permission rules, misspelled keys and hook events with did-you-mean, dead deny rules, malformed hooks, duplicate agents.
Memory files priced in tokens and dollars — with stated caching assumptions — duplicated instructions, pasted code blocks, vague skill descriptions.
Ungitignored local settings, machine-specific paths in committed config, shadowed settings, empty skills.
Commit one agentdoctor.policy.json and every repo is held to it: required denies, forbidden allows, approved MCP servers, context budgets.
SARIF annotations on the PR diff, exit-code gating, and content-anchored baselines that survive unrelated edits.
Deterministic, non-interactive, machine-readable end to end: --json with a
shipped JSON Schema, --explain for every
rule, secrets redacted in every format so findings are safe in model context. This site serves
llms.txt and every docs page as raw markdown. A ready-made
Claude Code skill turns findings into fixes.
# the agentic fix loop
agentdoctor . --no-user --json # findings, sorted most-severe-first
# edit finding.file at finding.line (configPath names the exact key)
agentdoctor . --no-user --quiet # exit 0 = verified fixed
A+ zero findings · A info only · B/C warnings · D/F errors.
The formula fits in a sentence, because a grade nobody can explain is a grade nobody trusts.
--share prints a paste-ready score card and --badge a README badge —
both carry rule ids and counts only, never messages or paths, so they are safe to post from private repos.
npx @jqntn/agentdoctor --init-ci # gate every PR: SARIF annotations + exit code
npx @jqntn/agentdoctor --init-skill # let Claude Code audit and fix config itself
npx @jqntn/agentdoctor --badge # show your grade in the README
npx @jqntn/agentdoctor # audit this project + ~/.claude
npx @jqntn/agentdoctor --only security # one category
npx @jqntn/agentdoctor --explain security/hook-remote-code
npx @jqntn/agentdoctor --init-policy # start a team standard