Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f843908
test: add unit, integration, smoke, and e2e test suite
kargarisaac Feb 25, 2026
b86395c
refactor(config): update default.toml and settings for test support
kargarisaac Feb 25, 2026
07f866a
refactor(memory,runtime): adapt pipelines and providers for testability
kargarisaac Feb 25, 2026
38f97ed
chore: update gitignore, pyproject, README, and linting docs
kargarisaac Feb 25, 2026
d8cfc10
chore: v0.1.0 release prep — CONTRIBUTING, CHANGELOG, CI, slim deps, …
kargarisaac Feb 25, 2026
4ce819b
feat(extract): add user preference extraction to memory pipeline
kargarisaac Feb 25, 2026
7159427
feat(sync): hash-based change detection for session traces
kargarisaac Feb 25, 2026
c11b71e
feat(runtime): async explore tool for parallel subagent execution
kargarisaac Feb 25, 2026
901587c
fix(dashboard): NULL start_time, missing graph return, datetime seria…
kargarisaac Feb 25, 2026
5808e5d
fix(dashboard): overhaul charts, runs, memory filters, pipeline tab, …
kargarisaac Feb 25, 2026
4015aa8
feat(runtime,memory): add optional guidance to RLM pipelines
kargarisaac Feb 25, 2026
143afe5
fix(dashboard): restore runs Open flow with full-screen chat viewer
kargarisaac Feb 25, 2026
9496334
feat(dashboard): enhance markdown rendering and statistics tracking
kargarisaac Feb 25, 2026
e288da5
feat(dashboard): update branding and enhance session detail extraction
kargarisaac Feb 25, 2026
8a14208
fix(dashboard): streamline onFilterChange method by removing unnecess…
kargarisaac Feb 25, 2026
de40f60
feat(dashboard): introduce local UI for session analytics and memory …
kargarisaac Feb 25, 2026
a0094b1
chore(docs): remove Acreta migration section from README
kargarisaac Feb 25, 2026
5d67807
docs: refresh README and CLI reference defaults
kargarisaac Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Create venv and install dependencies
run: uv venv && uv pip install -e '.[test]'

- name: Lint with ruff
run: uv run ruff check src/ tests/

- name: Run unit tests
run: |
uv run python -m pytest tests/ -x -q \
--ignore=tests/test_smoke_pipelines.py \
--ignore=tests/test_smoke_agent.py \
--ignore=tests/test_integration_extract.py \
--ignore=tests/test_integration_summarize.py \
--ignore=tests/test_integration_agent.py \
--ignore=tests/test_integration_providers.py \
--ignore=tests/test_e2e_sync.py \
--ignore=tests/test_e2e_maintain.py \
--ignore=tests/test_e2e_full_cycle.py \
-m "not smoke and not integration and not e2e"
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,23 @@ site/
.tmp/
specs/
.lerim/
.playwright-cli/
.logfire/
.pytest_cache/
.ruff_cache/
.claude/


dev-docs/

# Test artifacts
snapshot_report.html
qa_artifacts/*
.pytest_tmp/

# Local browser/db state
orb.db/

AGENTS.md
CLAUDE.md

29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0] - 2026-02-25

### Added

- Continual learning layer for coding agents and projects.
- Platform adapters for Claude Code, Codex CLI, Cursor, and OpenCode.
- Memory extraction pipeline using DSPy RLM to extract decisions and learnings from coding session traces.
- Trace summarization pipeline using DSPy RLM to produce structured summaries with YAML frontmatter.
- PydanticAI lead agent with a read-only explorer subagent for memory operations.
- Three CLI flows: `sync` (extract, summarize, write memories), `maintain` (merge, archive, decay), and `chat` (query memories).
- Daemon mode for continuous sync and maintain loop.
- Local read-only web dashboard.
- Session catalog with SQLite FTS5 for session search.
- Job queue with stale job reclamation.
- TOML-layered configuration: shipped defaults, global, project, and env var override.
- OpenTelemetry tracing via Logfire with PydanticAI and DSPy instrumentation.
- Multi-provider LLM support: OpenRouter (with Nebius routing), Ollama, ZAI, OpenAI, Anthropic.
- File-first memory model using markdown files with YAML frontmatter.
- Project-first memory scope with global fallback.
- Memory primitives: decisions, learnings, and summaries.
- Comprehensive test suite with 290 tests across unit, smoke, integration, and e2e layers.
- Skills distribution via `npx skills add lerim-dev/lerim-cli`.
95 changes: 95 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Contributing to Lerim

Lerim is licensed under BSL 1.1. By contributing you agree your changes
fall under the same license (1 user free, 2+ users need a commercial license).

## Dev environment setup

Requires Python 3.12+.

```bash
uv venv && source .venv/bin/activate
uv pip install -e '.[test]'
```

## Running tests

```bash
# Unit tests (no LLM keys needed)
tests/run_tests.sh unit

# Smoke tests (needs LLM API key in env)
tests/run_tests.sh smoke

# Everything
tests/run_tests.sh all
```

Lint before submitting:

```bash
ruff check src/ tests/
```

## Coding style

Full rules live in `docs/simple-coding-rules.md`. The short version:

- **Minimal code.** Prefer fewer functions, fewer layers, fewer lines.
- **Strict schemas.** Use Pydantic models / enums for inputs and outputs.
- **Fail fast.** No `try/except` fallbacks for missing packages. If something
is required, let it raise.
- **Docstrings everywhere.** Every file gets a top-level docstring explaining
what it contains. Every function gets a docstring.
- **Self-tests.** Add an `if __name__ == "__main__":` block that exercises the
real code path (no mocking or stubbing). Keep it inline, not in a separate
function.
- **No dead code.** When you replace logic, remove the old path.
- **Config from TOML, keys from env.** Runtime config comes from the TOML
layer stack (`default.toml -> global -> project -> env var`). Only API keys
use environment variables.

## Adding a new platform adapter

This is the most common contribution. Follow these steps:

1. **Create `src/lerim/adapters/<platform>.py`.**
Start with a top-level docstring. Implement the four functions required by
the `Adapter` protocol in `src/lerim/adapters/base.py`:

- `default_path() -> Path | None` -- where traces live on disk.
- `count_sessions(path) -> int`
- `iter_sessions(traces_dir, start, end, known_run_ids) -> list[SessionRecord]`
- `find_session_path(session_id, traces_dir) -> Path | None`
- `read_session(session_path, session_id) -> ViewerSession | None`

See an existing adapter (e.g. `codex.py` or `claude.py`) as a reference.

2. **Register the adapter** in `src/lerim/adapters/registry.py`:
add an entry to `_ADAPTER_MODULES` and optionally to `_AUTO_SEED_PLATFORMS`.

3. **Add a self-test** (`if __name__ == "__main__":` block) at the bottom of
the new adapter file that exercises the real code path.

4. **Add unit tests** in `tests/test_<platform>_adapter.py`. Look at the
existing adapter test files for the expected patterns.

5. **Update `tests/README.md`** if you added new fixtures or test infrastructure.

## Reporting bugs

Open a GitHub issue with:

- Steps to reproduce.
- Expected vs actual behavior.
- Lerim version (`python -m lerim --version`), Python version, and OS.
- Relevant config (redact API keys).

## Pull request checklist

- [ ] `ruff check src/ tests/` passes with no errors.
- [ ] `tests/run_tests.sh unit` passes.
- [ ] New/changed files have top-level docstrings and function docstrings.
- [ ] New source files include an `if __name__ == "__main__":` self-test when practical.
- [ ] No mocking or stubbing in self-test flows.
- [ ] Related docs updated if behavior changed.
124 changes: 111 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img src="assets/lerim.png" alt="Lerim Logo" width="160">
</p>

<p align="center"><strong>Continual learning layer for coding agents.</strong></p>
<p align="center"><strong>Continual learning layer for coding agents</strong></p>
<p align="center"><a href="https://lerim.dev/">lerim.dev</a></p>

Lerim is a continual learning layer that gives coding agents persistent memory across sessions. It watches your agent conversations (Claude Code, Codex, Cursor, OpenCode, ...), extracts decisions and learnings, and stores them as plain markdown files that both humans and agents can read. Memories are refined offline over time through merging, deduplication, archiving, and decay-based forgetting. You can query stored memories anytime to bring relevant past context into your current session.
Expand All @@ -16,7 +16,7 @@ Lerim is file-first and primitive-first.
- Global fallback memory: `~/.lerim/`
- Search default: `files` (no index required)
- Orchestration runtime: `pydantic-ai` lead agent + read-only explorer subagent
- Extraction/summarization: `dspy.RLM` role-configured models (default Ollama `qwen3:8b`)
- Extraction/summarization: `dspy.RLM` role-configured models (default OpenRouter `x-ai/grok-4.1-fast`)
- Graph source of truth: explicit id/slug references (and `related` when present)

This keeps memory readable by humans and easy for agents to traverse.
Expand All @@ -29,6 +29,22 @@ Lead flow:
4. Lead writes memory only through boundary-enforced runtime write/edit tools.
5. `sync` stays lightweight; `maintain` runs offline memory refinement (merge duplicates, archive low-value entries, consolidate related memories, apply time-based decay).

### Sync path

<p align="center">
<img src="assets/sync.png" alt="Sync path" width="700">
</p>

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

<p align="center">
<img src="assets/maintain.png" alt="Maintain path" width="700">
</p>

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
Expand Down Expand Up @@ -70,6 +86,39 @@ At the start of a session, tell your agent:

Your agent will run `lerim chat` or `lerim memory search` to pull in past decisions and learnings before it starts working.

## Dashboard

The dashboard gives you a local UI for session analytics, memory browsing, and runtime status.

<p align="center">
<img src="assets/dashboard.png" alt="Lerim dashboard" width="1100">
</p>

### Run it locally

```bash
# 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`](skills/lerim/cli-reference.md)
Expand All @@ -93,8 +142,8 @@ lerim status # runtime state
```bash
uv venv && source .venv/bin/activate
uv pip install -e .
scripts/run_tests.sh unit
scripts/run_tests.sh all
tests/run_tests.sh unit
tests/run_tests.sh all
```

### Configuration
Expand All @@ -108,6 +157,13 @@ TOML-layered config (low to high priority):

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-fast`
- `explorer`: `provider=openrouter`, `model=x-ai/grok-4.1-fast`
- `extract`: `provider=openrouter`, `model=x-ai/grok-4.1-fast`, `sub_model=x-ai/grok-4.1-fast`
- `summarize`: `provider=openrouter`, `model=x-ai/grok-4.1-fast`, `sub_model=x-ai/grok-4.1-fast`

### Tracing (OpenTelemetry)

Lerim uses PydanticAI's built-in OpenTelemetry instrumentation for agent observability.
Expand Down Expand Up @@ -144,12 +200,58 @@ Config options (`[tracing]` in TOML):
| `include_httpx` | `false` | Capture raw HTTP request/response bodies |
| `include_content` | `true` | Include prompt/completion text in spans |

### Supported platforms
### Connecting coding agents

- `claude` — reads from `~/.claude/projects/` (JSONL files)
- `codex` — reads from `~/.codex/sessions/` (JSONL files)
- `cursor` — reads from Cursor's `state.vscdb` SQLite DB, exports sessions as JSONL to `~/.lerim/cache/cursor/`
- `opencode` — reads from `~/.local/share/opencode/`
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:

```bash
lerim connect auto
```

Or connect a specific platform:

```bash
lerim connect claude
lerim connect codex
lerim connect cursor
lerim connect opencode
```

List currently connected platforms:

```bash
lerim connect list
```

Disconnect a platform:

```bash
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:

```bash
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

Expand Down Expand Up @@ -217,10 +319,6 @@ lerim memory reset --yes # wipe everything
lerim sync --max-sessions 5 # re-sync newest conversations
```

## Migration from Acreta

If you previously used Acreta, the data directories have moved from `~/.acreta/` to `~/.lerim/` and from `<repo>/.acreta/` to `<repo>/.lerim/`. Existing data is not migrated automatically. Run `lerim memory reset --yes && lerim sync` to start fresh.

## Docs

- Runtime architecture: `docs/architecture.md`
Expand Down
Binary file added assets/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/maintain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/screenshots/codex-analysis.png
Binary file not shown.
Binary file removed assets/screenshots/codex-overview.png
Binary file not shown.
Binary file removed assets/screenshots/readiness-report.png
Binary file not shown.
Binary file removed assets/screenshots/session-viewer.png
Binary file not shown.
Binary file added assets/sync.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Unified dashboard frontend lives here.

- `index.html`: main dashboard page (tabs, Alpine stores, UI actions).
- `assets/graph-explorer/`: built graph explorer bundle used by Memories tab.
- `assets/lerim.png`: dashboard brand logo used in the top navbar.
- `frontend/graph-explorer/`: Vite + TypeScript source for graph explorer bundle.

Served by `src/lerim/app/dashboard.py` from this folder as the static root.
Expand Down
Binary file added dashboard/assets/lerim.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading