Hooks
Hooks are the secret sauce: they let PLUR run without the agent ever calling a tool. The agent walks into a conversation with relevant engrams already in context; it walks out with new engrams already captured. No prompt nudging, no manual saves.
Different runtimes have different hook surfaces. Here’s what PLUR uses in each.
Claude Code
Section titled “Claude Code”Installed by npx @plur-ai/mcp init or plur init. Writes to ~/.claude/settings.json (global) or .claude/settings.json (project).
| Claude Code event | PLUR action |
|---|---|
SessionStart | Call plur_session_start with task description. Inject top-N engrams into the system prompt. |
UserPromptSubmit | Scan the user message for correction patterns. Stage candidate engrams. |
Stop | Call plur_session_end. Write the episode, promote stable candidates. |
The hook binary itself is ~/.plur/bin/plur-claude-hook, installed by plur init. To inspect what’s in settings.json:
cat ~/.claude/settings.json | jq '.hooks'Expected shape:
{ "hooks": { "SessionStart": [{ "type": "command", "command": "~/.plur/bin/plur-claude-hook session-start" }], "UserPromptSubmit": [{ "type": "command", "command": "~/.plur/bin/plur-claude-hook prompt" }], "Stop": [{ "type": "command", "command": "~/.plur/bin/plur-claude-hook session-end" }] }}Project-level scoping
Section titled “Project-level scoping”When .plur.yaml is present in the project root, hooks read it on session start and tag every engram captured during that session with the configured scope and domain. Without .plur.yaml, engrams default to scope: global.
Disabling a hook
Section titled “Disabling a hook”Remove the line from settings.json. To re-enable later, re-run plur init. The hook is idempotent — it won’t duplicate entries.
OpenClaw
Section titled “OpenClaw”@plur-ai/claw implements seven hooks via OpenClaw’s ContextEngine plugin slot:
| Hook | PLUR action |
|---|---|
bootstrap | Open the local engram store; warm the index. |
ingest | Scan incoming content for correction patterns; stage candidates. |
assemble (required) | Inject relevant engrams into the assembled system prompt. |
compact (required) | Prioritise pinned/locked engrams when context fills; spill the rest. |
afterTurn | Capture the turn as an episode; check for learnings. |
prepareSubagentSpawn | Hand the subagent a scoped engram bundle so it inherits memory. |
onSubagentEnded | Merge subagent learnings back into the parent store. |
Most ContextEngine plugins implement only assemble and compact and miss the loop. PLUR’s claw plugin implements all seven — that’s what makes the integration “deep”. See OpenClaw adapter.
Hermes (Python)
Section titled “Hermes (Python)”Three lifecycle hooks pick up automatically once plur-hermes is installed:
| Hermes hook | PLUR action |
|---|---|
pre_llm_call | Inject relevant memories into the upcoming LLM call. |
post_llm_call | Extract corrections, preferences, and insights from the response. |
on_session_end | Write the episode timeline. Promote stable candidates. |
These mirror the OpenClaw triad of assemble / afterTurn / lifecycle-end. See Hermes adapter.
What “automatic capture” actually does
Section titled “What “automatic capture” actually does”The capture step (UserPromptSubmit / ingest / post_llm_call) scans for these patterns by default:
- “no, actually …”
- “from now on …”
- “the way we do X here is …”
- “don’t … instead …”
- “always …” / “never …”
- “correction:” / “clarification:”
Pattern matches stage candidate engrams — not active ones. Candidates need to recur (default 3 times) or be explicitly promoted before they enter the injection pool. This avoids polluting recall with one-off frustrations.
You can customise patterns in ~/.plur/config.yaml:
capture: correction_patterns: - default - "(?i)never\\s+do\\s+" - "(?i)always\\s+prefer\\s+" auto_promote_after: 3What “automatic injection” actually does
Section titled “What “automatic injection” actually does”On session start (or assemble / pre_llm_call), the hook calls plur_inject with:
- The task description (if available — Claude Code passes the first user message).
- The current scope (read from
.plur.yamlor session config). - A budget (default: 12 engrams, ~2 KB of system prompt).
The returned engrams are prepended to the system prompt as a structured block:
## DIRECTIVES[ENG-2026-0325-021] ...
## CONSTRAINTS[ENG-2026-0503-061] ...
## ALSO CONSIDER[ENG-2026-0101-019] ...This is the format every PLUR-aware agent recognises. The same pattern works in Claude Code, OpenClaw, Hermes, and custom adapters.
Debugging hook failures
Section titled “Debugging hook failures”If hooks aren’t firing:
plur doctor # full diagnostictail -f ~/.plur/episodes.jsonl # watch for eventsls -la ~/.plur/bin/ # confirm hook binary presentcat ~/.claude/settings.json | jq '.hooks' # confirm registrationIf plur_inject is being called but engrams aren’t appearing in context, check the embedder:
plur status | grep -i embedbge-m3 is the default; first-run download is ~500 MB. Without it, hybrid recall falls back to BM25-only.