Fast repo hygiene checks for AI coding agents.
agent-hygiene scans the files that steer coding agents before they steer a
repository: AGENTS.md, CLAUDE.md, Cursor rules, Copilot instructions, MCP
JSON configs, GitHub Copilot hooks, and GitHub Actions workflows that trigger
agentic work.
It is built for one job: make agent-ready repositories safer to trust, easier to audit, and predictable to run in CI.
agent-hygiene is not currently published on PyPI. Install the latest
published GitHub wheel:
python -m pip install \
https://github.com/Yuki9814/agent-hygiene/releases/download/v0.6.0/agent_hygiene-0.6.0-py3-none-any.whl
agent-hygiene scan .Or install and run a source checkout:
git clone https://github.com/Yuki9814/agent-hygiene.git
cd agent-hygiene
python -m pip install .
agent-hygiene scan .
# Without installing:
PYTHONPATH=src python -m agent_hygiene scan .Published wheels and source distributions are listed on the GitHub Releases page.
AI coding agents are becoming normal project infrastructure. The instruction
files and tool configs around them are now part of the supply chain, but most
repos still treat them like notes. A bad AGENTS.md, risky MCP command, or
overpowered issue-comment workflow can silently change how every future agent
run behaves.
agent-hygiene turns those surfaces into a quick health check:
- catches hidden Unicode and prompt-injection phrases in agent instructions
- flags hardcoded credentials and suspicious exfiltration patterns
- reviews MCP server commands for shells, inline code, unpinned
npx, and inline secrets - checks GitHub Copilot hook JSON for structural errors, embedded environment secrets, and outbound HTTP trust boundaries
- highlights risky GitHub Actions triggers such as
pull_request_targetwith broad write permissions - detects stale path references and missing verification commands
- emits text, JSON, Markdown, or SARIF for GitHub code scanning
agent-hygiene scan .
agent-hygiene scan . --format json --output agent-hygiene.json
agent-hygiene scan . --format json --portable \
--source-revision "$(git rev-parse --verify HEAD)" \
--output agent-hygiene.json
agent-hygiene scan . --format sarif --output agent-hygiene.sarif
agent-hygiene scan . --min-score 90 --fail-on high
agent-hygiene baseline . --output .agent-hygiene-baseline.jsonExit codes:
0: score and severity gate passed1: findings exceeded--fail-onor score fell below--min-score2: invalid usage, unreadable output, or an incomplete scan
The scanner fails closed. Symlinked, non-regular, unreadable, or oversized
agent-controlled files make the result incomplete; they never produce a
passing ready status or a successful SARIF invocation. Relevant inputs are
opened with bounded, descriptor-verified reads, so a FIFO, Unix-domain socket,
or final-component file replacement cannot be mistaken for a successfully
scanned regular file. POSIX opens also require nonblocking mode, which keeps a
special-file replacement from hanging the open; non-POSIX platforms retain
the regular-file and identity checks but do not claim the same no-hang
guarantee for platform-specific endpoints. This distinguishes a clean scan
from a scan that could not inspect all relevant inputs.
Repository configuration and baselines are treated as untrusted policy inputs:
they must be bounded regular files, remain inside the scanned repository, and
contain strict JSON. An unsafe configuration exits with code 2; an unsafe
baseline is reported as an incomplete scan and is never used for suppression.
Evaluation and evidence inputs use the same regular-file boundary and return
exit code 2 when that boundary cannot be verified.
name: Agent Hygiene
on:
pull_request:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
# Pin an existing release. Review release notes before upgrading.
- id: hygiene
uses: Yuki9814/agent-hygiene@v0.6.0
with:
min-score: "85"
fail-on: high
sarif: agent-hygiene.sarif
json: agent-hygiene.json
baseline: .agent-hygiene-baseline.json
- uses: github/codeql-action/upload-sarif@4187e74d05793876e9989daffde9c3e66b4acd07 # v3
if: always()
with:
sarif_file: agent-hygiene.sarif
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
if: always()
with:
name: agent-hygiene-json
path: ${{ steps.hygiene.outputs.json }}
if-no-files-found: errorThe optional json input creates a portable report before the final severity
gate runs. It omits the absolute checkout root and records the workflow commit
as a declared source revision. Download that artifact and preview it locally in
PatchHive; no scan content is sent by
PatchHive. The producer and revision fields are declarations, not a signature
or proof of authenticity. See docs/patchhive.md for the
frozen cross-project fixtures and exact handoff contract.
Treat .agent-hygiene.json, baselines, inline suppressions, and the workflow as
policy code: a pull request can propose changes to them. This repository's
CODEOWNERS marks trust-boundary files for maintainer review, but branch rules
must require that review for enforcement.
Agent instruction files:
AGENTS.md,CLAUDE.md,GEMINI.md,CODEX.md.cursorrules,.windsurfrules,.clinerules.cursor/rules/*.mdc.github/copilot-instructions.md.github/instructions/*.instructions.md.github/agents/*.md,.claude/agents/*.md.github/prompts/*.prompt.mdskills/**/SKILL.md,.claude/skills/**/SKILL.md
These repository-scoped surfaces are discovered at the root and inside nested packages, so monorepo-specific agent instructions are not silently skipped.
MCP configs:
.mcp.json,mcp.json.vscode/mcp.json,.cursor/mcp.jsonclaude_desktop_config.json
GitHub Actions:
.github/workflows/*.yml.github/workflows/*.yaml
GitHub Copilot hooks:
.github/hooks/*.json- the top-level
hooksblock in.github/copilot/settings.jsonand.github/copilot/settings.local.json - cross-tool
.claude/settings.jsonand.claude/settings.local.jsonhooks
HIGH AH006 .mcp.json:7
MCP server launches a shell with inline code.
Fix: Replace shell wrappers with a direct executable plus fixed arguments.
agent-hygiene scan [path]
--format text|json|markdown|sarif
--output FILE
--portable
--source-revision HEX
--min-score NUMBER
--fail-on none|low|medium|high|critical
--ignore-rule RULE_ID
--baseline FILE
--no-baseline
--no-color
--quiet
agent-hygiene init [path]
agent-hygiene baseline [path] --output .agent-hygiene-baseline.json
agent-hygiene explain RULE_ID
agent-hygiene evaluate MANIFEST [--format text|json]
agent-hygiene review-pack MANIFEST --output blind-review.json
agent-hygiene evidence DIRECTORY [--format json|markdown] [--output FILE]
agent-hygiene init writes .agent-hygiene.json:
{
"exclude": [
".git",
"node_modules",
"dist",
"build"
],
"ignore": [],
"ignore_rules": [],
"baseline": ".agent-hygiene-baseline.json",
"min_score": 85,
"fail_on": "high"
}ignore accepts glob patterns for paths, path:line, or
RULE_ID:path:line. ignore_rules disables entire rules. For one-off cases,
place agent-hygiene-ignore AH006 on the same line as a finding or
agent-hygiene-ignore-next-line AH006 on the previous line.
Existing projects can adopt the scanner without stopping every pull request on day one:
agent-hygiene baseline . --output .agent-hygiene-baseline.json
agent-hygiene scan . --baseline .agent-hygiene-baseline.json --fail-on high| ID | Rule | Default severity |
|---|---|---|
| AH001 | Hidden Unicode in agent-controlled text | high |
| AH002 | Prompt override or secrecy instruction | high |
| AH003 | Hardcoded credential-like value | critical |
| AH004 | Dangerous shell or destructive command | high |
| AH005 | Network exfiltration pattern | critical |
| AH006 | Risky MCP command shape | high |
| AH007 | Inline secret in MCP environment | critical |
| AH008 | Agentic workflow with broad trust boundary | high |
| AH009 | Missing verification command in instructions | low |
| AH010 | Stale path reference in instructions | low |
| AH011 | Vague instruction block likely to drift | medium |
| AH012 | Duplicate root instruction files can drift | low |
| AH013 | Oversized agent instruction file | medium |
| AH014 | Invalid MCP JSON | medium |
| AH015 | Risky GitHub Copilot hook | medium |
More detail lives in docs/rules.md.
The repository includes a synthetic, versioned corpus with positive and negative fixtures. The evaluator copies each fixture into an isolated temporary repository, compares findings by rule, relative path, and line, then enforces declared precision and recall gates:
PYTHONPATH=src python -m agent_hygiene evaluate tests/corpus/manifest.jsonThe synthetic corpus snapshot has 20 cases and 14 expected findings. It currently reports 14 true positives, 0 false positives, and 0 false negatives against gates of 0.95 precision and 0.90 recall. These are seeded regression fixtures, not a claim about performance on independent real-world repositories. The methodology and manifest contract are documented in docs/evaluation.md.
Version 0.4 adds a separate evidence contract for future read-only canaries against consenting public repositories. Selection manifests, machine observations, human reviews, and adjudications remain separate, and summaries recompute raw counts rather than trusting handwritten totals.
agent-hygiene review-pack tests/corpus/manifest.json \
--output blind-review.json
agent-hygiene evidence evidence/v0.4.0 --format markdownThe review pack uses content-derived ordering and neutral case IDs, and omits
seeded labels, source fixture paths, expected findings, and scanner output.
Canary records require a canonical GitHub repository URL, fixed commit, public
consent link, and non-sensitive study limitations. Stored observations exclude
absolute checkout roots and raw finding evidence. Findings-only review cannot
measure recall, and zero denominators are reported as null; they also block
independent-validation status.
Current status: 0 recorded reviewers, 0 consenting public repositories, not independently validated. The contract and tooling are ready; the external review and canary work in issue #4 is not complete. See docs/evidence.md for the protocol.
Release verification generates an ephemeral 100,000-file repository and enforces p95 scan latency of at most 2.5 seconds and peak RSS of at most 150 MiB:
PYTHONPATH=src python tools/benchmark_large_repo.py --format jsonPull-request CI and release verification run the same 100,000-file gate, and packaging depends on it. The versioned result records the platform, Python version, fixture shape, individual timings, gates, and pass state. Fixture generation, child startup, and imports are excluded from scan latency. See docs/performance.md for the exact method and limitations.
JSON scan output keeps the existing summary and findings fields and adds
schema_version: 1 plus tool name and version metadata. Finding paths remain
repository-relative. When a GitHub repository identity or safe Git origin is
available, JSON and SARIF include the same scope fingerprint so consumers can
reconcile formats across checkout locations without encoding the absolute scan
root. SARIF remains version 2.1.0, retains
agentHygieneFingerprint/v1, adds GitHub code scanning's
primaryLocationLineHash when the bounded scan contains its complete context,
and includes the tool version, severity, and remediation properties. The
location hash keeps alerts stable when unrelated lines are inserted before a
finding and distinguishes repeated matching contexts in one file. See
SARIF interoperability for the exact contract, oversized-file
behavior, and privacy boundary.
Evidence is redacted before output and fingerprinting. Upgrading from an older release can therefore change the fingerprint of a finding whose evidence contained credential-like text; review and regenerate that baseline entry.
- zero runtime dependencies
- useful default checks with low setup friction
- readable output for humans, SARIF for CI
- conservative findings with practical remediation text
- no LLM API required
Compatibility is tested on Python 3.9 through 3.13. Releases follow semantic versioning and publish source and wheel artifacts on GitHub. Security-sensitive reports can be submitted privately using the process in SECURITY.md.
Project ownership and release responsibilities are recorded in MAINTAINERS.md. See CONTRIBUTING.md for rule-change requirements, SUPPORT.md for support boundaries, and ROADMAP.md for the next evidence gates.
MIT