Repository navigation
Releases: aoc81/lore
Release list
Lore 0.5.1
Everything since v0.4.0 — the v0.5.0 feature set, which was never released, plus four fixes.
If you tried to install Lore before today, it did not work. marketplace.json was missing its required owner field from the very first commit, so /plugin marketplace add aoc81/lore failed for everyone with Invalid schema: owner: Invalid input. It went unnoticed through five versions because developing the plugin in its own repo loads it as lore@inline, which skips that validation entirely. Fixed, and guarded by tests/test_manifests.py.
Bootstrap from git history — /lore:mine
Cold start is the failure mode this whole idea dies of: empty store → recall surfaces nothing → nobody captures → still empty. /lore:mine reads the material already sitting in git — reverts, fix commits, bodies that explain a why, churn hot spots — scores it (English and Spanish signals; noise, sweep and store-only commits filtered), and marks any candidate an existing entry already covers using the recall scorer, so a second pass never duplicates the first. The threshold sits deliberately above "fix keyword + focused change": a bare Fix X with no body is the most common commit in any repo, and showing all of them buries the rest.
Personal store
The capture gate catches user preferences, which until now landed in the committed, team-shared — effectively published — store. personalStoreDir is the private half: recall reads it alongside the team store and flags it [personal], but it is never indexed, linted, or secret-scanned, and the skill routes preference-shaped learnings there rather than quietly writing them to the shared store. A path inside the team store is refused, since it would be committed anyway, and the deleted-ref freshness flag is skipped for entries stored outside the project, whose files: may name another repo.
Team-wide secret guard
The blocking pre-push scan only ever protected the machine that ran /lore:init, so a teammate without the hook could still push a key into a published store. A workflow template runs the same scan_secrets.py and verify_refs.py --strict on every PR, from the committed .lore/ copies, so CI and the local hook cannot drift apart and nobody else has to install anything. /lore:init offers it; Codex gets --ci.
Read telemetry
The recall log recorded what was shown, never what was used. A PostToolUse(Read) hook logs store reads, and /lore:stats separates the two — "surfaced 12x, read 0x" is a misleading title, not a lesson. With no read events logged at all, the line reports "none logged yet" rather than accusing every entry of it.
Codex parity
Codex has since gained PreToolUse and SessionStart(compact) with the same additionalContext contract, so edit-time recall and the post-compaction reminder now run there too — 4 hooks registered, up from 2.
Fixes
- The secret scanner missed every prefixed secret name. The hardcoded-assignment rule wrapped its keyword in
\b, and_is a word character, so\bnever fires between an underscore and a letter:db_password,my_api_key,STRIPE_SECRETandaws_secret_access_keyall walked past a blocking guard. Those are the dominant real-world spellings — snake_case and SCREAMING_SNAKE in Python, YAML and.envcontent, which is exactly what gets pasted into a learning.client_secrethit only because it is spelled out as its own alternative, which disguised the gap. The placeholder filter still neutralisesdb_password = changeme, and backtracking stays linear. - The linter was dead in a personal-store-only project.
verify_refsbailed whenever the team store directory was missing, even withpersonalStoreDirset and populated, so/lore:lint,/lore:statsand/lore:sweepwere all silently gone while recall and capture kept working. It now gives up only when both stores are absent. Related: the check mode no longer reports "all entries have valid file refs" after examining zero entries. marketplace.jsongained its requiredowner(see above), plus a manifest test suite that checks the declared versions agree and everyhooks.jsoncommand points at a script that actually ships.
Upgrading
If you installed from the default branch during the window when the manifests said 0.5.0, you may be running a build with the old secret scanner under that same version string. claude plugin update lore@lore picks up 0.5.1.
Lore 0.4.0
Recall now honors your config everywhere, stops repeating itself during a refactor, and works for prompts that aren't in English.
Recall correctness
staleStatusesis respected everywhere. It only reached prompt-time ranking before; freshness flags and edit-time recall hardcodedsuperseded/obsolete/deprecated. A project-defined status likearchivedwas penalized on prompt recall but treated as live guidance when you edited the file. Both paths now read the config.- Edit-time matches are ranked before they're cut.
maxRecalltruncated in alphabetical path order, so a superseded entry could displace a current one. Live entries now sort first, ties keep store order. - New
staleAfterMonths(default6) — one configurable freshness threshold, replacing the same constant duplicated in two units (STALE_AFTER_MONTHS = 6in recall,STALE_AFTER_DAYS = 183in the linter).
Edit-time recall dedupes per session
The PreToolUse hook fires on every Edit/Write, so a refactor touching one file ten times injected the same learning ten times. Surfaced entries are now remembered for the session in .git/lore-recall-seen.json (local, inside .git, never committed) and not re-injected. Keyed by the hook's session_id; a new session starts clean. Fails open — no session id, no .git, or corrupt state means the learning still surfaces.
Non-English prompts
- Unicode, accent-folding tokenizer.
[a-z0-9_]+splitautenticaciónintoautenticaci+n, quietly degrading recall for anyone prompting in Spanish (or any accented language). Tokens are now\w+with diacritics folded, so an accented prompt also matches a tag written without accents (autenticación↔autenticacion). - Spanish stop words.
para,este,cuando,quiero,necesitoand ~85 more reached the scorer and could prefix-match real vocabulary (para→parameters). Deliberately not stopped:todo/todos/algo, which are real English/domain tokens. - New
stopWordsconfig key — words added to the built-in list, for domain noise (a product name in every prompt) or the three above. - Docs: write tags in the language you prompt in — accents fold, languages don't.
Hardening
- Injected titles are capped at 140 characters and stripped of control characters. Recall's "never inject bodies" promise limits the prompt-injection surface of a shared store — but
title:was injected verbatim and unbounded, so a malicious PR could smuggle an instruction payload through it. - The secret scanner no longer double-reports an Anthropic key (the OpenAI
sk-rule also matched it).
Performance & diagnostics
iter_entriesreads a bounded 8 KB head instead of whole entry files — it only ever needed the frontmatter, and it runs on every prompt and every edit. Constant cost per entry, with a full-read fallback so correctness never depends on the window.- The linter names ignored
.lore.jsonkeys.load_configdeliberately never fails, so a typo likemaxRecalsilently did nothing./lore:lintand/lore:statsnow report unknown keys with a did-you-mean suggestion, plus wrongly-typed values.
Tests
52 → 86. Closes the coverage gaps on run_pretool, the run_hook JSON envelope, --index, and recall-log rotation, and covers every change above (including a regression test that fails against the old English-only stop list).
No behavior change requiring action: every new config key defaults to the previous behavior.
Install / update: /plugin install lore@lore (auto-updates).
Lore 0.3.0
Sharper recall, a quieter capture nudge, near-duplicate triage, and a real test suite — all still hook-driven, staying in the lightweight recall/capture niche.
Recall
- Word-boundary scoring — an exact word hit scores 2, a prefix overlap scores 1, and an entry needs a score of 2 to surface.
authno longer matchesauthor, and one weak overlap never injects noise. - Short tags are findable — 2–3 char prompt words (
ci,api,aws) now match against exact tags, where they're curated vocabulary. Previously the 4-char minimum made them impossible to hit. - Directory & glob
files:refs — a ref may be an exact path, a directory prefix (src/auth/), or a glob (src/*.py), and all three power edit-time recall, the linter, and drift triage. Path matching is case-insensitive on Windows. - Local recall telemetry — every surfaced entry is logged to
.git/lore-recall.log(local only, never committed) so/lore:statscan show which learnings actually get used and which never surface. - New
/lore:search <query>— query the same scorer the recall hook uses, on demand.
Capture
captureNudgeconfig (always|smart|off, defaultsmart) — the smart mode skips the end-of-turn nudge on purely conversational turns (no tool use), so the LORE CHECK keeps its signal instead of becoming a mechanical reflex. Reads the transcript and fails open. Booleans work as shorthand (true→smart,false→off).- Post-compaction reminder (new
SessionStarthook) — right after context is compacted, a short lore reminder is re-injected — exactly the moment an uncaptured learning would otherwise die with the context. (PreCompactcan't inject context, so the post-compactSessionStartis the supported channel.) - Capture-time secret scan — the skill now scans the file it just wrote, so a leak is caught at write time, not just at the push boundary.
Health / freshness
/lore:lint --dupes— near-duplicate triage: entry pairs sharing title/tag vocabulary (e.g. two teammates capturing the same gotcha on parallel branches) plus category-name variants (CIvsci).- Richer
/lore:stats— now reports recall activity (most- and never-surfaced entries), the near-duplicate count, and a note when the project's.lore/hook copies fall behind the installed plugin version. - Faster drift triage —
--reportnow computes drift in a single streaminggit logpass instead of one subprocess per referenced file. .lore/VERSIONstamp —/lore:initstamps the hook copies so the linter can flag stale ones.
Robustness
_TEMPLATE.mdand_/.-prefixed files are no longer counted as entries (fixes the stats count and the generated index)..lore.jsonvalues are type-validated — a wrongly-typed key is ignored instead of crashing a hook.
Tests
- A 52-test stdlib
unittestsuite (scorer, ref matching, config, transcript-based nudge gating, secret scan, git drift) and a CI workflow running it on 3 OSes × Python 3.9/3.13.
⚠️ Behavior change
The end-of-turn capture nudge now defaults to smart (skips tool-less turns). To restore the previous every-turn behavior, set "captureNudge": "always" in a project's .lore.json.
Install / update: /plugin install lore@lore (auto-updates).
Lore 0.2.0
Four new capabilities — all hook-driven, staying in the lightweight recall/capture niche.
Recall
- Freshness flags inline — every surfaced learning now shows cheap staleness signals (a deleted-file ref, or "verified N months ago") so a possibly-stale entry is never trusted blindly. The costly git-drift check stays in
--report, out of the hook. - Edit-time recall (new
PreToolUsehook) — when the agent is about to edit a file, any learning whosefiles:names that path surfaces right then. The gotcha shows up exactly when you touch the code. Verified live in the VS Code extension.
Capture
- Deterministic overlap check — before writing, the skill runs
recall.py --queryto list already-related entries, so a new learning updates one instead of duplicating it.
Health / freshness
/lore:stats— store-health snapshot: counts by status/category, drift backlog, long-unverified entries, and a soft dangling-[[link]]count.- Richer
--index— a status summary line in the generated README. - Fixed two pre-existing
--reportcrashes (emptygit logoutput on untracked files; dict comparison on a gap tie).
Docs
- A "See it in action" demo in the README — an animated SVG rendered from the real hooks (
demo/make_cast.py), showing recall appearing on its own and theLORE CHECKclosing the turn.
Install / update: /plugin install lore@lore (auto-updates).
Lore v0.1.0
First tagged release of Lore — a self-maintaining knowledge store for Claude Code and OpenAI Codex: capture non-obvious learnings, recall the relevant ones into context, and keep them fresh.
Highlights
The loop
- Recall (
UserPromptSubmithook) surfaces relevant past learnings into context — matches title + tags only, never echoes bodies. - Capture (
Stophook +loreskill) records a learning when one is both non-obvious and reusable. - Freshness —
/lore:lintref-check + drift triage + index regen, with an optional pre-push hook.
Security hardening (this release)
- Blocking secret scan at the push boundary — the store is committed/pushed by default and capture is autonomous, so
scan_secrets.pyruns in the pre-push hook and aborts the push if a learning contains a likely key, token, private key, JWT, credential URL, or hardcoded secret. Redacted output, placeholder filtering, and allowlisting (lore:allow-secret,secretAllowin.lore.json,LORE_SCAN_BLOCK=0,--no-verify). - Capture-time prevention — the skill now references secrets instead of quoting them.
- New
/lore:scancommand to run the guard on demand. - README Security section documenting the posture, the recall anti-injection design, and pinning the plugin to a reviewed tag.
Install
Claude Code:
/plugin marketplace add aoc81/lore
/plugin install lore@lore
Then /lore:init per project. OpenAI Codex: python3 codex/install.py.
Everything is stdlib Python — no dependencies, no network calls.