A lint for Claude Code memory directories — catches frontmatter rot, oversized files, and stop-word noise before it pollutes Claude's context.
Project description
claude-memory-lint
Disclaimer: This is an independent third-party tool. It is not affiliated with, endorsed by, or sponsored by Anthropic. "Claude" and "Claude Code" are trademarks of Anthropic and are used here nominatively to identify the official CLI/product this tool integrates with.
A static lint for Claude Code memory directories. Catches the file-quality problems that lead to context pollution before they reach the prompt — stop generic-keyword noise at the source instead of chasing it at runtime.
cml check ~/.claude/projects/<id>/memory/ # exits 1 on ERROR
cml fix ~/.claude/projects/<id>/memory/ # auto-add missing aliases
cml stats ~/.claude/projects/<id>/memory/ # counts only, no contents
Why
Claude Code memory directories grow quickly. Once frontmatter is
inconsistent and a meaningful fraction of files only carry generic
keywords (api, github, claude, task, agent, …), runtime
routing degrades into "every common word matches every file": context
gets stuffed with unrelated memory and the model's attention drifts.
Runtime routers can patch the symptom. The cause is on the write
side: missing frontmatter, empty aliases:, oversized files, stale
copies. claude-memory-lint raises those defects to ERROR / WARN /
INFO at write-time so they never reach the auto-load path.
Privacy posture
This is the privacy-conscious choice in the memory-tooling space:
- No LLM calls. Period. Heuristics only.
- No file body in stdout.
statsandcheck --format jsonprint filenames, rule IDs, and counts — never the file content. - Auto-fix writes a
.baknext to the original and never sends anything off-machine. - A separate hook (
claude-memory-router) handles runtime routing with the same posture; this lint is its compile-time companion.
Install
pip install claude-memory-lint
# or for development:
pip install -e .[dev]
Python 3.10+. No required runtime dependencies.
Rules
| ID | Severity | What it catches |
|---|---|---|
| R001 | ERROR | frontmatter missing or unterminated |
| R002 | ERROR | both aliases: and triggers: are empty / absent |
| R003 | WARN at 25 KiB / ERROR at 30 KiB | file size threshold (lowered from 50 KiB in v0.1.2) |
| R004 | WARN | filename stem is not kebab-case ASCII |
| R005 | INFO | inbound: file is not referenced by any other memory file (orphan) |
| R006 | WARN | stop-word density in name + description ≥ 40 % |
| R007 | INFO | duplicate normalised stem (likely stale copy) |
| R008 | INFO | mtime older than 180 days |
| R009 | ERROR | secret literal pattern (GitHub PAT / AWS / Anthropic / OpenAI / Google / Slack / Stripe) — match is never echoed in the lint output |
| R010 | WARN (opt-in) | outbound: dangling markdown link ](*.md) — target not found in tree or archive/ (enable via --dangling-links). Complementary to R005: R005 catches a file no one points at; R010 catches a link that points at nothing |
| R011 | WARN (opt-in) | stale backup file *.bak / .backup / .orig / trailing ~ older than 7 days in the active directory (enable via --stale-backup) |
Thresholds are tunable in code; runtime config file support is on the roadmap.
R009 (added in v0.2.0) is the response to a real-world incident where a
GitHub PAT literal sat in a memory file for days under .gitignore
"protection" while still being one filesystem-read away from the LLM
context. The rule deliberately reports only the pattern type and
the line — the matched substring is never written to stdout, JSON,
SARIF, or any error path — because a lint that leaks the secret it
caught is worse than no lint.
Use as a pre-commit hook
Add to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/hinanohart/claude-memory-lint
rev: v0.2.1
hooks:
- id: claude-memory-lint
args: [check, --rule, R001, --rule, R002]
This blocks commits that introduce a memory file without proper frontmatter or aliases.
Use in CI
- name: lint memory directory
run: |
pip install claude-memory-lint
cml check --format sarif path/to/memory > cml.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: cml.sarif
Companion: claude-memory-router
This lint pairs with claude-memory-router:
- The lint enforces write-time quality (aliases, size, freshness).
- The router consumes that quality at read-time and only injects the most relevant memory files into the prompt.
Garbage-in, garbage-out is real for routers as for any other engine. The lint exists so the router can do its best work.
Output formats
text default — human-readable, one finding per line + summary
json machine-readable; convenient for CI gates
sarif SARIF 2.1.0 — uploadable to GitHub code scanning
Testing
pip install -e .[dev]
pytest -v
Aim for 45+ passing tests covering parser edge cases (no
frontmatter / unterminated frontmatter / inline lists / multi-line
lists), per-rule positive and negative cases, the corpus rule, and the
CLI front-end including JSON / SARIF reporters.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_memory_lint-0.2.1.tar.gz.
File metadata
- Download URL: claude_memory_lint-0.2.1.tar.gz
- Upload date:
- Size: 51.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f4b9c8c268162ef521341877ed87f7106e6616dd4a44b6506bdd4f5ca28b296
|
|
| MD5 |
117d8e7f1daf080aeb0aad96cc159818
|
|
| BLAKE2b-256 |
a4baefdf3a04d52e9343a7c09a9fa4320978c506ac95fcd650f87c09b2c59539
|
Provenance
The following attestation bundles were made for claude_memory_lint-0.2.1.tar.gz:
Publisher:
publish.yml on hinanohart/claude-memory-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_memory_lint-0.2.1.tar.gz -
Subject digest:
8f4b9c8c268162ef521341877ed87f7106e6616dd4a44b6506bdd4f5ca28b296 - Sigstore transparency entry: 1484854021
- Sigstore integration time:
-
Permalink:
hinanohart/claude-memory-lint@59d398dc730a2987a60f33c50fd53d7b6e992534 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hinanohart
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@59d398dc730a2987a60f33c50fd53d7b6e992534 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file claude_memory_lint-0.2.1-py3-none-any.whl.
File metadata
- Download URL: claude_memory_lint-0.2.1-py3-none-any.whl
- Upload date:
- Size: 26.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68e522773f49a2263c632b36810835c9b20a9f60980786ca7d070f9a043efd1f
|
|
| MD5 |
ab9f0c4b8ad5ca6d77e4d3b5e7dbf44b
|
|
| BLAKE2b-256 |
a3b77846a1dc6f81020c0ed901e463b2f795aeded7ca91b193268085173ed172
|
Provenance
The following attestation bundles were made for claude_memory_lint-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on hinanohart/claude-memory-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_memory_lint-0.2.1-py3-none-any.whl -
Subject digest:
68e522773f49a2263c632b36810835c9b20a9f60980786ca7d070f9a043efd1f - Sigstore transparency entry: 1484854042
- Sigstore integration time:
-
Permalink:
hinanohart/claude-memory-lint@59d398dc730a2987a60f33c50fd53d7b6e992534 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hinanohart
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@59d398dc730a2987a60f33c50fd53d7b6e992534 -
Trigger Event:
workflow_dispatch
-
Statement type: