1ea8c2e5fe
Observed live: real sessions flagged durable candidates (a preference, two insights, a convention) but every session showed 'flush events: 0'. The manifest backgrounded flush and assist-error with '( cmd &)', so the parent exited and closed stdin before the child could read it -- the child then had no session_id, fell back to 'no-session', and drained an empty buffer. Now the CLI reads the payload itself and re-execs a detached worker with start_new_session, so the hook still returns in ~0.27s. Verified end to end: sent=1, session_state=created. Also records a surface hint per event and shows it in 'sessions', since the manifest pins MEM0_EDITOR to the same value on every client. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
136 lines
5.2 KiB
YAML
136 lines
5.2 KiB
YAML
# "${CLAUDE_PLUGIN_ROOT}/bin/mem0-agent" hook wiring -- the single source of truth.
|
|
#
|
|
# v1 hand-maintained four editor dialects (Claude Code, Cursor, Codex, Antigravity).
|
|
# They drifted: an event existed in one file and not another, timeouts disagreed, and
|
|
# a hook was silently non-functional in two editors for months. Nothing here is edited
|
|
# per editor. Run `python3 hooks/generate.py` to emit the manifests into hooks/generated/,
|
|
# and `python3 hooks/generate.py --check` in CI to fail the build when they drift.
|
|
#
|
|
# Field reference for `hooks:` entries
|
|
# event editor lifecycle event the hook binds to
|
|
# matcher regex the editor matches against the event subject (null = all)
|
|
# command shell command; {placeholders} are filled per editor
|
|
# timeout seconds the editor waits before killing the hook
|
|
# background true = detach and return immediately; output is discarded
|
|
# blocking true = the editor waits for stdout and may inject it into the session
|
|
# local_only true = the hook MUST NOT touch the network (enforced via MEM0_LOCAL_ONLY=1)
|
|
# status_message spinner text while a blocking hook runs (omit for silent hooks)
|
|
# why one line: why this hook exists at all
|
|
|
|
version: 1
|
|
policy: "v2"
|
|
|
|
defaults:
|
|
timeout: 10
|
|
background: false
|
|
blocking: false
|
|
local_only: false
|
|
|
|
editors:
|
|
- id: claude-code
|
|
supported: true
|
|
output: "hooks.json" # Claude Code auto-discovers <plugin>/hooks/hooks.json
|
|
dialect: "claude-code"
|
|
env:
|
|
MEM0_EDITOR: "claude-code"
|
|
placeholders:
|
|
session_id: "$CLAUDE_SESSION_ID"
|
|
why: "Reference target. Nested schema: hooks -> Event -> [{matcher, hooks:[{type,command,timeout}]}]."
|
|
|
|
- id: cursor
|
|
supported: false
|
|
output: "generated/cursor.hooks.json"
|
|
dialect: "flat-camel"
|
|
env:
|
|
MEM0_EDITOR: "cursor"
|
|
placeholders:
|
|
session_id: "$CURSOR_SESSION_ID"
|
|
why: "Flat camelCase events (sessionStart) with no inner hooks array, and no SessionEnd/PreCompact equivalent. Declared so the gap is visible; unsupported until the dialect is verified end to end."
|
|
|
|
- id: codex
|
|
supported: false
|
|
output: "generated/codex.hooks.json"
|
|
dialect: "claude-code"
|
|
env:
|
|
MEM0_EDITOR: "codex"
|
|
placeholders:
|
|
session_id: "$CODEX_SESSION_ID"
|
|
why: "Same nested schema as Claude Code but hooks are installed into ~/.codex by a script rather than discovered from the package. Unsupported until that installer is rewritten for v2."
|
|
|
|
- id: antigravity
|
|
supported: false
|
|
output: "generated/antigravity.hooks.json"
|
|
dialect: "claude-code"
|
|
env:
|
|
MEM0_EDITOR: "antigravity"
|
|
placeholders:
|
|
session_id: "$ANTIGRAVITY_SESSION_ID"
|
|
why: "Ships no stable hook contract yet. Declared so it is never re-added as a hand-written fifth file."
|
|
|
|
hooks:
|
|
- id: session-context
|
|
event: "SessionStart"
|
|
matcher: "startup|resume|compact"
|
|
command: "${CLAUDE_PLUGIN_ROOT}/bin/mem0-agent context --session-id {session_id}"
|
|
timeout: 10
|
|
background: false
|
|
blocking: true
|
|
local_only: false
|
|
status_message: "Loading mem0 context..."
|
|
why: "The one retrieval per session. Injects durable project knowledge plus the open-thread snapshot so the assistant starts where the last session ended."
|
|
|
|
- id: prompt-observe
|
|
event: "UserPromptSubmit"
|
|
matcher: null
|
|
command: "${CLAUDE_PLUGIN_ROOT}/bin/mem0-agent observe --source prompt"
|
|
timeout: 3
|
|
background: false
|
|
blocking: true
|
|
local_only: true
|
|
status_message: null
|
|
why: "Runs on the hot path of every keystroke-to-response, so it is local-only by contract: it scores the prompt for capture triggers and drains the pending assist buffer. v1 made a network search here and added latency to every single turn."
|
|
|
|
- id: bash-assist-error
|
|
event: "PostToolUse"
|
|
matcher: "Bash"
|
|
command: "${CLAUDE_PLUGIN_ROOT}/bin/mem0-agent assist-error"
|
|
timeout: 5
|
|
background: false
|
|
blocking: false
|
|
local_only: false
|
|
status_message: null
|
|
why: "A failing command is the highest-value retrieval moment. Detached so a lookup never stalls the tool loop; any hit is written to the session buffer and surfaced by the next UserPromptSubmit."
|
|
|
|
- id: stop-flush
|
|
event: "Stop"
|
|
matcher: null
|
|
command: "${CLAUDE_PLUGIN_ROOT}/bin/mem0-agent flush"
|
|
timeout: 5
|
|
background: false
|
|
blocking: false
|
|
local_only: false
|
|
status_message: null
|
|
why: "Turn boundary is the only place a candidate can be judged against how the turn actually ended. Batched so one turn costs at most one write."
|
|
|
|
- id: precompact-flush
|
|
event: "PreCompact"
|
|
matcher: null
|
|
command: "${CLAUDE_PLUGIN_ROOT}/bin/mem0-agent flush --reason precompact"
|
|
timeout: 5
|
|
background: false
|
|
blocking: false
|
|
local_only: false
|
|
status_message: null
|
|
why: "Compaction is where context dies. Flush the candidate buffer before it is summarized away."
|
|
|
|
- id: sessionend-flush
|
|
event: "SessionEnd"
|
|
matcher: null
|
|
command: "${CLAUDE_PLUGIN_ROOT}/bin/mem0-agent flush --reason end"
|
|
timeout: 5
|
|
background: false
|
|
blocking: false
|
|
local_only: false
|
|
status_message: null
|
|
why: "Last chance to persist the open-thread snapshot. Backgrounded so quitting the editor is never delayed by a network write."
|