Imported from DANgerous25/callmem (
AGENTS.md). Install upstream withnpx skills add DANgerous25/callmem. Copyright stays with the author.
AGENTS.md — callmem Development Norms
You are working on callmem, a persistent memory system for coding agents. Read this file completely before starting any task.
Quick Commands
When the user says one of these phrases, execute the corresponding action:
| Phrase | Action |
|---|---|
| catch up | Run callmem briefing (or call mem_get_briefing via MCP). Summarize the current project state and ask what to work on. |
| wrap up | Run tests, commit, and push. |
| status | Call mem_get_tasks and report open items. |
| dfn (done for now) | Run tests, commit, and push. |
| dfd (done for day) | Run tests, commit, and push. Assume the next reader is a fresh agent tomorrow — spell out specific next steps in the commit message body. |
| do | Read the relevant task document, implement everything specified. Run tests after each change. Commit after each file. |
Memory is automatic
callmem captures every Claude Code / OpenCode event and extracts entities (decisions, todos, facts, features, bugfixes, discoveries, failures) into .callmem/memory.db. The briefing is generated live by callmem briefing (CLI) or mem_get_briefing (MCP), both reading the same DB — there is no flat SESSION_SUMMARY.md to keep in sync.
No flat memory files. Don't write to .callmem/SESSION.md, TODO.md, or DECISIONS.md — those were removed. The DB is the source of truth; the briefing reads from it.
Still ingest explicitly when:
- You make a design decision the extractor might miss —
mem_ingesttype"decision" - You want to record a TODO immediately —
mem_ingesttype"todo"withpriority - Something surprising happens —
mem_ingesttype"discovery"or"failure" - You need to recall past work —
mem_search query=...ormem_get_entities ids=[...](short IDs from the briefing work, e.g.#F5AVDQ25) - Before re-reading a file you've worked on before — call
mem_file_context path=<file>first. If the returned timeline covers what you need, skip the raw read (typical saving: ~95% of tokens). - Long sessions (50+ messages) — every ~30 messages, call
mem_check_context message_count=<n>(passestimated_tokenstoo if you can). If it returnscompress_recommended, summarize the oldest ~30 messages (preserve decisions, TODOs, and failures verbatim) and callmem_compress_context summary=<text> message_range="messages 1-30". Replace the compressed span in your context with the marker it returns, and usemem_searchto recall specifics later.
Raw events are captured automatically — focus ingest calls on decisions/todos, not transcripts.
Consult memory before disk
If callmem is registered for this project (an .mcp.json referencing callmem is present), it is cheaper to ask memory than to re-read files or re-ask the user:
- At the start of every non-trivial task, call
mem_get_briefingonce. The briefing surfaces recent decisions, open TODOs, unresolved failures, and recent session hooks. It costs ~2k tokens once and routinely saves you re-reading 5–20 files. - Before reading a file you suspect was touched before, call
mem_file_context path=<file>. If the timeline covers what you need, skip the raw read. - Before asking the user a clarifying question, call
mem_search query=<topic>. There's a good chance an earlier session already decided it.
This is a real cost discipline. The callmem usage command reports per-session "memory used" rate. The goal is to get that rate well above the current ~10% baseline.
Cite entity IDs when memory informs your work
When a memory entity, briefing item, or mem_search result shapes what you do or say, cite the 8-character short ID in your response the same way you'd cite a line number. The format is #XXXXXXXX — the last 8 characters of the entity's ULID, exactly as the briefing prints them.
Examples:
- "Per
#R7BAFG3Y, this was the uv shim issue — the fix is to reinstall the global tool." - "There's an unresolved failure on this (
#FPCHSXRM), so I'll start by reproducing it before changing anything." - "
#NBPRHE0Yalready tracks the version-string sync TODO — I'll close that one out as part of this commit."
Why this matters:
- The
callmem usageanalytics counts citation matches against real entity IDs in the DB. A citation is the only countable proof that memory informed a response — without citations, memory looks unused even when it's read. - A reader (you in two weeks, the user, the next agent) can pull up any cited entity in a second to see the full context.
- It keeps you honest: if you cite, you read; if you didn't read it, don't cite it.
Cite freely but only when accurate. Do not invent IDs. Do not cite IDs you have not actually seen in a briefing, search result, or mem_get_entities call.
Git Discipline
Every meaningful change gets a commit
- Commit after completing a logical unit of work (a function, a test passing, a bug fix)
- Do not accumulate hours of work in uncommitted changes
- Prefer many small commits over one giant commit
Commit messages
- Use conventional commit format:
feat:,fix:,refactor:,test:,docs:,chore: - Be specific:
feat: implement FTS5 search in retrieval enginenotfeat: add search - Be specific:
feat: implement event ingest pipelinenotfeat: add things - Never exceed 72 characters in the subject line
- Use the body for detail when needed
Push frequently
- Push after every commit, or at minimum after every completed task
- Never leave a session with unpushed commits
- If you're about to end a session, push first
Branching
- Work on
mainfor now (single developer, iterative build) - If a task is large, use a branch:
feat/core-engine - Always push branches
Code Quality Norms
Never include AI attribution
- No "Generated by", "Created by AI", "Co-authored-by: AI", "Written with assistance from" or similar
- No tool names in comments (no "Claude", "GPT", "GLM", "Copilot", "OpenCode")
- No "As an AI" or "I" in code comments
- Write code as if a human wrote it — because you are the developer
Comments and docstrings
- Write docstrings for all public functions and classes
- Comments explain why, not what — the code shows what
- Do not write comments that restate the code:
# increment counterabovecounter += 1 - Do not write TODO comments that are vague:
# TODO: fix this later— say what and why
Type hints
- All function signatures must have type hints (parameters and return)
- Use
from __future__ import annotationsat the top of every module - Use modern syntax:
str | NonenotOptional[str],list[str]notList[str]
Error handling
- Catch specific exceptions, never bare
except: - Log errors with enough context to debug (include the input that caused the error)
- Fail loudly in development, degrade gracefully in production
Testing
- Write tests alongside implementation, not as an afterthought
- Run the full test suite before committing:
pytest tests/ -v - If a test fails, fix it before moving on — do not commit failing tests
- Test the happy path, at least one edge case, and at least one error case per function
Imports
- Use absolute imports:
from callmem.core.database import Database - Sort imports with
ruff(isort-compatible) - No wildcard imports: never
from module import *
Code Style
Follow existing patterns
- Read existing code before writing new code
- Match the style and structure of adjacent modules
- If
database.pyuses context managers, your module should too - If
models/events.pyusesto_row()/from_row(), yours should too
Keep functions short
- If a function exceeds 30 lines, consider splitting it
- If a function does two distinct things, make it two functions
- Prefer composition over inheritance
SQL
- All queries use parameterized placeholders (
?), never string formatting - SQL lives in
repository.py, not in engine or handler code - Use the
Database.connect()context manager for all database access
Session Workflow
Starting a task
- Run
callmem briefingor callmem_get_briefingvia MCP - Read the relevant task specification (if applicable)
- Read the files you'll modify to understand current state
- Plan before coding — understand the interfaces you need to match
During a task
- Commit after each meaningful step
mem_ingestdecisions, TODOs, discoveries, and failures as they arise- If you're stuck, describe the problem clearly before trying fixes
Ending a task
- Run
pytest tests/ -v— all tests must pass - Run
ruff check src/ tests/— no lint errors - Commit and push all changes
Sensitive Data Protection
All ingested content passes through a two-layer detection pipeline inline at ingest (not async):
- Pattern matching — Regex patterns catch API keys, passwords, tokens, credit card numbers, etc. Fast, zero-cost, runs first.
- Local LLM classification — If pattern matching flags nothing, a local Ollama model (same one used for maintenance) does a quick sensitivity check. Since this is your own machine, there is zero privacy concern feeding raw content to it.
When sensitive content is detected:
- The raw value is encrypted (Fernet symmetric encryption) and stored in the vault
- The memory stores a redacted placeholder:
[REDACTED:vault:abc123] - The vault key is derived from a user passphrase + salt — never stored in the repo
Rules:
- Never log, print, or include raw sensitive values in error messages
- Never skip detection — all ingest paths must go through
redaction.py - Vault key files (
vault.key,vault.salt) are in.gitignore— never commit them - See docs/sensitive-data.md for implementation details
Architecture Rules
- The core engine (
src/callmem/core/) is adapter-agnostic — no MCP or HTTP imports - The MCP server (
src/callmem/mcp/) calls the engine, never the database directly - The UI (
src/callmem/ui/) calls the engine, never the database directly - Data models (
src/callmem/models/) are pure Pydantic — no business logic - Background workers use the job queue — never process inline during a request
- The interactive LLM and the memory-maintenance LLM are always separate concerns
Project Layout Reference
src/callmem/
├── core/ # Engine, DB, retrieval, workers — the brain
│ ├── database.py
│ ├── engine.py
│ ├── repository.py
│ ├── retrieval.py
│ ├── briefing.py
│ ├── redaction.py # Two-layer sensitive data detection
│ ├── crypto.py # Fernet vault encryption
│ ├── ollama.py
│ ├── extraction.py
│ ├── summarization.py
│ ├── compaction.py
│ ├── workers.py
│ ├── queue.py
│ ├── prompts.py
│ └── migrations/
├── mcp/ # MCP server — the external interface
├── ui/ # Web UI — the inspection layer
├── adapters/ # Agent-specific adapters
└── models/ # Data models — the shared contracts
Task Execution
Each task specifies:
- Files to create/modify
- Constraints
- Acceptance criteria
- Suggested tests
Do not skip ahead. Complete all acceptance criteria before moving on.