Mnemonic devices for agents.
Filesystem-scoped memory recall · Hook the right memories into the right context. 🎣
You can't recall what you don't know you know.
An agent can have the right memory stored perfectly and still fail to use it because retrieval begins with a cue. MemHooks stores that cue close to the code or folder where it matters.
Memory systems know how to remember. MemHooks tells the agent what to recall here.
When an agent works in a directory, MemHooks resolves MEMHOOKS.md root → leaf, applies inheritance and role routing, and produces a bounded retrieval plan for the memory backend the runtime actually uses.
workspace/
├── MEMHOOKS.md
├── backend/
│ ├── MEMHOOKS.md
│ └── auth/
│ ├── MEMHOOKS.md
│ └── refresh.py
root hook
↓
local hooks
↓
reference resolver
↓
backend-neutral retrieval intent + provider hints
↓
memory adapter
↓
bounded relevant context
↓
do the work
Normal users generally do not hand-maintain MEMHOOKS.md.
- User: installs/enables MemHooks and may configure a memory backend.
- Agent/runtime: creates, updates, prunes, and consumes local retrieval cues.
- MemHooks reference engine: parses, validates, resolves, explains and maintains the hook frontmatter.
- Memory backend: stores and retrieves the actual memories.
There is deliberately one structured data path in v0.6.0:
agent/runtime
↓
memhooks note / memhooks event
↓
YAML frontmatter
↓
reference parser + resolver
↓
memhooks explain / Rust API
↓
adapter
The Markdown body is optional source-attributed retrieval guidance. It is not a second hidden routing database.
memhooks/v2 separates universal retrieval intent from provider-native controls.
The core understands only provider-neutral concepts:
recall_queries- query
priority when.rolesentitiesresourcestagsexcludescopesensitivity- filesystem inheritance
Provider-native controls live under:
backends:
<provider>:
... provider-owned configuration ...MemHooks preserves and structurally merges provider mappings but deliberately does not interpret their internal keys.
---
schema: memhooks/v2
inherits: true
scope: backend/auth
recall_queries:
- query: "Why did the authentication design change after the outage?"
priority: 1.0
when:
roles: [reviewer, architect]
entities:
- Authentication
resources:
- name: auth-postmortem
kind: postmortem
salience: 0.9
tags: [security]
backends:
mem0:
top_k: 8
rerank: true
hindsight:
memory_types: [experience]
strategy: reflect
entities:
- name: Authentication
type: CONCEPT
salience: 0.95
exclude:
- obsolete OAuth prototype
backends:
mem0:
filters:
user_id: auth-agent
hindsight:
bank: project-memory
---Here priority, roles, entities, resources, tags, and exclusions are MemHooks concepts. Mem0 filters/reranking and Hindsight banks/types/Reflect are provider-owned hints.
First-class adapter references are included for:
The provider references ship inside the crates.io package as of v0.5.1.
cargo install memhooksFor an exact release:
cargo install memhooks --version 0.6.0Rust runtimes can use the same engine directly:
cargo add memhooks@0.6.0memhooks init .memhooks validate --all
memhooks validate --all --format json
memhooks validate --all --format sarifA nonexistent target is an error rather than a misleading empty success.
memhooks explain backend/auth
memhooks explain backend/auth --role reviewer
memhooks explain backend/auth --format jsonThe JSON handoff serializes the resolved structure rather than a manually duplicated field list. It includes source-attributed Markdown guidance as well as core/provider routing.
memhooks note \
--cwd "$PWD" \
--query "Why did authentication change after the outage?" \
--priority 0.9 \
--role reviewer \
--entity '{"name":"Authentication","type":"CONCEPT","salience":0.95}' \
--resource '{"name":"auth-postmortem","kind":"postmortem","salience":0.9}' \
--tag security \
--backends '{"mem0":{"top_k":8}}'The cue is written directly into YAML frontmatter and is immediately visible through memhooks explain and the Rust resolver.
Runtime adapters can pipe one post_tool_call JSON event into:
memhooks eventAutomatic anchors only inspect explicit path-bearing tool-input fields and only retain files that actually exist inside the canonical project root. The maintainer does not regex arbitrary file contents for dotted strings.
The file schema stays memhooks/v2; the machine handoff is now explicitly
versioned as memhooks/plan-v1. Update the Rust CLI and Hermes adapter together.
- All filesystem entry points share fallible canonical path/root resolution,
including default
.invocations from nested directories. - Semantic validation is required before resolution or persistence. Invalid role routing, priorities, entities and backend namespaces fail with diagnostics; unknown extension fields remain round-trippable warnings.
- Hook and lock symlinks/special files are rejected. Initialization rechecks file state under the same exclusive lock as creation, preserving concurrent notes.
MEMHOOKS_MAX_CHARScaps the complete emitted context, including its wrapper. Truncation preserves valid JSON and never retains queries without their routing constraints. Invalid optional-hook input/configuration produces{}plus a diagnostic.- Hosts can supply session-local
active_filesandactive_roles. The loader resolves at most eight relevant directory scopes under one shared timeout, rather than loading every hook in the project. Sibling routing controls remain separate. - New/refreshed automatic cues use directory-qualified identities. Explicit
delete/rename file events reconcile local generated resources;
prunecatches stale cues from changes made outside those events.
memhooks prune --all --dry-run
memhooks prune --all
memhooks remove --query "An obsolete local cue" --dry-run
memhooks remove --query "An obsolete local cue"These commands only edit retrieval cues, never backend memories. Dry runs do not
change hook files or create locks. explain --format json reports role,
inheritance-cut and override omissions; the bounded handoff reports budget omissions.
See runtime contract and migration.
v0.5.1 established the shared implementation; the protocol remains memhooks/v2.
Notable changes:
- one normative data model — the reference maintainer writes the same YAML frontmatter the resolver reads;
- complete resolver handoff —
guidanceis no longer dropped byexplain; - canonical root semantics — a Git root is a hard boundary unless an explicit containing
MEMHOOKS_ROOTis supplied; - local query override — same trimmed query text at a child scope replaces parent query metadata instead of issuing the query twice;
- precise diagnostics — YAML AST spans locate the actual malformed
priority,salience, etc.; - tolerant linting — malformed structured entries such as
quer:reach targeted diagnostics rather than killing the whole file as an enum parse error; - trust-boundary hardening — the Hermes adapter consumes validated resolver JSON and injects it explicitly as untrusted repository-controlled data; raw BEGIN/END fences are gone;
- atomic maintenance — exclusive lock + same-directory atomic replacement;
- SARIF hardening — repository-relative artifact paths and rule metadata;
- reproducible builds — committed
Cargo.lock, declared MSRV CI, doctests, Python 3.10/3.12, Ruff, and locked Rust builds; - maintained YAML stack — deprecated
serde_yamlreplaced withserde_yaml_ng, with Saphyr used for source spans; .gitignore,SECURITY.md,CONTRIBUTING.md, corrected funding metadata, and a rootMEMHOOKS.mdfor dogfooding.
Resolver, maintainer, and bundled runtime adapters use the same rules:
- an explicit containing
MEMHOOKS_ROOTwins; - otherwise the nearest Git root is a hard boundary;
- outside Git, the highest hooked ancestor may serve as root.
A stray hook above a Git repository cannot silently capture maintenance writes for that repository.
For same-text queries, locality behaves like an override:
# root
recall_queries:
- query: "What security invariant matters here?"
priority: 0.2# child
recall_queries:
- query: "What security invariant matters here?"
priority: 0.9The resolved plan contains the query once, at priority 0.9, sourced from the child hook.
Other generic list cues accumulate with duplicate suppression; more-local scalar core fields win. Provider mappings use recursive mapping merge with local scalar/list replacement.
Install the reference binary first:
cargo install memhooksClone the skill and install the pre-LLM adapter:
git clone https://github.com/AronAxe/MemHooks.git ~/.hermes/skills/memhooks
mkdir -p ~/.hermes/agent-hooks
cp ~/.hermes/skills/memhooks/hooks/hermes/memhooks_pre_llm.py ~/.hermes/agent-hooks/Then configure Hermes:
hooks:
pre_llm_call:
- command: "python3 ~/.hermes/agent-hooks/memhooks_pre_llm.py"
timeout: 5
post_tool_call:
- command: "memhooks event"
timeout: 5The pre-LLM adapter does not parse YAML itself. It asks memhooks explain --format json for one validated routing plan and injects structurally bounded JSON marked as untrusted repository-controlled retrieval metadata.
The legacy scripts/memhooks_update.py remains only as a compatibility launcher to the Rust CLI; it no longer owns parsing or persistence.
- Human-friendly Wiki source — approachable, cross-linked explanation intended for the GitHub Wiki tab
- Documentation index
- Quickstart
- CLI reference
- Rust library/API guide
- Agent integration guide
- Protocol guide
- Troubleshooting
- Security policy
- Contributing
The normative protocol contract is references/memhooks-format.md.
MemHooks is not a vector database, memory provider, automatic memory-writing system, GraphRAG framework, prompt-privilege mechanism, shell-command manifest, or background daemon.
It is deliberately narrow infrastructure:
When an agent works here, remember these things first.
If MemHooks is about retrieving the right context, Token Terminator is about not wasting tokens on the wrong context.
v0.6.0 — validated routing, consistent project boundaries, active-file recall, bounded context, and generated-cue lifecycle. Protocol: memhooks/v2.
MIT © 2026 Aron Bijl

