Design¶
How memoire is implemented — scoring, context compression, ingestion, provider architecture, and language coverage.
Scoring¶
Node score¶
score = recency + frequency + centrality + side_effect_cost
recency = exp(-age_days / 7)
frequency = log1p(access_count)
centrality = log1p(reachability × 2 + causal_in)
side_effect_cost = log1p(len(side_effects)) × 0.5
reachability is computed by BFS from each node — the total number of nodes reachable downstream via causal edges. This is more accurate than out-degree: a core module with 3 direct importers each imported by 5 more files has out-degree 3 but reachability 18.
Edge score¶
edge_score = score(source) + score(target) + causal_bonus + cost_bonus + confidence_boost
causal_bonus = 1.0 if is_causal
cost_bonus = 0.5 if cost == "high"
confidence_boost = log1p(observations) × 0.3
Context compression¶
get_context() returns three things:
- Structure — directory/file tree (~200 tokens, full project shape)
- Relationships — top 100 edges ranked by score (~5,000–25,000 tokens depending on graph density)
- Recent events — last 10 episodic events (~200 tokens)
Total context typically ranges from 6,000 to 25,000 tokens depending on project size and edge density. Compare to re-reading the project: a 20-file Python project typically runs 20,000–60,000 tokens; a project with PDFs and images can easily reach 300,000+ tokens.
When the assistant needs more detail, it calls expand(path) — full content and relationships for that specific node, only when actually needed.
The top-100 edge cap means the causal context stabilises well below 30,000 tokens regardless of project size. The structural-only view (Condition B in the benchmark) compresses to 2,000–7,000 tokens for most projects.
Ingestion pipeline¶
Code files¶
Static analysis extracts:
- Import dependencies → IMPORTS edges
- Inheritance → INHERITS edges
- Side-effect categories (network, file_io, subprocess, database, cache)
- State mutations (self.attr = ..., this.attr = ...) → later promoted to DRIVES
- Test-file detection → IMPORTS promoted to ASSERTS_ON
After each ingest run, three promotion rules fire:
- Fan-in promotion — modules imported by 3+ files receive
DRIVESedges to all importers - Test assertion promotion — test-file
IMPORTSbecomeASSERTS_ON(high cost) - Mutation promotion — files with detected state writes receive
DRIVESedges to importers
Markdown and RST files¶
Full text is stored for full-text search. An LLM call extracts intentional causal edges: SPECIFIES, IMPLEMENTS, DRIVES, DOCUMENTS, RELATES_TO — each with a rationale sentence.
PDF files¶
Extracted page-by-page using pypdf. Pages are concatenated with --- Page N --- separators and fed into the same LLM extraction pipeline as markdown. Requires pip install "memoire-ai[pdf]".
Image files (PNG, JPG, SVG, GIF, WebP)¶
Base64-encoded and sent to the provider's vision API (Claude by default) with a prompt requesting causal edges as JSON. Produces SPECIFIES, DRIVES, DOCUMENTS, and RELATES_TO edges describing what the diagram or screenshot depicts and how it relates to other project files.
Image extraction requires a provider with vision capability (claude or openai). Requires pip install "memoire-ai[pdf]" (includes Pillow).
Language coverage¶
Static analysis (consequential causality) is implemented for seven language families:
| Language | Side effects | State mutations | Test detection |
|---|---|---|---|
| Python | requests, httpx, sqlite3, redis, subprocess, open() |
self.attr = ... |
test_*.py, *_test.py, tests/ |
| TypeScript / JS | fetch, axios, fs.*, exec, spawn, prisma, mongoose |
this.attr = ... |
.test.ts, .spec.ts, __tests__/ |
| Go | net/http, os.Create, exec.Command, database/sql |
— | _test.go |
| Rust | reqwest, std::net, std::fs, Command::new, sqlx, diesel |
self.field = ... |
_test.rs, tests/ |
| Java | java.net, HttpClient, java.io, ProcessBuilder, java.sql |
this.field = ... |
*Test.java, src/test/ |
| Ruby | Net::HTTP, faraday, File.*, Open3, ActiveRecord, Redis |
@attr = ... |
_spec.rb, _test.rb, spec/ |
| C / C++ | socket, fopen, system, popen, sqlite3_exec, curl_easy_* |
— | test_*.c, *_test.cpp, tests/ |
Markdown and RST files feed the intentional causality layer via LLM extraction. PDFs and images are also processed — see the Ingestion pipeline section above.
Multi-provider architecture¶
The causal graph, scoring, and MCP server are provider-agnostic. Three integration layers vary by provider:
- Instructions file: tells the assistant to call
get_contextat session start - MCP config: where the MCP server registration lives
- LLM for markdown/PDF/image extraction: which API produces intentional causal edges
| Provider | Instructions file | MCP config | Activity hooks | Extraction model |
|---|---|---|---|---|
| Claude Code | CLAUDE.md |
.claude/settings.json |
✓ PostToolUse / PreToolUse | claude --print CLI |
| Cursor | .cursor/rules/memoire.mdc |
.cursor/mcp.json |
— | Anthropic API |
| Windsurf | .windsurfrules |
~/.codeium/windsurf/mcp_config.json |
— | Anthropic API |
| Codex CLI | AGENTS.md |
.codex/config.toml |
— | OpenAI API |
| Gemini CLI | GEMINI.md |
.gemini/settings.json |
— | Google API |
| Ollama | — | — | — | Local Ollama |
See Provider Setup for the full configuration details.
Daemon architecture¶
The memoire daemon is a long-running asyncio process with three subsystems running concurrently:
- Filesystem watcher (
watchdog) — monitors the project root for file changes. On change: re-processes the modified file, runs post-ingest promotions every 10 changes. - Hook receiver — an HTTP server (localhost) that receives
hook-eventandpre-readcalls from the AI coding assistant's hooks. Writes episodic events to the graph. - MCP server — exposes
get_context,expand,search,recent_eventsover the Model Context Protocol. Called by the assistant at session start and during operation.
All three share a single SurrealDB connection pool. The daemon is registered as a systemd user service (Linux) or LaunchAgent (macOS) via memoire install-service — it starts on login and restarts automatically if it crashes.