Content
# Lint-AI
**Open-source agent memory for coding agents.**
> **AI memory that knows what is still true.** Your agent doesn't just forget; it can confidently remember outdated decisions.
Lint-AI is an open-source **persistent agent memory** layer for Claude Code, Codex, Gemini CLI, Antigravity CLI (AGY), and Muse Code. It turns project history — sessions, documents, decisions, traces, notes, and code — into **current, evidence-backed context** when an agent needs it.
Search can find the right topic. Lint-AI helps an agent answer the harder question: **is this information still true?** It uses relevance, time, and semantic relationships to distinguish the latest truth from old-but-still-relevant history.
[Agent memory guide](docs/agent-memory.md) · [Quickstart](docs/quickstart.md) · [Reproducible demo](#reproducible-terminal-demo) · [Agent integrations](docs/agents.md) · [Benchmarks](#benchmark-highlights) · [Documentation](https://rooagi.github.io/Lint-AI/)
**Current benchmark:** 500-question aggregate · 85.6% fractional Recall@5 ·
96.8% any-hit Recall@10 · 13.0 ms average query latency · single CPU · no GPU
---
## The stale-memory problem
Your project says one thing on Monday and something different on Friday. Both versions remain relevant to the same question.
**Older — `decision-a.md`**
```md
# Gateway Retry Policy
Gateway timeout retry attempts: 5.
```
**Newer — `decision-b.md`**
```md
# Gateway Retry Policy
Gateway timeout retry attempts: 2.
```
Now ask:
```text
How many retry attempts should we use for gateway timeouts?
```
A normal retriever can return either document — or both — and leave the model to guess which value is current.
**Lint-AI keeps `2` as the current answer and preserves `5` as superseded history.**
That difference matters anywhere project truth changes over time: architecture decisions, API contracts, configuration values, runbooks, ownership, terminology, and implementation plans.
There is no required `supersedes:` frontmatter in this example. For simple configuration/value claims, Lint-AI can infer chronological replacement when the documents establish the same semantic domain. Inferred supersession is domain-scoped so unrelated settings such as two different services' `timeout` values do not suppress one another. Explicit supersession metadata remains authoritative when you provide it.
```bash
lint-ai --query \
"How many retry attempts should we use for gateway timeouts?" \
examples/demo-real-terminal
```
Current-state retrieval returns the newer decision with `semantic_status: current`, and `--llm-context` excludes the stale value.
### Reproducible terminal demo

The repository includes a reproducible **asciinema PTY recording** that builds the actual `lint-ai` binary, sets deterministic file mtimes for a controlled stale-memory scenario, runs the neutral query above, verifies the result, and renders the captured terminal session.
```bash
bash scripts/run_real_terminal_demo.sh
```
See [`examples/demo-real-terminal/`](examples/demo-real-terminal/) and [`.github/workflows/demo-real-terminal.yml`](.github/workflows/demo-real-terminal.yml).
---
## Why Lint-AI?
Agent memory is not only a retrieval problem. It is a **state problem**.
A useful memory layer has to answer:
1. **Relevance** — is this about the question?
2. **Recency** — when was this evidence true?
3. **Supersession** — did something newer replace it?
4. **Temporal intent** — is the user asking about now, last Tuesday, or a historical state?
5. **Evidence** — where did the answer come from?
| Ordinary retrieval | Lint-AI |
|---|---|
| Finds topically similar text | Ranks relevant **and current** evidence |
| Can surface stale decisions as if they are current | Tracks time and supersession |
| Treats relative time as mostly query text | Resolves temporal intent against a reference clock |
| Returns passages | Returns context with source and relationship evidence |
| Treats documents mostly in isolation | Connects facts, entities, links, symbols, ownership, and time |
| Leaves corpus drift hidden | Surfaces contradictions, stale claims, terminology drift, orphan pages, and missing links |
Historical evidence is not deleted. A historical query can still retrieve superseded material as history rather than presenting it as current state.
---
## Quickstart
Install directly from GitHub:
```bash
cargo install --git https://github.com/RooAGI/Lint-AI
```
Lint or index a local corpus:
```bash
lint-ai /path/to/repo
```
Query it:
```bash
lint-ai --query "what is the current retry policy?" /path/to/repo/docs
```
Get LLM-ready context:
```bash
lint-ai --llm-context "what is the current retry policy?" /path/to/repo/docs
```
For Docker, HTTP, Python, Rust, and MCP setup, see the [full quickstart](docs/quickstart.md).
---
## Agent integrations
Lint-AI provides opt-in integrations for major coding-agent clients.
| Integration | Project memory | Lifecycle capture | MCP tools | Replay / comparison |
|---|---:|---:|---:|---:|
| [Claude Code](docs/claude-code.md) | Yes | Yes | Yes | Yes |
| [Codex](docs/codex.md) | Yes | Yes | Yes | Yes |
| [Gemini CLI](docs/gemini-cli.md) | Yes | Yes | Yes | — |
| [Antigravity CLI / AGY](docs/agy.md) | Yes | Yes | Yes | — |
| [Muse Code](docs/muse-code.md) | Yes | Yes | Yes | — |
Build the Claude Code and Codex integrations:
```bash
cargo build --release --features claude-code,codex
./target/release/lint-ai --claude-code-install /path/to/project
./target/release/lint-ai --codex-install /path/to/project
```
All agents share one project-scoped memory store:
```text
/path/to/project/.lint-ai/memory/
```
Upgrading from an older version? Legacy per-provider stores
(`.lint-ai/claude-memory/`, `.lint-ai/codex-memory/`,
`.lint-ai/gemini-cli-memory/`, `.lint-ai/agy-memory/`,
`.lint-ai/muse-memory/`) are migrated into `.lint-ai/memory/` on first run
and removed once their migration succeeds.
The shared MCP controls include session recording, memory listing, and runtime enable/disable controls:
```text
mcp__lint-ai__record_session {"action":"start|stop|status"}
mcp__lint-ai__list_memories {"limit":20}
mcp__lint-ai__enable_lint_ai {}
mcp__lint-ai__disable_lint_ai {}
mcp__lint-ai__lint_ai_status {}
```
See [agent integrations](docs/agents.md) and the [MCP guide](docs/mcp.md) for the complete setup.
---
## Benchmark highlights
Lint-AI is evaluated on **LongMemEval-S**, a public benchmark for long-context agent-memory retrieval over multi-session conversations.
### How to read the benchmark numbers
Every result has three independent labels:
1. **Scope:** the 500-question aggregate evaluates the full scoped dataset;
the 133-question segmented comparison evaluates only the multi-session
slice.
2. **Metric:** fractional (regular) recall measures the fraction of all gold
sessions recovered; any-hit recall counts a question as successful when at
least one gold session is recovered.
3. **Cutoff:** `@5`, `@10`, and `@20` mean the top 5, 10, or 20 returned
results. A Recall@5 value must not be compared with Recall@10 without
checking both the metric type and benchmark scope.
| Benchmark | Scope | Metric | Cutoffs |
|---|---|---|---|
| Aggregate headline | 500 questions | Fractional and any-hit recall | @5, @10, @20 |
| Segmented comparison | 133 multi-session questions | Any-hit recall (fractional also in raw report) | @5, @10 |
### Why two benchmark tables?
The **500-question aggregate** is the public overall headline. It runs the
heuristic global path across every LongMemEval-S question category. The
**133-question segmented comparison** isolates the multi-session category and
uses the same question subset for fixed segmented, adaptive segmented, and
single-index baselines. The single-index row is therefore the fair control for
segmented routing—not a second overall headline.
Because the scopes and query mix differ, their recall and latency values must
not be compared as a time series or combined into one score.
The current benchmark uses the repository's raw LongMemEval-S dataset, runs 500 question-scoped queries through the heuristic release backend, and uses no embedding vectors.
**500 questions · heuristic release backend · single CPU · no GPU**
| Metric | Result |
|---|---:|
| Fractional Recall@5 | **85.6%** |
| Fractional Recall@10 | **92.0%** |
| Fractional Recall@20 | **93.1%** |
| Any-hit Recall@5 | **94.2%** |
| Any-hit Recall@10 | **96.8%** |
| Any-hit Recall@20 | **97.6%** |
| MRR | **87.0%** |
| NDCG@10 | **85.0%** |
| Average query latency | **13.0 ms** |
`Fractional Recall@k` is the average fraction of all correct answer sessions recovered in the top *k*. `Any-hit Recall@k` counts a query as successful when any correct answer session appears in the top *k*.
The repository keeps the dataset downloader, production-style benchmark binary, shared AgentMemory scorer, comparison workflow, and temporal-reasoning experiment harnesses so results can be reproduced and changes can be evaluated without silently changing the query path.
See [benchmark methodology](docs/benchmark.md), [benchmark results](docs/benchmark-results.md), and [comparison methodology](docs/comparison.md).
### Latest segmented-index comparison
The latest 133-query multi-session comparison measures the three supported
retrieval modes on the same corpus and CPU-only host. This run explicitly used
five initial segments; adaptive routing is opt-in and may expand that candidate
set from 5 to 12 segments. The server and benchmark CLI default to fixed top-3.
| Mode | Routing / candidates | Any-hit Recall@5 | Any-hit Recall@10 | MRR | Avg. query latency |
|---|---|---:|---:|---:|---:|
| Segmented (fixed) | top-5 | **95.49%** | 95.49% | **0.859** | **1.25 ms** |
| Segmented (adaptive) | 5 → 12 | **96.24%** | **96.24%** | 0.839 | 4.36 ms |
| Single index | global search | 93.23% | 93.98% | 0.814 | 6.41 ms |
Adaptive routing improves any-hit Recall@5 and any-hit Recall@10 by 0.75
percentage points over
fixed routing, with a latency trade-off. The fixed segmented mode remains the
recommended default for predictable latency. See the
[segmented-index design](docs/distributed-segment-index.md) and
`segment_scoped_benchmark` for the full command and conditions. The exact
summary is checked in as
[`segment-multisession-v0.2.0.json`](comparison/results/segment-multisession-v0.2.0.json).
<details>
<summary><strong>Experimental rust-bert POS/NER branch</strong></summary>
<br>
An experimental model-backed POS/NER branch has also reached higher retrieval metrics, but it is intentionally kept separate from the audited heuristic release dependency graph. See the benchmark documentation for its results and conditions.
</details>
### Integration smoke measurements
The repository also includes reproducible Claude Code and Codex replay/performance tests. These are diagnostic one-run measurements, **not universal performance guarantees**.
| Provider / arm | Continuation | Input tokens | Tool calls | Scenario recall | Hook time |
|---|---:|---:|---:|---:|---:|
| Claude native memory | 22.47 s | 123,536 | 4 | 2/3 | 0 ms |
| Claude Lint-AI only | 7.05 s | 15,914 | 0 | 3/3 | 1.40 s |
| Codex native memory | 38.13 s | 151,000 | 5 | 2/3 | 0 ms |
| Codex Lint-AI only | 18.53 s | 62,478 | 2 | 2/3 | 1.33 s |
Read the full conditions and limitations:
- [Claude Code performance tests](docs/claude-code-performance-tests.md)
- [Codex performance tests](docs/codex-performance-tests.md)
- [Comparison methodology](docs/comparison.md)
- [Session metrics](metrics/README.md)
---
## What Lint-AI can do
- Recover the right past session or project note when an agent needs context
- Rank current evidence ahead of superseded guidance
- Infer simple chronological configuration updates inside an established semantic domain
- Preserve historical evidence for historical questions
- Resolve relative-time queries against an explicit reference date when one is available
- Catch contradictions before they spread
- Detect terminology drift across documents
- Find orphaned or weakly linked pages
- Query facts, entities, symbols, ownership, and time
- Build review packets from changed files and related semantic context
- Feed grounded context into an LLM instead of raw text blobs
- Keep agent memory inspectable and local to the project
---
## How it works
Lint-AI treats project memory as more than a bag of text.
```text
Sessions · docs · code · traces
│
▼
1. Ingest
│
▼
Facts · entities · symbols · time
│
▼
2. Understand
│
▼
Links · ownership · supersession
│
▼
3. Connect
│
▼
Ranked · sourced · current context
│
▼
4. Retrieve
```
Under the hood, Lint-AI builds a lexical index plus sparse entity/term tables and overlays graph structure for links, symbols, ownership, semantic relations, and time.
A single prepared-query path owns query analysis, augmented search text, temporal context, routing intent, reference-clock handling, semantic suppression, and provenance annotation. The CLI and `IndexStore` use the same semantic/temporal query policy so current-state behavior does not depend on which high-level entry point you call.
The same corpus model powers review-oriented checks such as missing cross-references, orphan pages, low-confidence claims, stale knowledge, and semantic drift.
For implementation details, see:
- [Chunk strategy](docs/chunk-strategy.md)
- [Lexical data](docs/lexical-data.md)
- [Artifact indexing](docs/artifact-indexing.md)
- [Shared integration architecture](src/integrations/README.md)
---
## Use Lint-AI from your stack
### CLI
```bash
lint-ai --query "what is the current retry policy?" /path/to/repo
```
### Docker / HTTP
```bash
export SERVER_TOKEN=local-dev-token
docker compose up --build -d
curl http://127.0.0.1:8080/health
```
See the [HTTP server guide](docs/server.md).
### Python
Build the optional Python extension with `uv` and `maturin`, then use the
memory service from Python:
```bash
uv venv --python 3.10
source .venv/bin/activate
uv pip install "maturin>=1.9.4,<2"
maturin develop --release --uv
```
To build a distributable wheel instead, run `maturin build --release`; the
wheel is written under `target/wheels/`.
```python
import lint_ai
memory = lint_ai.Memory(path="./memory-index")
memory.add(
"request-1",
"user-1",
"session-1",
[{"role": "user", "content": "Docker runs on Ubuntu hosts"}],
)
print(memory.search("docker ubuntu", "user-1", 5))
# Use the same API against a running lint-ai server:
# NOTE: base_url must be passed by keyword. A positional first argument
# is `path` and would silently create a local index instead of connecting.
remote = lint_ai.Memory(base_url="http://127.0.0.1:8080", api_key="local-token")
print(remote.search("docker ubuntu", "user-1", 5))
```
### Rust
`MemoryService` is the only public memory access API. Request, response, document,
and configuration types are public; stores, indexes, snapshots, builders, and
persistence modules are internal. See [the API migration guide](docs/memory-service-api.md).
```rust
use lint_ai::{MemoryService, PipelineOptions, SourceDocument};
fn main() -> anyhow::Result<()> {
let mut memory = MemoryService::in_memory(PipelineOptions::default());
memory.upsert(SourceDocument::with_stable_doc_id_from_source(
"docs/install.md".to_string(),
"Docker install guide for Linux hosts".to_string(),
"docker install".to_string(),
None,
vec!["Overview".to_string()],
vec![],
None,
None,
));
memory.refresh()?;
let results = memory.search_with_filters("docker install", "workspace", None, 5, &Default::default())?;
println!("{}", serde_json::to_string_pretty(&results)?);
Ok(())
}
```
See [docs/quickstart.md](docs/quickstart.md) for complete examples.
---
## When is Lint-AI a good fit?
Lint-AI is especially useful when you maintain:
- Long-running agent memory over conversations, notes, and decisions
- Markdown knowledge bases with many cross-links
- Internal documentation where terminology and decisions drift over time
- Codebases where symbols, ownership, usage, and review context matter
- Fast-changing corpora where freshness matters as much as relevance
If your only requirement is simple keyword search over a static set of documents, Lint-AI is probably more machinery than you need.
---
## Project status
Lint-AI is under active development. The release query path uses the heuristic backend; experimental model-backed query semantics remain separate from the audited release dependency graph.
The security audit workflow runs `cargo audit`. Binary persistence uses
`oxicode`, and the lexical index uses Tantivy 0.25 to preserve measured recall.
Current audits report only
transitive Tantivy warnings in `lru` and `memmap2`; these are tracked for
upstream fixes.
Issues, benchmark reproductions, integration feedback, and contributions are welcome.
- [Open an issue](https://github.com/RooAGI/Lint-AI/issues)
- [Join a discussion](https://github.com/RooAGI/Lint-AI/discussions)
- [Read the documentation](https://rooagi.github.io/Lint-AI/)
## License
Apache-2.0
Connection Info
You Might Also Like
No recommendations available