Continual learning layer for coding agents and software projects.
Project description
Your coding agents forget everything after each session.
Lerim remembers — across all of them.
The Problem
You spend 20 minutes explaining context to your coding agent. It writes great code. Next session? It's forgotten everything. Every decision, every pattern, every "we tried X and it didn't work" -- gone.
And if you use multiple agents -- Claude Code at the terminal, Cursor in the IDE, Codex for reviews -- none of them know what the others learned. Your project knowledge is scattered across isolated sessions with no shared memory.
This is agent context amnesia, and it's the biggest productivity drain in AI-assisted development.
The Solution
Lerim is a continual learning layer that gives coding agents persistent, shared memory across sessions and platforms. Use Claude Code, Cursor, Codex, and OpenCode on the same project — Lerim unifies their knowledge into one memory store that every agent can query.
- Watches your agent sessions across Claude Code, Codex CLI, Cursor, and OpenCode
- Extracts decisions and learnings automatically using LLM pipelines
- Stores everything as plain markdown files in your repo (
.lerim/) - Refines memories continuously -- merges duplicates, archives stale entries, applies time-based decay
- Unifies knowledge across all your agents -- what Cursor learns, Claude Code can recall
- Answers questions about past context:
lerim ask "why did we choose Postgres?"
No proprietary format. No database lock-in. Just markdown files that both humans and agents can read. Memories get smarter over time, not stale.
Supported Agents
| Agent | Session Format | Status |
|---|---|---|
| Claude Code | JSONL traces | Supported |
| Codex CLI | JSONL traces | Supported |
| Cursor | SQLite to JSONL | Supported |
| OpenCode | SQLite to JSONL | Supported |
More agents coming soon -- PRs welcome!
How It Works
Lerim is file-first and primitive-first.
- Primitive folders:
decisions,learnings,summaries - Project memory first:
<repo>/.lerim/ - Global fallback memory:
~/.lerim/ - Search default:
files(no index required) - Orchestration runtime:
pydantic-ailead agent + read-only explorer subagent - Extraction/summarization:
dspy.ChainOfThoughtwith transcript windowing, role-configured models (default OpenRouteropenai/gpt-5-nano, 300K window) - Graph source of truth: explicit id/slug references (and
relatedwhen present)
This keeps memory readable by humans and easy for agents to traverse.
Lead flow:
- Extract candidates from transcript archive.
- Lead agent orchestrates with runtime tools and delegates a read-only explorer subagent.
- Lead runs deterministic decision policy for
add|update|no-op. - Lead writes memory only through boundary-enforced runtime write/edit tools.
syncstays lightweight;maintainruns offline memory refinement (merge duplicates, archive low-value entries, consolidate related memories, apply time-based decay).
Sync path
The sync path processes new agent sessions: reads transcript archives, extracts decision and learning candidates via DSPy, deduplicates against existing memories, and writes new primitives to the memory folder.
Maintain path
The maintain path runs offline refinement over stored memories: merges duplicates, archives low-value entries, consolidates related memories, and applies time-based decay to keep the memory store clean and relevant.
Quick start
1. Install
pip install lerim
Prerequisites: Python 3.10+, Docker
2. Set up
lerim init # interactive setup — detects your coding agents
lerim project add . # add current project (repeat for other repos)
3. Start Lerim
lerim up
That's it. Lerim is now running as a Docker service — syncing sessions, extracting
decisions and learnings, refining memories, and serving a dashboard at http://localhost:8765.
4. Teach your agent about Lerim
Install the Lerim skill so your agent knows how to query past context:
npx skills add lerim-dev/lerim-cli
This works with Claude Code, Codex, Cursor, Copilot, Cline, Windsurf, OpenCode, and other agents that support skills.
5. Get the most out of Lerim
At the start of a session, tell your agent:
Check lerim for any relevant memories about [topic you're working on].
Your agent will run lerim ask or lerim memory search to pull in past decisions and learnings before it starts working.
Running without Docker
If you prefer not to use Docker, Lerim works directly:
lerim connect auto # detect agent platforms
lerim daemon # run sync + maintain in terminal
Dashboard
The dashboard gives you a local UI for session analytics, memory browsing, and runtime status.
Run it locally
# simple
lerim dashboard
# explicit host/port
python -m lerim dashboard --host 127.0.0.1 --port 8765
Then open http://127.0.0.1:8765.
Tabs
- Overview: high-level metrics and charts (sessions, messages, tools, errors, tokens, activity by day/hour, model usage).
- Runs: searchable session list (50/page) with status and metadata; open any run in a full-screen chat viewer.
- Memories: library + editor for memory records (filter, inspect, edit title/body/kind/confidence/tags).
- Pipeline: sync/maintain status, extraction queue state, and latest extraction report.
- Settings: dashboard-editable config for server, model roles, and tracing; saves to
~/.lerim/config.toml.
Notes
- Top bar filters (
Agent,Scope) update dashboard metrics and run listings. - Graph Explorer code is kept in the project but currently hidden in the UI.
CLI reference
Full command reference: skills/lerim/cli-reference.md
# Setup (host-only)
lerim init # interactive setup wizard
lerim project add ~/codes/my-app # register a project
lerim project list # list registered projects
# Docker service
lerim up # start Lerim container
lerim down # stop it
lerim logs --follow # tail logs
# Alternative: run directly without Docker
lerim serve # start HTTP server + daemon loop
# Service commands (require lerim up or lerim serve running)
lerim ask "Why did we choose this?" # query memories
lerim sync # one-shot: sync sessions + extract
lerim maintain # one-shot: merge, archive, decay
lerim status # runtime state
lerim dashboard # show dashboard URL
# Local commands (run on host, no server needed)
lerim memory search "auth pattern" # keyword search
lerim memory list # list all memories
lerim memory add --title "..." --body "..." # manual memory
lerim connect auto # detect and connect platforms
Development
uv venv && source .venv/bin/activate
uv pip install -e .
lerim init # first-time config
lerim project add . # track this repo
docker build -t lerim . # build Docker image locally
lerim up # start the service
tests/run_tests.sh unit
tests/run_tests.sh all
Configuration
TOML-layered config (low to high priority):
src/lerim/config/default.toml(shipped with package -- all defaults)~/.lerim/config.toml(user global)<repo>/.lerim/config.toml(project overrides)LERIM_CONFIGenv var path (explicit override, for CI/tests)
API keys come from environment variables only (ZAI_API_KEY, OPENROUTER_API_KEY, OPENAI_API_KEY, optional ANTHROPIC_API_KEY).
Default role model config (from src/lerim/config/default.toml):
lead:provider=openrouter,model=x-ai/grok-4.1-fastexplorer:provider=openrouter,model=x-ai/grok-4.1-fastextract:provider=openrouter,model=openai/gpt-5-nano,max_window_tokens=300000summarize:provider=openrouter,model=openai/gpt-5-nano,max_window_tokens=300000
Tracing (OpenTelemetry)
Lerim uses PydanticAI's built-in OpenTelemetry instrumentation for agent observability. Stderr logs are kept minimal; detailed traces (model calls, tool calls, tokens, timing) go through OTel spans instead.
One-time setup:
uv pip install logfire
logfire auth
logfire projects new
Enable tracing:
# env var (quick toggle)
LERIM_TRACING=1 lerim sync
# or in config
# .lerim/config.toml
[tracing]
enabled = true
View traces at https://logfire.pydantic.dev.
Config options ([tracing] in TOML):
| Key | Default | Description |
|---|---|---|
enabled |
false |
Enable tracing (or set LERIM_TRACING=1) |
include_httpx |
false |
Capture raw HTTP request/response bodies |
include_content |
true |
Include prompt/completion text in spans |
Connecting coding agents
Lerim ingests session transcripts from your coding agents to extract decisions and learnings. The lerim connect command registers an agent platform so Lerim knows where to find its sessions.
Supported agents
| Platform | Session store | Format |
|---|---|---|
claude |
~/.claude/projects/ |
JSONL files |
codex |
~/.codex/sessions/ |
JSONL files |
cursor |
~/Library/Application Support/Cursor/User/globalStorage/ (macOS) |
SQLite state.vscdb, exported to JSONL cache |
opencode |
~/.local/share/opencode/ |
SQLite opencode.db, exported to JSONL cache |
How to connect
Auto-detect and connect all supported platforms at once:
lerim connect auto
Or connect a specific platform:
lerim connect claude
lerim connect codex
lerim connect cursor
lerim connect opencode
List currently connected platforms:
lerim connect list
Disconnect a platform:
lerim connect remove claude
Custom session path
If your agent stores sessions in a non-default location, use --path to point Lerim to the correct folder:
lerim connect claude --path /custom/path/to/claude/sessions
lerim connect cursor --path ~/my-cursor-data/globalStorage
The path is expanded (~ is resolved) and must exist on disk. This overrides the auto-detected default for that platform.
Search
Retrieval is file-first: scan markdown memory files directly. No index required.
Memory layout
Project scope:
<repo>/.lerim/
config.toml # project overrides
memory/
decisions/
learnings/
summaries/
YYYYMMDD/
HHMMSS/
{slug}.md
archived/
decisions/
learnings/
meta/
traces/
sessions/
workspace/
sync-<YYYYMMDD-HHMMSS>-<shortid>/
extract.json
summary.json
memory_actions.json
agent.log
subagents.log
session.log
maintain-<YYYYMMDD-HHMMSS>-<shortid>/
maintain_actions.json
agent.log
subagents.log
index/ # reserved
Global fallback scope follows the same layout under ~/.lerim/.
Primitive frontmatter (lean)
decision:id,title,created,updated,source,confidence,tagslearning:id,title,created,updated,source,confidence,tags,kindsummary:id,title,description,date,time,coding_agent,raw_trace_path,run_id,repo_name,created,source,tags
All metadata lives in frontmatter — no sidecars.
Reset policy
Memory reset is explicit and destructive.
lerim memory reset --scope project|global|both --yes- Deletes
memory/,workspace/, andindex/under selected root(s), then recreates canonical folders. --scope project: resets<repo>/.lerim/only.--scope global: resets~/.lerim/only (includes sessions DB).--scope both(default): resets both.- Sessions DB lives in global
index/, so--scope projectalone does not reset sessions.
Fresh start:
lerim memory reset --yes # wipe everything
lerim sync --max-sessions 5 # re-sync newest conversations
Contributing
Lerim is open to contributions. Whether it's a new agent adapter, a bug fix, or a documentation improvement, PRs are welcome.
- Read the Contributing Guide
- Browse open issues
- Agent adapter PRs are especially appreciated -- see
src/lerim/adapters/for examples
Docs
Full documentation: docs.lerim.dev
If lerim saves you from re-explaining context to your agent, give it a ⭐
Star on GitHub
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 lerim-0.1.51.tar.gz.
File metadata
- Download URL: lerim-0.1.51.tar.gz
- Upload date:
- Size: 111.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fe6ca3ca074600bccfaad5c914febb5443c9294583743bb0c95cea245e39d2f
|
|
| MD5 |
6eac435757176d1f1ccbe3148f7594c5
|
|
| BLAKE2b-256 |
590d7dadb43b26f56b47e3ec414dfaf4551207aa91bb82c38b3dea5ae65cf191
|
Provenance
The following attestation bundles were made for lerim-0.1.51.tar.gz:
Publisher:
publish.yml on lerim-dev/lerim-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lerim-0.1.51.tar.gz -
Subject digest:
4fe6ca3ca074600bccfaad5c914febb5443c9294583743bb0c95cea245e39d2f - Sigstore transparency entry: 1006689690
- Sigstore integration time:
-
Permalink:
lerim-dev/lerim-cli@1b3a938de51021e82b39c8d4b8e2672d8c5e2434 -
Branch / Tag:
refs/tags/v0.1.51 - Owner: https://github.com/lerim-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1b3a938de51021e82b39c8d4b8e2672d8c5e2434 -
Trigger Event:
push
-
Statement type:
File details
Details for the file lerim-0.1.51-py3-none-any.whl.
File metadata
- Download URL: lerim-0.1.51-py3-none-any.whl
- Upload date:
- Size: 127.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2d1fc88d11a44e5b6874fd75bf30baec3cd18af2fccc6dd2b0fd7c483594d6a
|
|
| MD5 |
2cd3e1b64d73bfb2242471f629302f05
|
|
| BLAKE2b-256 |
65d26d1856abba2bfc04376a75ac362869cd8804300bc1b348afc49b988f219e
|
Provenance
The following attestation bundles were made for lerim-0.1.51-py3-none-any.whl:
Publisher:
publish.yml on lerim-dev/lerim-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lerim-0.1.51-py3-none-any.whl -
Subject digest:
f2d1fc88d11a44e5b6874fd75bf30baec3cd18af2fccc6dd2b0fd7c483594d6a - Sigstore transparency entry: 1006689695
- Sigstore integration time:
-
Permalink:
lerim-dev/lerim-cli@1b3a938de51021e82b39c8d4b8e2672d8c5e2434 -
Branch / Tag:
refs/tags/v0.1.51 - Owner: https://github.com/lerim-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1b3a938de51021e82b39c8d4b8e2672d8c5e2434 -
Trigger Event:
push
-
Statement type: