Skip to main content

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. stats and check --format json print filenames, rule IDs, and counts — never the file content.
  • Auto-fix writes a .bak next 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)
R012 WARN (opt-in) frontmatter aliases: / triggers: items that reduce to stop-words only (re-introduces the 1-hit pollution R006 removes from name+description); enable via --trigger-stopwords
R013 WARN (opt-in) high-salience emphasis emoji (🔥 🚨 ⚠ 🛑 ❌ ✅ 💥 ❗) over threshold — uniform priority signalling collapses LLM attention weighting; enable via --emphasis-density (threshold via emphasis_max, default 5)
R014 WARN (default ON) frontmatter supersedes: target does not resolve in tree or archive/ — R010 lifted to a structured field; disable via --no-supersedes-check
R015 WARN at 4 KiB / ERROR at 8 KiB (default ON) auto-inject file body exceeds per-turn token budget — detected via frontmatter inject:/auto_load: or filename pattern (MEMORY.md, *-core.md); R003's 25/30 KiB cold-file threshold does not catch per-turn repeating cost; disable via --no-inject-bloat-check
R016 WARN (default ON) INDEX-role file (MEMORY.md, INDEX.md, or frontmatter role: index) has more than 200 lines ([lines]) or body content mixed into what should be an entry-list only ([body-mixed]); disable via --no-index-purity-check
R017 WARN (opt-in) memory body line promises an artifact at an absolute path (~- or /-rooted) that does not exist on disk — operationalises the "implemented-in-prose-only" pattern; enable via --phantom-artifact

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.4.0
    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

The current suite is 95 tests covering parser edge cases (no frontmatter / unterminated frontmatter / inline lists / multi-line lists), per-rule positive and negative cases for R001-R017, the corpus rules R007 and R011, and the CLI front-end including JSON / SARIF reporters.

License

MIT. See LICENSE.

Metadata

Release files for claude-memory-lint 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for claude-memory-lint 0.4.0
File Size Uploaded Provenance Actions
claude_memory_lint-0.4.0.tar.gz 72.7 kB With provenance Details for claude_memory_lint-0.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for claude-memory-lint 0.4.0
File Interpreter ABI Platform Actions
claude_memory_lint-0.4.0-py3-none-any.whl Python 3 none any Details for claude_memory_lint-0.4.0-py3-none-any.whl

Total release size: 112.9 kB

Release files / claude_memory_lint-0.4.0.tar.gz

Download URL claude_memory_lint-0.4.0.tar.gz
Size 72.7 kB
Tags Source
SHA-256 checksum
How to use checksums
c5e56224a634d54d9f074e91aa9344c0563298124258876adbf2d0476eae9677
BLAKE2b-256 checksum
How to use checksums
36b7827ab09cd4b1ed06fb0bb0a04b20d74e1e99cc3508f7373adb05b283e6e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 13, 2026.

Transparency log

Release files / claude_memory_lint-0.4.0-py3-none-any.whl

Download URL claude_memory_lint-0.4.0-py3-none-any.whl
Size 40.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df2dc6782bebc9b7147cef3e9b37ed15e60b58632bb20815f28f41b339b68632
BLAKE2b-256 checksum
How to use checksums
c94c3424e9817e69ced5cc2a8e5c28b505fd1a2b9dbf05a1c68e49f0e31c604d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 13, 2026.

Transparency log

Security for claude-memory-lint 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for claude_memory_lint-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page