Ambient, hook-driven memory · Claude Code & Codex

Every codebase has lore.

The bug with the non-obvious cause. The convention no one wrote down. The footgun that cost an afternoon. Lore captures them — then your agent recalls the relevant one automatically on your next prompt, no slash command required, so the same lesson is never learned twice.

Open source · MIT · zero recurring cost · install once, works everywhere · no runtime dependencies

claude code — lore
you ▸ add a retry to the image upload job ↳ lore recalled 1 learning into context: • learnings/jobs/upload-retry-idempotency.md “Uploads aren't idempotent — retries duplicate rows.” claude ▸ Good catch — I'll key it on a dedupe token so re-runs don't double-upload.

No command to run. The relevant note just shows up.

Ambient recall

A hook matches what you typed against your notes and injects the best matches every turn — locally, instantly, with no model call. Matching is word-boundary, not substring, with a score threshold that keeps weak overlaps out, and it folds accents, so a prompt in Spanish finds the note as surely as an English one. It even fires when the agent is about to edit a file — once per note per session — so the gotcha surfaces exactly when you touch the code. You never have to remember to look.

Frictionless capture

At the end of a working turn, Claude is nudged to write down anything genuinely new and reusable — one fact per file, in plain Markdown that lives in your repo. The nudge is smart by default, skipping conversational turns, and an overlap check updates a related note instead of spawning a duplicate.

Stays fresh

A deterministic linter flags notes whose code was deleted or has drifted, and every recalled note carries a freshness flag — so a stale entry is never trusted blindly, and your knowledge base never rots into confident, wrong advice.

Ambient by design

Memory that doesn't wait to be asked

Most agent memory is manual — you have to remember to look, run a command, or paste context into a workflow. Lore is ambient: it lives in hooks, so the relevant learning is already in context before the agent answers, and the capture nudge fires on its own at the end of a working turn. No slash command in the loop, no model call, no recurring cost.

That makes it a light layer, not another framework to adopt. Install it once and it works in every project — on its own, or quietly alongside whatever heavier workflow suite you already run.

The loop

Each task leaves the next one easier

Most work accrues debt — every change makes the next one a little harder. Lore inverts that: the knowledge you generate is captured and fed forward.

  1. 1

    Recall

    You prompt. Relevant past learnings are pulled into context before Claude answers.

  2. 2

    Work

    Claude solves the task with that hindsight already in hand.

  3. 3

    Capture

    If the turn produced something non-obvious and reusable, it's written down — one note.

  4. 4

    Keep fresh

    As the code changes, drifted notes are flagged for a quick re-verify. Then repeat.

Starting from empty? /lore:mine reads the history you already have — reverts, fix commits, the messages that explain a why, the files with the most churn — and proposes the first notes, so the loop has something to recall on day one instead of week six.

Install

Three lines, then it's working

Requires Claude Code or OpenAI Codex, plus Python 3 (already on macOS & Linux; on Windows, Claude Code's bundled Git Bash runs it).

# add the marketplace and install the plugin
/plugin marketplace add aoc81/lore
/plugin install lore@lore

# then, once per project you want it in:
/lore:init

/lore:init creates a learnings/ folder (committed by default, so your team shares it) and offers two optional guards: a pre-push freshness check, and a small GitHub Actions workflow that runs the same checks on every PR. Nothing is sent anywhere — recall is a local text match.

Commands

Eight commands, mostly invisible

Recall and the capture nudge run on their own. These are for when you want to drive it directly.

/lore:init

Scaffold the learnings/ store in a project and (optionally) install the pre-push hook and the CI guard.

/lore:mine

Bootstrap an empty store from git history — ranks past reverts, fix commits, and churn hot spots, then writes the ones that clear the gate.

/lore:capture

Write a learning from the current work now — applies the gate, avoids duplicates, files it in the store.

/lore:search

Search the store by title and tags from the CLI — the same scorer the recall hook runs on every prompt.

/lore:lint

Check that every note's referenced files still exist. --report ranks drifted notes; --dupes finds near-duplicate pairs; --index rebuilds the index.

/lore:stats

Store-health snapshot: counts by status and category, the drift backlog, near-duplicates, dangling links, and recall activity — including notes that keep surfacing but never get read.

/lore:sweep

Re-verify drifted notes against the current code and update, supersede, or refresh them.

/lore:scan

Scan the store for committed secrets — the same blocking check the pre-push hook runs, on demand.

These are Claude Code slash commands. On Codex the same steps are plain scripts: init is python3 codex/install.py, capture is the lore skill plus the Stop hook, and lint/mine are direct python3 ~/.codex/lore/{verify_refs,mine_history}.py calls.

Under the hood

How the trick works

Recall is a hook, not a model call

A UserPromptSubmit hook tokenizes your prompt and matches it against each note's title and tags — never the body. Matching is word-boundary (auth never hits author) with a score threshold that filters noise, and Unicode accent-folding, so a Spanish prompt tokenizes properly and autenticación hits a tag written either way. The top few are injected as paths and titles, and Claude opens a file only if it's actually relevant; notes marked superseded are down-ranked and flagged, so they're never read as current truth. Each surfaced note is logged locally — and logged again when Claude actually opens the file, so /lore:stats can tell shown from used.

# injected before Claude sees your prompt
Possibly-relevant prior learnings:
- learnings/api/pagination-cursor.md — Cursor,
  not offset, above 10k rows
- learnings/ci/cache-key-lockfile.md — Cache key
  must hash the lockfile  [SUPERSEDED]

One fact per file, in your repo

Each learning is a Markdown file with light frontmatter. Plain text, version-controlled, greppable, and readable without the plugin. Tags are how recall finds it; files: is how the linter knows what the note depends on.

---
title: Cursor pagination above 10k rows
track: knowledge
tags: [api, pagination, cursor, scale]
files: [src/api/list.ts]
status: current
verified: 2026-01-12
---

Day one, not week six

An empty store surfaces nothing, so the plugin looks dead and nobody captures — the failure mode that kills this idea before it pays off. /lore:mine breaks it: it scores your git history for reverts, fix commits, messages that explain a why, and churn hot spots, marks any candidate an existing note already covers, and hands Claude a ranked worklist to write real notes from — under the same gate as any other capture, with the diff read as evidence first.

$ /lore:mine

  scanned 412 commits, found 23 candidate(s)

  [1] score 7  a3f19c2e1  2026-02-14  Revert "switch
      the upload queue to at-least-once"
      signals: revert, fix/bug, explains why
      files: src/jobs/upload.ts, src/db/queue.sql
      read it: git show a3f19c2e1

  churn hot spots (undocumented gotchas live here):
      31x  src/api/list.ts

Freshness is deterministic

The linter checks that referenced files still exist, and a --report mode uses a single streaming git log pass to rank notes whose code changed since you last verified them — a cheap, LLM-free worklist of what to re-check. A --dupes mode flags near-duplicate notes before they pile up, and an optional pre-push hook runs the existence check before every push.

$ /lore:lint --report

Drift triage (biggest gap first):
   34d  learnings/api/pagination-cursor.md
        verified 2026-01-12, code changed 2026-02-15

Shared by default, guarded at the push

The store is committed so a whole team benefits — which means a note is effectively published the moment it's written. A blocking pre-push secret scan reads the store and aborts the push if it finds a key, token, or credential; because a git hook only protects the machine it's installed on, /lore:init also offers a ~15-line GitHub Actions workflow that runs the same scan on every PR, from the copies committed to your repo. Recall itself only ever emits paths and titles — never the body, and titles are length-capped and stripped of control characters — so a shared store has a small injection surface. Run the scan by hand any time with /lore:scan.

$ git push

# pre-push hook runs scan_secrets.py
lore: secret scan — 14 learnings
✗ aws-keys.md:7  AWS access key id
push aborted — remove the secret or allowlist it

FAQ

Questions

Does it send my code or notes anywhere?

No. Recall is a local text match over files in your repo — no network calls, no external service. Capture and the linter run locally too. The only thing that ever leaves your machine is what you push — which is exactly what the secret scan guards.

Does it cost anything per prompt?

No. Recall is a deterministic text match run in a hook — no model call, no tokens, no API spend on each turn. You install it once and it runs for free in every project, however many prompts you send.

Does it work on Windows?

Yes — macOS, Linux, and Windows. The hooks are stdlib Python invoked through a small shell wrapper that resolves python3/python/py. On Windows, Claude Code's bundled Git Bash runs it. No build step, no dependencies.

Are my learnings private or shared?

Both — split by who else has to read it. The team store is committed by default, so everyone benefits from the same knowledge base. Set personalStoreDir in .lore.json and you also get a private store outside the repo: recall reads it alongside the team store and flags it [personal], but it's never indexed, linted, or pushed — that's where preferences like “show me the diff before committing” go, instead of into a teammate's PR. Want everything private? Add the learnings/ folder to .gitignore. And because a shared store is effectively published, a blocking pre-push secret scan — plus the optional CI check — aborts before a key, token, or credential can land in it.

How is it different from other agent-memory tools?

Most are manual — you invoke recall inside a workflow or run a command to look things up. Lore is ambient: recall and capture live in hooks, so they happen on their own, with no command in the loop and no per-prompt cost. It's a light, standalone layer rather than a framework — install it once and it works in every project, on its own or alongside whatever heavier workflow suite you already use.

Which agents does it work with?

Both Claude Code and OpenAI Codex. They share the same hook contract, so prompt recall, edit-time recall, the capture nudge, and the post-compaction reminder all run natively on each, from the same scripts and store. One gap: read telemetry needs a Read tool, which Codex doesn't have, so the shown vs. used stats are Claude-only. The notes themselves are just Markdown, useful even without either plugin.

Where does the name come from?

Every codebase has lore — the hard-won, half-remembered knowledge that lives in people's heads and chat threads. Lore writes it down where the work happens, and hands it back exactly when it's needed.

Your codebase, with a memory.

Install it once, and every task starts teaching the next.