Skip to content

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.

Installed by npx @plur-ai/mcp init or plur init. Writes to ~/.claude/settings.json (global) or .claude/settings.json (project).

Claude Code eventPLUR action
SessionStartCall plur_session_start with task description. Inject top-N engrams into the system prompt.
UserPromptSubmitScan the user message for correction patterns. Stage candidate engrams.
StopCall 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:

Terminal window
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" }]
}
}

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.

Remove the line from settings.json. To re-enable later, re-run plur init. The hook is idempotent — it won’t duplicate entries.

Installed by plur init --cursor (auto-detected when a .cursor/ directory exists in the project). Writes .cursor/mcp.json (with the reduced PLUR_TOOL_PROFILE=cursor tool profile), .cursor/hooks.json, and a static .cursor/rules/plur-memory.mdc rule.

Cursor eventHandlerPLUR action
sessionStartplur hook-cursor-session-startInject recalled engrams for the new conversation (BM25-only, deliberately — the hook must beat a cold-start embedder load). Writes the session sentinel.
preToolUseplur hook-cursor-guardSession guard — deny tool calls until plur_session_start has run. Mainly covers background/cloud agents, where sessionStart never fires.
postToolUseplur hook-cursor-post-toolMark the sentinel when plur_session_start was called; on a 10-minute cadence, write a plur_learn reminder.
stopplur hook-cursor-stopEvery 3rd completed stop, auto-submit a follow-up nudge to record learnings via plur_learn.

All four hooks are registered with failClosed: false — a hook failure never blocks Cursor.

One Cursor-specific quirk: Cursor’s hook-output additional_context field is dropped by a confirmed race condition (acknowledged by Cursor’s team, no fix ETA), so PLUR delivers recalled memory and reminders through dynamically rewritten rules files (.cursor/rules/plur-context.mdc, .cursor/rules/plur-reminder.mdc) instead of hook output. plur init --cursor gitignores both, since they can contain recalled engram content. plur doctor diagnoses the Cursor wiring, including whether the workspace is loading the reduced tool profile.

@plur-ai/claw implements seven hooks via OpenClaw’s ContextEngine plugin slot:

HookPLUR action
bootstrapOpen the local engram store; warm the index.
ingestScan 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.
afterTurnCapture the turn as an episode; check for learnings.
prepareSubagentSpawnHand the subagent a scoped engram bundle so it inherits memory.
onSubagentEndedMerge 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.

Three lifecycle hooks pick up automatically once plur-hermes is installed:

Hermes hookPLUR action
pre_llm_callInject relevant memories into the upcoming LLM call.
post_llm_callExtract corrections, preferences, and insights from the response.
on_session_endWrite 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: 3

What “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.yaml or 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.

If hooks aren’t firing:

Terminal window
plur doctor # full diagnostic
tail -f ~/.plur/episodes.yaml # watch for events
ls -la ~/.plur/bin/ # confirm hook binary present
cat ~/.claude/settings.json | jq '.hooks' # confirm registration

If injection is running but engrams aren’t appearing in context, check the embedder:

Terminal window
plur doctor

bge-small-en-v1.5 is the default embedder (~50 MB, 384-dim); the first hybrid call downloads it. Without it, hybrid recall falls back to BM25-only.