Lint-AI is for teams building agent memory and semantic review over large, fast-changing corpora: assistant sessions, task notes, traces, reports, decisions, code, and documentation that accumulate faster than any team can review manually.
It is a good fit for people who maintain:
- long-running agent memory over conversations, notes, and decisions
- markdown knowledge bases with lots of cross-links
- internal docs where terminology drifts over time
- codebases where symbols, ownership, usage, and review context matter
- corpora where “what changed?” and “what is current?” matter as much as keyword search
Use Lint-AI when plain search is not enough and memory recall needs evidence. It treats stored context as a network of facts, concepts, links, symbols, ownership facts, and timestamps, then surfaces misalignment, missing context, and retrieval results suited for downstream review or LLM grounding.
The problem it solves is system-level consistency: reading individual documents is not enough when terminology drifts, definitions conflict, ownership changes, or outdated claims persist across a growing corpus. Lint-AI analyzes corpora collectively, not in isolation, to catch these issues before they spread.
Why people use it:
- to recover the right past session or note when an agent needs context
- to catch contradictions before they spread
- to detect terminology drift across documents
- to find orphaned or weakly linked pages
- to ask corpus-level questions over facts, entities, symbols, and time
- to build review packets from changed files and related semantic context
- to feed grounded context into an LLM instead of raw text blobs
Evaluated on LongMemEval-S (500 questions), a public benchmark for long-context agent memory retrieval over multi-session conversation corpora. The scoped variant is used: each query searches only the sessions attached to that question, matching the realistic setting where a system knows which sessions are candidates for a given user. No embedding vectors are used anywhere in the pipeline.
The section below shows both the rust-bert POS/NER branch result and the default heuristic release result.
Aggregate (n=500):
| metric | value |
|---|---|
| recall@5 | 86.9% |
| recall@10 | 93.8% |
| recall@20 | 94.4% |
| recall_any@5 | 94.8% |
| recall_any@10 | 98.2% |
| MRR | 87.1% |
| NDCG@10 | 86.0% |
| avg query latency | 5.1 ms |
recall@k is fractional recall over all gold sessions. recall_any@k counts 1.0 if any gold session appears in the top k. Latency is measured on a single CPU core with no GPU.
By question type:
| question type | n | recall@5 | recall@10 | recall_any@5 | MRR | NDCG@10 |
|---|---|---|---|---|---|---|
| single-session-assistant | 56 | 100.0% | 100.0% | 100.0% | 99.1% | 99.3% |
| single-session-user | 70 | 97.1% | 98.6% | 97.1% | 86.8% | 89.8% |
| knowledge-update | 78 | 96.8% | 98.7% | 100.0% | 95.2% | 94.4% |
| single-session-preference | 30 | 80.0% | 96.7% | 80.0% | 64.7% | 72.3% |
| temporal-reasoning | 133 | 81.1% | 92.0% | 91.7% | 84.1% | 82.3% |
| multi-session | 133 | 77.7% | 87.1% | 94.7% | 85.3% | 80.2% |
Results file: benchmark/data/lintai_longmemeval_scoped_results_0512.json
Aggregate (n=500):
| metric | value |
|---|---|
| recall@5 | 83.7% |
| recall@10 | 89.6% |
| recall@20 | 91.1% |
| recall_any@5 | 92.4% |
| recall_any@10 | 95.6% |
| recall_any@20 | 97.0% |
| MRR | 84.3% |
| NDCG@10 | 81.9% |
| avg query latency | 6.1 ms |
recall@k is fractional recall over all gold sessions. recall_any@k counts 1.0 if any gold session appears in the top k. Latency is measured on a single CPU core with no GPU.
By question type:
| question type | n | recall@5 | recall@10 | recall_any@5 | MRR | NDCG@10 |
|---|---|---|---|---|---|---|
| single-session-assistant | 56 | 100.0% | 100.0% | 100.0% | 98.2% | 98.7% |
| single-session-user | 70 | 94.3% | 98.6% | 94.3% | 77.4% | 82.6% |
| knowledge-update | 78 | 94.2% | 96.8% | 98.7% | 94.6% | 92.4% |
| single-session-preference | 30 | 80.0% | 93.3% | 80.0% | 65.8% | 72.2% |
| temporal-reasoning | 133 | 77.0% | 85.7% | 86.5% | 81.8% | 78.2% |
| multi-session | 133 | 72.5% | 79.3% | 93.2% | 82.7% | 74.3% |
Results file: benchmark/data/lintai_longmemeval_scoped_results.json
See docs/quickstart.md for the shortest path to build, run, query, and use the crate from Rust.
Lint-AI provides provider-specific integrations for Claude Code, Codex, and Gemini CLI. Each integration is opt-in at build time and combines lifecycle hooks with a project-scoped memory layer:
| Feature | Claude Code | Codex |
|---|---|---|
| Project-scoped MCP server | Yes | Yes |
| Lifecycle memory retrieval and capture | Yes | Yes |
| Segmented persistent memory | Yes | Yes |
search and info MCP tools |
Yes | Yes |
record_session control |
Yes | Yes |
| Enable/disable Lint-AI at runtime | Yes | Yes |
| Session status reporting | Native status line plus MCP | MCP plus terminal status line |
| Replay with Lint-AI enabled or disabled | Yes | Yes |
| Session import into provider memory | Yes | Yes |
Gemini CLI uses the opt-in gemini-cli feature and its documented JSON hooks:
cargo build --release --features gemini-cli
./lint-ai --gemini-cli-install /path/to/projectGemini records hook events, injects project memory, and exposes the shared MCP
tools through SessionStart, BeforeAgent, model, tool, compression, and
session-end events. See docs/gemini-cli.md for details.
Antigravity CLI (agy) uses the opt-in agy feature and the same
Gemini-compatible MCP and lifecycle-hook protocol:
cargo build --release --features agy
./lint-ai --agy-install /path/to/projectSee docs/agy.md for configuration paths and session recording.
Build and install both integrations:
cargo build --release --features claude-code,codex
./lint-ai --claude-code-install /path/to/project
./lint-ai --codex-install /path/to/projectEach installer writes lifecycle hooks and an MCP server entry into that agent's
own configuration, which is global, and a memory policy into the named project:
a skill for Claude Code, a delimited block in AGENTS.md for Codex. The MCP
entry carries no project root — the server resolves the project it is launched
in — so installing for a second project does not repoint the first.
Provider memory remains isolated:
/path/to/project/.lint-ai/claude-memory/
/path/to/project/.lint-ai/codex-memory/
The MCP controls are available inside either client:
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 {}
Enabling Lint-AI enables recording by default. Recording can then be stopped independently, so memory retrieval and full session capture remain separate choices. Recording is local, bounded, redacted, and capture-only; it does not inject memory by itself.
For a baseline/replay A/B comparison:
./lint-ai --replay-session <session-id> \
--session-provider codex \
--replay-disable-lint-ai
./lint-ai --replay-session <session-id> \
--session-provider codex \
--replay-enable-lint-aiEach replay receives a new replay-* session ID and a separate archive. The
comparison workflow measures task quality, token usage, latency, tool
activity, memory retrieval, and recording completeness.
These are the latest validated smoke-run measurements for the segmented-routing scenario: one repetition, continuation phase only. They are diagnostic results for the stated run, not universal performance guarantees.
| Provider / arm | Continuation | Input tokens | Cached input | Output tokens | Tool calls | Recall | Hook time |
|---|---|---|---|---|---|---|---|
| Claude native memory | 22.47 s | 123,536 | 123,526 | 1,239 | 4 | 2/3 | 0 ms |
| Claude Lint-AI only | 7.05 s | 15,914 | 15,912 | 376 | 0 | 3/3 | 1.40 s |
| Claude both layers | 7.17 s | 15,914 | 15,912 | 271 | 0 | 3/3 | 1.36 s |
| Codex native memory | 38.13 s | 151,000 | 99,840 | 1,505 | 5 | 2/3 | 0 ms |
| Codex Lint-AI only | 18.53 s | 62,478 | 43,264 | 604 | 2 | 2/3 | 1.33 s |
| Codex both layers | 13.98 s | 31,684 | 25,088 | 454 | 1 | 2/3 | 1.39 s |
The corresponding reports and run conditions are documented in the Claude Code performance tests and Codex performance tests. Both documents describe the one-run limitation and the provider/model/repository versions used.
MCP-path smoke validation measured 47.09 s for Claude MCP-only and 9.38 s for Codex with Lint-AI MCP. The Codex run recorded 13,246 input tokens, 77 output tokens, 2/3 recall, and 1.35 s hook time; the Claude run recorded 189,022 input tokens, 3,678 output tokens, and one Lint-AI MCP call. A separate direct Claude MCP search check returned results in approximately 220 ms; it did not measure agent search-selection behavior.
More detail: Claude Code integration, Codex integration, shared integration architecture, and the integration test strategy. and session metrics.
Lint-AI ingests sessions, notes, traces, documents, and code-oriented artifacts, builds a lexical index plus sparse entity/term tables, and overlays graph structure for links, symbols, ownership, and co-occurrence. Queries are analyzed for intent, entities, and temporal hints, then scored with a blend of lexical, semantic, claim, topic, timestamp, and graph signals. The same corpus analysis also powers misalignment checks such as missing cross-refs, orphan pages, low-confidence claims, and review-oriented packet generation.
If you want the deeper implementation view, see:
docs/chunk-strategy.mddocs/lexical-data.mddocs/artifact-indexing.md
Query semantics currently use the heuristic backend in the release build. The model-backed POS/NER path was used in the experimental rust-bert branch and is kept separate from the audited release graph.
Lint a corpus:
./lint-ai /path/to/repoIf you prefer cargo run:
cargo run --bin lint-ai -- /path/to/repoQuery the corpus:
./lint-ai --query "docker install linux" /path/to/repo/docsGet LLM-ready retrieval context:
./lint-ai --llm-context "docker install linux" /path/to/repo/docsInspect the derived inventory:
./lint-ai /path/to/repo/docs --show-concepts
./lint-ai /path/to/repo/docs --show-headingsShow the main query-oriented modes:
./lint-ai --index /path/to/repo/docs
./lint-ai --show-tier0 /path/to/repo
./lint-ai --show-tier1-entities /path/to/repo
./lint-ai --show-tier1-terms /path/to/repo --tier1-term-ranker yakeUse spaCy for Tier 1 entities if available:
./lint-ai /path/to/repo --show-tier1-entities --tier1-ner-provider spacy --spacy-model en_core_web_smFor optional Claude Code support, build with --features claude-code and see
docs/claude-code.md for the MCP, hooks, and install flow.
Common graph exports, chunking knobs, lexical subset regeneration, and artifact indexing details are documented in docs/.
Use IndexStore when you want mutable ingestion, MemoryIndex when you want an immutable query snapshot, and SourceDocument to add content. For the broader semantic graph, use CorpusGraph, SymbolStore, UsageGraph, and the review model types that are re-exported from the crate root.
use lint_ai::{IndexStore, PipelineOptions, SourceDocument};
fn main() -> anyhow::Result<()> {
let mut index = IndexStore::in_memory(PipelineOptions::default());
index.upsert(SourceDocument {
doc_id: "artifact-1".to_string(),
source: "artifact://artifact-1".to_string(),
content: "docker install guide for linux hosts".to_string(),
concept: "docker install".to_string(),
group_id: None,
headings: vec!["Overview".to_string()],
links: vec![],
timestamp: None,
doc_length: 36,
author_agent: None,
});
let results = index.query("docker install", 5)?;
println!("{}", serde_json::to_string_pretty(&results)?);
Ok(())
}For corpus-local persistence under .lint-ai/, use:
use std::path::Path;
use lint_ai::{IndexStore, PipelineOptions};
let index = IndexStore::for_corpus(Path::new("/path/to/corpus"), PipelineOptions::default())?;If you already have prepared DocRecord values and want the built search structure directly, use lint_ai::index::MemoryIndex.
For symbol and review workflows, the crate root also re-exports:
CorpusGraphfor querying documents, symbols, and usage togetherSymbolRecord/SymbolStorefor symbol indexingOwnershipRecord/OwnershipSummaryfor ownership facts and summariesUsageGraph/UsageEdge/UsageNodefor symbol and ownership relationship tracingReviewPacket/ReviewFinding/ReviewDifffor structured review output
By default the linter skips files larger than 5MB and stops after 50k files. Override these limits:
./lint-ai /path/to/repo --max-bytes 10000000 --max-files 100000Limit directory traversal depth:
./lint-ai /path/to/repo --max-depth 10Limit total bytes read across the corpus:
./lint-ai /path/to/repo --max-total-bytes 100000000The tool automatically scopes to /path/to/repo/docs/** when that folder exists.
Analyze a corpus and emit a suggested lint-ai.json config:
./lint-ai /path/to/repo/docs --analyzeExample output:
Suggested config:
{
"stopwords": ["group messages", "pairing", "channel routing"],
"ignore_sections": ["unscoped", "related"],
"ignore_crossref_sections": ["unscoped", "related"],
"ignore_paths": [],
"allowlist_concepts": []
}
Stats:
pages: 31
top concepts:
- group messages (25)
- pairing (25)
- channel routing (22)
- slack (11)
- telegram (11)
top sections:
- configuration (41)
- setup (35)
- unscoped (31)
- security (28)
- related (22)
You can place a lint-ai.json file in the target root, or pass --config /path/to/lint-ai.json, to control filters.
Use --strict-config to fail fast if the config is invalid.
Limit config size:
./lint-ai /path/to/repo --max-config-bytes 2000000Example used for Openclaw channels:
{
"stopwords": ["channel", "message", "messages", "bot", "client", "config"],
"ignore_sections": ["related", "unscoped"],
"ignore_crossref_sections": ["related", "unscoped"],
"ignore_paths": [],
"allowlist_concepts": ["discord", "slack", "telegram", "whatsapp", "signal", "matrix"],
"scope_prefix": "docs/channels/"
}Run it with ./lint-ai /path/to/openclaw/docs/channels --config /path/to/openclaw/lint-ai.json.
If you want to contribute, the most useful workflow is:
cargo build
cargo test
cargo fmt --allPlease keep changes focused and include tests when behavior changes.
Useful contribution rules:
- run
cargo testbefore opening a PR - run
cargo fmt --allfor Rust code changes - update docs when CLI flags, benchmarks, or query behavior change
- include benchmark notes when ranking or retrieval logic changes
- prefer small PRs with a clear scope
If the change affects query quality, mention the benchmark result it moves.
Running the linter emits findings tagged with severity and link-debt signals:
Missing cross-ref in docs/channels/discord.md -> [[signal]] (high)
Low link density in docs/channels/location.md (outgoing 1, avg 4.2)
Unreachable page: docs/channels/legacy.md
Orphan page: docs/channels/unused.md
Use --show-concepts when you need the derived concept inventory for tuning stopwords or allowlists in lint-ai.json.