Operational guide for AI coding agents working in this repository. Project-agnostic dual-memory CLI for Claude Code, Cursor, and OpenCode.
- Language: Python 3.12+
- Build backend: hatchling
- Package manager:
uv - CLI entry:
supamem→src/supamem/cli.py:main - Tests:
pytest(config inpyproject.toml) - Lint/format:
ruff(line length 100, target py312) - External deps: Qdrant 1.10+ (HTTP), MCP 1.13+, fastembed, langchain-text-splitters
src/supamem/
├── cli.py # Typer-based CLI dispatcher
├── console.py # Shared Rich console + theme (single source of branding)
├── config.py # Pydantic config schema (D-38)
├── config_io.py # Discovery: project → user → defaults; merge order locked
├── doctor.py # Health + drift report (Plan 80.6-11)
├── init.py # Greenfield bootstrap (Plan 80.6-08)
├── migrate.py # Brownfield migration paths (Plan 80.6-09)
├── mcp_server.py # MCP server entrypoint
├── embedders/ # minilm, bm25 — pluggable via entry-points
├── indexer/ # chunker (markdown_header), manifest
├── retrieval/ # tuned_hybrid, dense, bm25 backends
├── eval/ # Bench runner + bundled goldens (Plan 80.6-12)
├── hooks/ # claude_code, cursor — per-client snapshot hooks
├── install/ # claude_code, cursor, opencode — config installers
├── share/ # Canonical artifact templates
└── stats/ # Welford counter for usage telemetry
Four plugin groups in pyproject.toml. Third parties add backends without forking:
supamem.retrieval—tuned_hybrid,dense,bm25supamem.embedder—minilm,bm25supamem.chunker—markdown_header,transcriptsupamem.reranker—mxbai_v2(Phase 8)
Order: ./.supamem/config.toml (project) → ~/.config/supamem/config.toml (user) → share/default.toml (shipped). Merge is shallow; project wins.
- NEVER bypass failing tests — fix root cause
- NEVER hardcode collection names; always read from
config.collection - NEVER print to stdout in MCP server — JSON-RPC contract requires stdio purity (use
err_console) - NEVER write to
~/outside~/.cache/supamem/and~/.config/supamem/without explicit user opt-in - NEVER delete a Qdrant collection without
--forceflag confirmation - ALWAYS use
console.pyexports for terminal output (no bareprint()) - ALWAYS run
pytestfrom project root viauv run pytest - ALWAYS use
supamem doctorfor full environment diagnosis; ALWAYS usesupamem repairfor full self-heal. These are the canonical entry points — do not invent ad-hoc cache-clearing or model-redownload flows. (Phase 8 D-FETCH-04)
# Setup
uv sync --extra dev
# Tests
uv run pytest # full suite
uv run pytest tests/test_X.py -v # single file
# Lint / type
uv run ruff check src tests
uv run ruff format src tests
# Build + verify
uv build
uvx twine check dist/*
# Run locally
uv run supamem --help
uv run python -m supamem doctor- Subprocess-based CLI smoke tests (
test_cli_smoke.py) MUST pin a deterministic env:NO_COLOR=1,TERM=dumb,COLUMNS=200— popFORCE_COLOR. Rich autodetect alone is insufficient on CI runners. - Use
pytest-asynciowithmode=Mode.STRICT; mark async tests with@pytest.mark.asyncio. - Mock Qdrant via fixtures, not by hitting a live instance — bench/eval suites are the integration boundary.
- Bump
versioninpyproject.tomland addCHANGELOG.mdentry. - Verify license metadata complies with PEP 639 (
license = "MIT"SPDX, noLicense ::classifier). uv build && uvx twine check dist/*- Create annotated tag:
git tag -a vX.Y.Z -m "..." - Push tag:
git push origin vX.Y.Z— release workflow publishes to PyPI via Trusted Publisher OIDC. - Verify on PyPI:
pip install supamem==X.Y.Zin a clean venv.
PyPI tags are immutable: never re-use a published version number.
supamem install and supamem repair auto-patch ~/.claude/agents/ and <project>/.claude/agents/ so restrictive tools: whitelists (GSD, superpowers, hookify, etc.) include mcp__supamem__* — closes the silent dogfooding gap where subagents could not reach the supamem MCP server. The patcher is idempotent (lenient coverage detection: mcp__*, mcp__supamem__*, or any specific mcp__supamem__<tool> literal counts as covered), preserves user formatting via ruamel.yaml round-trip, and skips symlinks with a warning. Reversible via supamem unpatch-agents.
- Backup manifest:
platformdirs.user_cache_dir("supamem")/agent_patches.json(single rolling JSON, FileLock-protected, atomic temp-and-rename writes, schema_version=1). Per-entry: relative path, original frontmatter SHA-256 (newline-normalized), patched frontmatter SHA, originaltools:value verbatim, timestamp, supamem version. - Opt-out:
--skip-patch-agentsoninstall/init/repair. - Doctor surface:
supamem doctorSubagent reachability panel (read-only; never flips exit code) shows per-agent status grouped by[global]and[project]scope, plus the manifest path +unpatch-agentsreminder when a manifest exists.
NEVER edit agent_patches.json by hand — the CLI is the contract. NEVER auto-restore on pip uninstall: pip / uv / pipx have no portable uninstall hook (verified 2026-05-02), so the user-facing two-step contract is supamem unpatch-agents && pip uninstall supamem, surfaced via supamem doctor.
A daemon thread probes GitHub Releases on every CLI invocation, caches result for 24h in platformdirs.user_cache_dir("supamem")/update_check.json, and prints a stderr footer on the next invocation if a newer version is available. Suppress with SUPAMEM_NO_UPDATE_CHECK=1, CI=1, or NO_UPDATE_NOTIFIER=1. Never blocks; never raises.
The repo ships llms.txt (https://llmstxt.org/) — a curated, LLM-friendly index of the package's docs, CLI surface, MCP tools, and external links. It is the canonical source LLM consumers use to understand supamem WITHOUT crawling the full README.
You MUST update llms.txt whenever ANY of these change:
- README.md content reorganized or new sections added
- A CLI subcommand is added/removed/renamed
- An MCP tool is added/removed/renamed (including aliases)
- A new public env var, config key, or major version is shipped
- An external link reference (PyPI, CHANGELOG, MIGRATION, docs site) moves
- Translations are added or removed (the "## Translations" section)
Treat llms.txt like a public API doc: drift between code and llms.txt is a documentation bug, not "out of date but harmless". Verify after every release with: grep -n "v0\." llms.txt to catch stale version mentions.
When you use GSD (https://github.com/gsd-build/get-shit-done) workflows to plan supamem work, the resulting .planning/, .gsd/, .continue-here.md, HANDOFF-*.md files are LOCAL planning state. They are gitignored and MUST NOT be committed to the package repo — supamem ships as a clean Python package, not as a planning workspace. Same applies to .supamem/ and .claude/insights/_agent/ if a developer runs the tool against the supamem repo itself.
If you need durable design records, use docs/adr/, CHANGELOG.md, or commit messages — these are intended for the public.
The repo ships 5 READMEs: README.md (canonical English) + README.zh-CN.md, README.es.md, README.ja.md, README.ru.md. PyPI renders only the English file (readme = "README.md" in pyproject); translations are GitHub-only.
When you edit README.md, you MUST:
- Apply the same edit to all 4 translations (
README.{zh-CN,es,ja,ru}.md). Code blocks, badges, file paths, and CLI commands stay in their canonical English form — translate only prose, headings, and explanatory text. - Bump the
<!-- synced-with: README.md @ <sha> -->marker (line 2 of each translation) to the new commit SHA after your README commit lands. One-liner:SHA=$(git rev-parse --short HEAD) sed -i "s|synced-with: README.md @ [a-f0-9]\{6,\}|synced-with: README.md @ $SHA|g" \ README.zh-CN.md README.es.md README.ja.md README.ru.md
- NEVER ship a README change without updating the language switcher line at the top — all 5 files share the identical
[English](README.md) · [简体中文](README.zh-CN.md) · ...line as their first line. - If the English README adds a NEW section, translate it. If a section is removed in English, remove from translations too. Drift is the failure mode — keep them in lockstep.
- Translations are AI-assisted (disclosure on line 5 of each file). Native-speaker corrections via PR are encouraged; do NOT silently overwrite human-improved phrasing in subsequent updates — preserve any commit-history evidence of native edits.
CI guard candidate: a script that fails when README.md changes without bumping the synced-with SHA in siblings (deferred — add when drift first bites).
- README: high-level overview, install, quickstart
- MIGRATION.md: version-to-version upgrade notes
- CHANGELOG.md: release log
docs/(if present): ADRs, design docs
- New backend: register via entry-point group, do NOT modify
cli.pydispatch - New CLI subcommand: add Typer command in
cli.py, route to module undersrc/supamem/ - New config field: extend
config.pyPydantic schema + bump default inshare/default.toml - New hook target: add module under
src/supamem/hooks/<client>.py, register incli.py hookdispatcher - Failure in network code: blanket
except Exception: passis correct for non-essential probes (update_check); for indexing/retrieval, surface error to user viaerr_console
@~/.supamem/share/rules/dual-memory.md