@jqntn/agentdoctor
v0.1.7
Published
Lint and grade your AI coding-agent configuration: permissions, hooks, MCP servers, skills, subagents and CLAUDE.md. Catches silent failures, security holes and context waste, with fixes. Zero dependencies, no telemetry.
Maintainers
Readme
Agent harnesses read a surprising amount of config: permission rules, hooks that execute
automatically, MCP servers that run third-party code, and memory files re-sent on every
request. Almost none of it is validated, and the failures are silent. A misspelled hook
event never fires. A deny rule naming a tool that does not exist blocks nothing — while
looking exactly like a guardrail. Bash(*) in an allow list means every command the model
proposes runs without asking you.
agentdoctor reads that config and tells you what is actually wrong with it:
npx @jqntn/agentdoctoragentdoctor scanned 8 config files in /work/api
.claude/settings.json
4:7 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)".
security/unrestricted-bash
27:43 error PreToolUse hook uses curl | sh; the remote content is executed unreviewed on
every trigger.
| curl -sSL https://example.com/guard.sh | bash
-> Vendor the script into the repo and run it from a pinned path.
security/hook-remote-code
35:21 error "PostToolUsee" is not a hook event. Did you mean "PostToolUse"? As written,
this hook never runs.
correctness/unknown-hook-event
CLAUDE.md
1 warn CLAUDE.md is ~14,200 tokens of always-on context, sent with every request
(~$25/month at 3,300 requests, assuming it stays prompt-cached).
cost/memory-file-too-large
Summary Grade F 3 errors, 1 warning (72 rules, 41ms)Zero dependencies. No network calls. Credential files are never opened. MIT — all of it.
Install
Run it once with no install at all:
npx @jqntn/agentdoctorInstall the CLI:
npm install -g @jqntn/agentdoctor
npm install -D @jqntn/agentdoctorInstall as a Claude Code plugin, which adds the audit skill to every session:
/plugin marketplace add jqntn/agentdoctor
/plugin install agentdoctor@jqntnWhat it checks
Findings fall into five categories. 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, which the test suite asserts against a fixture. False positives are treated as more severe than missed findings, because a linter that cries wolf gets uninstalled and then catches nothing at all.
Every rule states what is wrong, why it matters, and what to change instead, and every rule
has a test. There are 72 today; the current catalogue is always in the
rule reference, or agentdoctor --list-rules.
Security
The config surface is an execution surface. Blanket Bash(*) allows; destructive commands
pre-approved without confirmation (sudo, rm -rf, force push, terraform destroy,
DROP TABLE, …); hooks piping remote content into a shell (curl | sh,
eval "$(curl …)"); live credentials committed in config (Anthropic, OpenAI, GitHub, AWS,
Slack, Stripe keys, JWTs, private keys — always reported redacted); MCP servers running
unpinned packages or carrying tokens in URLs; bypassPermissions committed to shared repos;
loader-hijacking env vars; world-writable config.
Correctness
Config that is silently ignored is worse than config that errors, because you believe it is working. Invalid JSON (which voids the whole file, permission rules included); misspelled settings keys, hook events, tool names, and models — each with a did-you-mean; deny rules that block nothing (an error, because it is a guardrail that only looks like one); malformed hooks and invalid matcher regexes; duplicate agent/skill names; MCP servers with no way to start.
Cost
Memory files and MCP tool schemas ride along on every request. Token counts for every memory file with an estimated monthly cost — the estimate assumes the file stays prompt-cached (that is exactly the content that caches) and states its assumptions; the same instruction duplicated across files; pasted code blocks that belong behind a file path; skill descriptions too vague for the model to ever load them.
Hygiene
settings.local.json not gitignored; machine-specific absolute paths in committed config;
local settings silently shadowing project settings; empty skill/agent bodies; duplicate
keybindings.
Policy
Team standards, enforced mechanically across every repo. Commit an
agentdoctor.policy.json and these activate — no flag, no account:
agentdoctor --init-policyrequiredDeny, forbiddenAllow, allowedMcpServers, requiredHooks, maxMemoryTokens,
forbiddenPermissionModes, plus drift detection when local settings quietly widen the
committed permission set. Policy guide.
Adopt it in one command
npx @jqntn/agentdoctor --init-ci # GitHub Actions: SARIF annotations on PRs + error gate
npx @jqntn/agentdoctor --init-skill # Claude Code skill: findings -> fixes, automatically
npx @jqntn/agentdoctor --init-agents # AGENTS.md section: same loop for Codex, Cursor, Gemini CLI
npx @jqntn/agentdoctor --badge # README badge with your current grade
npx @jqntn/agentdoctor --share # paste-ready score card (rule ids + counts only)Every audit ends in a grade - A+ down to F, formula stated in the docs. The badge and the
share card contain rule ids and counts only, never messages, paths, or snippets, so they are
safe to post from private repos.
CI
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: jqntn/agentdoctor@v0 # SARIF annotations on the PR, exit 1 on errorsFindings annotate the PR diff via SARIF. Adopting on a repo with existing findings? Record them once and fail only on new ones — baselines are anchored to content, not line numbers, so unrelated edits never invalidate them:
agentdoctor --write-baseline .agentdoctor-baseline.json # once
agentdoctor --baseline .agentdoctor-baseline.json # in CIBuilt for agents, audited by agents
Every capability is non-interactive and machine-readable: --json (stable schema, shipped as
JSON Schema), --sarif, --explain, --list-rules --json,
deterministic ordering, redacted secrets safe for model context, pipe-safe output. The docs
site serves llms.txt and raw markdown.
The repo is also a Claude Code plugin: it ships the
config-audit skill (audit -> fix loop, with fix recipes) and
invokable as /agentdoctor:config-audit. Install it any of three ways:
/plugin marketplace add jqntn/agentdoctor
/plugin install agentdoctor@jqntnWhat it will not do
- Never reads credential files.
.credentials.json,.netrc, private keys are excluded by path before anything opens them — asserted by tests. - No network calls. No telemetry, no update checks, nothing leaves the machine.
- No dependencies. A tool that warns about supply-chain risk should not install one.
- Never edits your config. Findings say what to change and why; the change is yours.
Architecture and design principles · FAQ · Security policy
Documentation
| | |
|---|---|
| Getting started | Install, first run, reading findings, exit codes |
| Configuration | Every flag, suppression, disabling rules |
| Rule reference | Every rule, with the reasoning behind it |
| CI setup | GitHub Actions, SARIF, exit-code gating |
| Baselines | Adopting on an existing repo |
| Team policy | One standard across many repos |
| Output formats | JSON, SARIF, terminal |
| Programmatic API | run(), custom rules, embedding |
| For agents | The machine-readable contract + fix loop |
| Architecture | How it works, design principles |
| FAQ | |
Contributing
False positives are treated as more severe than missed findings — if a rule fires on your
legitimate config, that is a bug. Adding a rule
takes one object and two tests: see CONTRIBUTING.md. The suite enforces the
invariants (every rule tested, zero findings on the clean fixture, zero dependencies), so if
npm test is green, the PR is reviewable.
License
MIT. Every rule, every output format, no accounts, no telemetry, no paid tier.
