fix(mem0-plugin): wire pre-commit capture hook, add Setup hook, fix access counters
- Add PreToolUse(Bash) hook: detects `git commit` commands, fires on_pre_commit.py in background to capture staged changes as memory - Add Setup(init|maintenance) hook: installs mem0ai SDK on first plugin install via ensure_deps.sh (120s timeout for slow networks) - Add on_git_commit_capture.sh wrapper: reads tool input JSON, matches git commit/merge/rebase, runs capture script fire-and-forget - Fix #24: add metadata.last_accessed ISO timestamp to access counter instructions in mem0-mcp/SKILL.md alongside existing access_count
This commit is contained in:
@@ -1,5 +1,18 @@
|
|||||||
{
|
{
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
"Setup": [
|
||||||
|
{
|
||||||
|
"matcher": "init|maintenance",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/ensure_deps.sh",
|
||||||
|
"statusMessage": "Installing mem0 SDK...",
|
||||||
|
"timeout": 120
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"SessionStart": [
|
"SessionStart": [
|
||||||
{
|
{
|
||||||
"hooks": [
|
"hooks": [
|
||||||
@@ -31,6 +44,16 @@
|
|||||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/block_memory_write.sh"
|
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/block_memory_write.sh"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": "Bash",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_git_commit_capture.sh",
|
||||||
|
"timeout": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"PostToolUse": [
|
"PostToolUse": [
|
||||||
|
|||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Hook: PreToolUse (matcher: Bash)
|
||||||
|
#
|
||||||
|
# Detects `git commit` commands and fires on_pre_commit.py in the background
|
||||||
|
# to capture staged changes as a mem0 memory. Never blocks the commit.
|
||||||
|
#
|
||||||
|
# Input: JSON on stdin with tool_name, tool_input
|
||||||
|
# Output: none (always exit 0)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
INPUT=$(cat)
|
||||||
|
|
||||||
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [ -z "$COMMAND" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$COMMAND" in
|
||||||
|
*"git commit"*|*"git merge"*|*"git rebase"*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
if [ ! -x "$SCRIPT_DIR/on_pre_commit.py" ] && [ ! -f "$SCRIPT_DIR/on_pre_commit.py" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
API_KEY="${MEM0_API_KEY:-${CLAUDE_PLUGIN_OPTION_MEM0_API_KEY:-}}"
|
||||||
|
if [ -z "$API_KEY" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git diff --cached --stat 2>/dev/null | python3 "$SCRIPT_DIR/on_pre_commit.py" &
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -276,14 +276,20 @@ add_memory(
|
|||||||
|
|
||||||
### Access counter: track memory usage
|
### Access counter: track memory usage
|
||||||
|
|
||||||
When you retrieve a memory via `search_memories` and **actually use it** in your response (i.e., it informed your answer or you cited it), increment its access counter by calling:
|
When you retrieve a memory via `search_memories` and **actually use it** in your response (i.e., it informed your answer or you cited it), increment its access counter and update the last-accessed timestamp by calling:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 1. Read current state
|
# 1. Read current state
|
||||||
mem = get_memory(memory_id=<id>)
|
mem = get_memory(memory_id=<id>)
|
||||||
current_text = mem["content"] # or mem["memory"], depending on response shape
|
current_text = mem["content"] # or mem["memory"], depending on response shape
|
||||||
|
current_meta = mem.get("metadata", {})
|
||||||
|
|
||||||
# 2. Update with access_count bump
|
# 2. Bump access_count and set last_accessed
|
||||||
|
import datetime
|
||||||
|
current_meta["access_count"] = current_meta.get("access_count", 0) + 1
|
||||||
|
current_meta["last_accessed"] = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||||
|
|
||||||
|
# 3. Update with preserved content
|
||||||
update_memory(
|
update_memory(
|
||||||
memory_id=<id>,
|
memory_id=<id>,
|
||||||
data=current_text, # preserve original text — required parameter
|
data=current_text, # preserve original text — required parameter
|
||||||
@@ -294,4 +300,4 @@ update_memory(
|
|||||||
|
|
||||||
**When to increment:** Only when you actually used the memory to answer. Don't bump on every search hit — that inflates counts for memories that were returned but irrelevant. Aim for 1-3 bumps per response at most.
|
**When to increment:** Only when you actually used the memory to answer. Don't bump on every search hit — that inflates counts for memories that were returned but irrelevant. Aim for 1-3 bumps per response at most.
|
||||||
|
|
||||||
**Why:** Access counts feed into `/mem0:dream` pruning decisions. Memories that are never accessed after creation are candidates for cleanup. Frequently accessed memories are protected from pruning regardless of age.
|
**Why:** `access_count` and `last_accessed` feed into `/mem0:dream` pruning decisions. Memories that are never accessed after creation are candidates for cleanup. Frequently accessed memories are protected from pruning regardless of age.
|
||||||
|
|||||||
Reference in New Issue
Block a user