lore (noun) — a body of traditions and knowledge on a subject, passed from person to person. — Oxford English Dictionary
简体中文 · English
Framework-agnostic project memory for AI coding agents. A long-term knowledge base that persists architecture, decisions, and conventions as plain Markdown any agent can read.
The pain point:
- Projects lack a memory file; in a new session the agent re-reads the code repeatedly.
- After
/init, platform memory files (e.g.AGENTS.md) are rarely updated by hand — stale knowledge pollutes memory.- Claude reads
CLAUDE.md, Codex readsAGENTS.md, the two drift apart- ... ...
So
lorewas born.
| Structured | Searchable | Portable |
|---|---|---|
Architecture / Decisions / Conventions in .lore/ with stable IDs and lifecycle tags |
lore query answers with [file#ID] citations; lore history traces commits |
Single source .lore/ projected to CLAUDE.md / .cursorrules / AGENTS.md via mirrors |
lore is a SKILL, not a CLI. It is a Markdown spec (
skill/SKILL.md) for Claude Code, Cursor, OpenCode, Cline, Aider, Copilot and others. There is nolorebinary — phrases likelore initandlore syncare spoken to your agent.
Contents: Installation · Quick start · How it works · Workflows · Platform mirrors · FAQ
Via skills CLI (recommended):
npx skills add https://github.com/TheaDust/lore --skill lore -y # project: ./.agents/skills/lore
npx skills add https://github.com/TheaDust/lore --skill lore -g -y # global: ~/.agents/skills/lore
# limit to specific agent(s):
npx skills add https://github.com/TheaDust/lore --skill lore -g -y --agent claude-code
npx skills add https://github.com/TheaDust/lore --skill lore -g -y --agent opencodeManual: the install unit is skill/:
git clone https://github.com/TheaDust/lore.git /tmp/lore
cp -r /tmp/lore/skill <your-agent-skills-dir>/lore
# e.g. cp -r /tmp/lore/skill ~/.claude/skills/loreOr tell your agent:
Install https://github.com/TheaDust/lore as a skill (skill is in
skill/).
Full reference docs: skill/references/ · Plain-language workflow guide: WORKFLOWS.md
Phrases you say to your agent (no binary):
Explicit lore <command> and natural-language requests clearly about project memory (such as "remember this project decision") both work. Native /init / /compact and unrelated generic tasks do not trigger lore.
lore init # One-time: scan project, draft entries, confirm, create .lore/
lore sync # After a feature/refactor: detect changes, propose [NEW]/[STALE]/[REFINED]/[ALERT]
lore compress # Refresh SUMMARY.md when stale; optionally regenerate mirrors
lore mirror # Force-regenerate CLAUDE.md and other mirrors from current .lore/lore query <term> # Answer from memory with entry IDs
lore audit # Check memory vs. code, report to .lore/audit/
lore history <entry-id|path|--scope> [--json] # Git commits behind an entry.lore/
├── SUMMARY.md # Digest; agents read first
├── .config.json # Optional config
├── _global/ # Global memory
│ ├── ARCHITECTURE.md
│ ├── DECISIONS.md
│ └── CONVENTIONS.md
├── scopes/<scope>/ # Per-scope memory, e.g. frontend, backend
│ ├── ARCHITECTURE.md
│ ├── DECISIONS.md
│ └── CONVENTIONS.md
├── draft/ # init proposals
├── audit/ # audit reports
└── .archive/ # My notes backups on wipe
Each entry is one bullet, at most two lines, with a deterministic ID LAYER-YYYY-MM-DD-xxxx — the xxxx is a 4-hex hash of the entry text, so the exact same text yields the same hash:
- [ARCH-2026-07-09-a3f2] Use Next.js App Router; reason: streaming + RSC. #added:2026-07-09
- [DEC-2026-02-03-7c19] Chose Zustand over Redux; reason: 60% less boilerplate. #added:2026-02-03 #verified:2026-06-15
- [CONV-2026-01-20-b1e8] Never commit secrets; use dotenv + .env.local (gitignored). #added:2026-01-20Changing an entry's text changes its ID — old IDs survive only in git history. Lifecycle tags (#added / #verified / #stale) track entry state; #superseded-by:<id> links replacements so compress / history can walk the chain. Spec: skill/references/entry-format.md.
Current-state queries and summaries exclude entries carrying #stale or #superseded-by, even when there is no successor. Untagged entries remain eligible; age alone is a review signal. Historical questions can still retrieve inactive entries with their status identified.
ARCH records the architectural fact and may include a brief reason within the two-line limit. Comparisons, tradeoffs, or detailed rationale needing a separate entry go in DEC; reason: alone does not force a split.
Four workflows update canonical memory or platform mirrors; audit writes a separate report without editing canonical entries:
init— one-time setup. Scans the project, drafts candidate entries into.lore/draft/, asks which agents' mirrors to cover, then on confirmation creates.lore/(running an initialcompressforSUMMARY.md) and the mirror files.sync— run after each feature, refactor, or bug fix. Combines commits since the last sync withgit diff HEADfor staged and unstaged changes, and scans untracked files separately (empty repos use a file scan). Checks each candidate body for duplicates before writing, classifies facts into ARCH / DEC / CONV, and proposes[NEW]/[STALE]/[REFINED]/[ALERT]updates. Low-risk changes apply automatically per the sync trust level; real additions or contradictions wait for your confirmation. Writes.lore/*only — mirrors untouched by default.compress— refreshesSUMMARY.mdwhen it goes stale (3–5 entries per scope and layer, idempotent), regenerating mirrors whenauto_mirroris on.mirror— force-regeneratesCLAUDE.mdand other mirror files from the current.lore/, skipping targets whose Lore section is unchanged and preservingMy notesverbatim.
Typical cadence: lore init once → lore sync after each feature/refactor → lore compress when SUMMARY.md is stale → lore mirror when scopes change or you want to publish.
A mirror keeps per-session cost flat by projecting a ~600-byte index into files your agent already reads:
Example CLAUDE.md Lore section
<!-- LORE:START -->
## Lore (auto-managed)
Project memory at `.lore/`. Before project-specific questions, read `.lore/SUMMARY.md`
as the digest, then open the referenced entries for the full text; cite entry IDs.
**Structure**:
- Digest: `.lore/SUMMARY.md`
- Global: `.lore/_global/`
- Scopes: `.lore/scopes/`
- `.lore/scopes/backend/` (Flask 3 + SQLAlchemy 2 + pytest; Python 3.11+)
- `.lore/scopes/frontend/` (React 18 + TypeScript + Vite + Zustand + Axios)
**Query**: `lore query <term>` or `lore query <scope>:<term>`
<!-- LORE:END -->
---
## My notes (free edit)
- Anything here is preserved verbatim across syncs.query and history are pure reads. audit never edits canonical entries but writes a report under .lore/audit/; see the Workflows table for details.
| Command | What it does | Writes | Reference |
|---|---|---|---|
init |
Scan project, draft entries, confirm | .lore/* + mirrors |
workflows#init |
sync |
Detect changes, propose updates | .lore/* only |
workflows#sync |
query |
Answer from memory with IDs | nothing | workflows#query |
audit |
Check memory vs. code, write report | .lore/audit/* |
workflows#audit |
compress |
Rebuild SUMMARY.md |
.lore/SUMMARY.md |
workflows#compress |
mirror |
Regenerate mirrors (deduped) | CLAUDE.md etc. |
workflows#mirror |
history |
Git commits for an entry / file / scope | nothing | workflows#history |
Sync trust levels
| Change type | high |
medium (default) |
low |
|---|---|---|---|
| De-duplicate hit | auto | auto | confirm |
| REFINED, tags only | auto | auto | confirm |
| REFINED, body changed | auto | confirm | confirm |
NEW entry |
auto | confirm | confirm |
STALE mark |
auto | confirm | confirm |
ALERT |
confirm | confirm | confirm |
medium auto-applies low-risk changes; real additions or contradictions require confirmation.
Canonical store is .lore/*; mirrors are projections into files agents already read. Targets auto-detected by scanning the repo root; lore init asks which agents to cover, or set mirror_targets explicitly.
| Platform | File |
|---|---|
| Claude Code | CLAUDE.md |
| Cursor | .cursorrules / .cursor/rules/*.mdc |
| Cline | .clinerules |
| Aider / Codex / OpenCode | AGENTS.md |
| Windsurf | .windsurfrules |
| GitHub Copilot | .github/copilot-instructions.md |
| Continue.dev | .continue/rules/lore.md |
| LangGraph / DeepAgents | no file — read .lore/*.md directly |
Each mirror has ## Lore (auto-managed) bounded by <!-- LORE:START --> / <!-- LORE:END --> and ## My notes (free edit) preserved verbatim.
Token cost
| Component | Loaded when | Typical size | Per-session |
|---|---|---|---|
| Mirror file | Every session | ~600 bytes (index mode) | yes |
skill/SKILL.md |
Every lore <cmd> |
~19 KB | per-invocation |
skill/references/workflows.md |
Every lore <cmd> (routed section) |
~17 KB | per-invocation |
.lore/SUMMARY.md |
On demand | 1–30 KB | on demand |
scopes/<scope>/*.md |
On demand | 1–5 KB each | on demand |
lore query result |
Per query | bounded by matches | per query |
Mirror size scales with scope count, not entry count. Dumping SUMMARY.md into the mirror trades session-start cost for zero fetch — not recommended.
Scripts and tests
python skill/scripts/id_hash.py "Use Next.js App Router" # 4-char ID hash
python skill/scripts/list_entries.py --json
python skill/scripts/find_duplicates.py --json
python skill/scripts/find_duplicates.py --json --candidate "Use Next.js App Router"
python skill/scripts/find_stale.py --days=90 --json
python skill/scripts/history.py DEC-2026-02-03-7c19Python 3.6+, stdlib only. Tests: python -m unittest discover -s tests -v. Details: skill/scripts/README.md.
Configuration
.lore/.config.json is optional:
{
"schema_version": 1,
"auto_mirror": false,
"sync_updates_mirror": false,
"sync_trust": "medium",
"mirror_targets": ["CLAUDE.md"],
"mirror_mode": "index",
"compress_thresholds": { "max_entries": 500, "max_days_since_compress": 30 },
"sync_thresholds": { "min_lines_changed": 50, "min_directories_changed": 2 }
}See skill/references/config.md and skill/references/compatibility.md.
When not to use lore
- Short-lived scripts or one-off demos
- Rapid prototyping where decisions churn weekly
- Tiny single-file projects
- Projects where agents should be read-only
- Monorepos with 50+ packages (split per cluster instead)
Does lore work without git?
Mostly. init / query / audit / compress / mirror work by reading files. sync loses git diff (agent asks what changed) and history requires a repo. Helper scripts work either way.
Can I hand-edit .lore/*.md?
Yes — plain Markdown. Use id_hash.py for new IDs. Run lore mirror after to refresh mirrors.
How is this different from .cursorrules / AGENTS.md?
Those are flat rule lists. lore is structured (ARCH/DEC/CONV), atomic (one fact per entry), and historical (#added / #verified / #stale), and it generates those files for you.
What about the agent's native /init or /compact?
Different concerns — /init scaffolds a project, /compact compresses conversation, lore manages long-term knowledge. They coexist.
I already have a root AGENTS.md. Can I still use lore?
Run lore init and choose take over — your file becomes a two-section mirror, original content preserved as My notes. Same for CLAUDE.md / .cursorrules.
I added a new scope after init. Re-run init?
No — lore sync detects the new scope from changed paths and creates scopes/<name>/ automatically. Then lore mirror.
What is the difference between sync and mirror?
sync updates .lore/ from code; mirror updates agent-facing files from .lore/. sync deliberately does not touch mirrors so git log stays readable.
What if I disagree with an entry?
Edit .lore/*.md directly. Next mirror / compress reflects it. git checkout .lore/ reverts.
Can I sync .lore/ without git?
Git is recommended. Other sync tools work for plain text but won't understand IDs or tags. Don't run two agents on the same .lore/ concurrently.
skill/SKILL.md · workflows · entry-format · summary-template · audit-template · monorepo-detection · stale-new-markers · platform-mirrors · config · history-command · compatibility · scripts