mirror of
https://github.com/thedotmack/claude-mem
synced 2026-04-25 17:15:04 +02:00
- Introduced functionality for installing, uninstalling, and checking the status of Cursor hooks. - Added a new command structure for managing hooks with detailed usage instructions. - Implemented a method to locate the cursor-hooks directory across different environments. - Updated build-hooks script to inform users about the location of Cursor hooks. This enhancement streamlines the integration of Claude-Mem with Cursor, improving user experience and accessibility of hooks.
130 lines
3.5 KiB
Bash
Executable File
130 lines
3.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Save Observation Hook for Cursor
|
|
# Captures MCP tool usage and shell command execution
|
|
# Maps to claude-mem's save-hook functionality
|
|
|
|
# Source common utilities
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "${SCRIPT_DIR}/common.sh" 2>/dev/null || {
|
|
echo "Warning: common.sh not found, using fallback functions" >&2
|
|
}
|
|
|
|
# Check dependencies (non-blocking)
|
|
check_dependencies >/dev/null 2>&1 || true
|
|
|
|
# Read JSON input from stdin with error handling
|
|
input=$(read_json_input)
|
|
|
|
# Extract common fields with safe fallbacks
|
|
conversation_id=$(json_get "$input" "conversation_id" "")
|
|
generation_id=$(json_get "$input" "generation_id" "")
|
|
workspace_root=$(json_get "$input" "workspace_roots[0]" "")
|
|
|
|
# Fallback to current directory if no workspace root
|
|
if is_empty "$workspace_root"; then
|
|
workspace_root=$(pwd)
|
|
fi
|
|
|
|
# Use conversation_id as session_id (stable across turns), fallback to generation_id
|
|
session_id="$conversation_id"
|
|
if is_empty "$session_id"; then
|
|
session_id="$generation_id"
|
|
fi
|
|
|
|
# Exit if no session_id available
|
|
if is_empty "$session_id"; then
|
|
exit 0
|
|
fi
|
|
|
|
# Get worker port from settings with validation
|
|
worker_port=$(get_worker_port)
|
|
|
|
# Determine hook type and extract relevant data
|
|
hook_event=$(json_get "$input" "hook_event_name" "")
|
|
|
|
if [ "$hook_event" = "afterMCPExecution" ]; then
|
|
# MCP tool execution
|
|
tool_name=$(json_get "$input" "tool_name" "")
|
|
|
|
if is_empty "$tool_name"; then
|
|
exit 0
|
|
fi
|
|
|
|
# Extract tool_input and tool_response, defaulting to {} if invalid
|
|
tool_input=$(echo "$input" | jq -c '.tool_input // {}' 2>/dev/null || echo "{}")
|
|
tool_response=$(echo "$input" | jq -c '.result_json // {}' 2>/dev/null || echo "{}")
|
|
|
|
# Validate JSON
|
|
if ! echo "$tool_input" | jq empty 2>/dev/null; then
|
|
tool_input="{}"
|
|
fi
|
|
if ! echo "$tool_response" | jq empty 2>/dev/null; then
|
|
tool_response="{}"
|
|
fi
|
|
|
|
# Prepare observation payload
|
|
payload=$(jq -n \
|
|
--arg sessionId "$session_id" \
|
|
--arg toolName "$tool_name" \
|
|
--argjson toolInput "$tool_input" \
|
|
--argjson toolResponse "$tool_response" \
|
|
--arg cwd "$workspace_root" \
|
|
'{
|
|
contentSessionId: $sessionId,
|
|
tool_name: $toolName,
|
|
tool_input: $toolInput,
|
|
tool_response: $toolResponse,
|
|
cwd: $cwd
|
|
}' 2>/dev/null)
|
|
|
|
elif [ "$hook_event" = "afterShellExecution" ]; then
|
|
# Shell command execution
|
|
command=$(json_get "$input" "command" "")
|
|
|
|
if is_empty "$command"; then
|
|
exit 0
|
|
fi
|
|
|
|
output=$(json_get "$input" "output" "")
|
|
|
|
# Treat shell commands as "Bash" tool usage
|
|
tool_input=$(jq -n --arg cmd "$command" '{command: $cmd}' 2>/dev/null || echo '{}')
|
|
tool_response=$(jq -n --arg out "$output" '{output: $out}' 2>/dev/null || echo '{}')
|
|
|
|
payload=$(jq -n \
|
|
--arg sessionId "$session_id" \
|
|
--arg cwd "$workspace_root" \
|
|
--argjson toolInput "$tool_input" \
|
|
--argjson toolResponse "$tool_response" \
|
|
'{
|
|
contentSessionId: $sessionId,
|
|
tool_name: "Bash",
|
|
tool_input: $toolInput,
|
|
tool_response: $toolResponse,
|
|
cwd: $cwd
|
|
}' 2>/dev/null)
|
|
else
|
|
exit 0
|
|
fi
|
|
|
|
# Exit if payload creation failed
|
|
if [ -z "$payload" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# Ensure worker is running (with retries like claude-mem hooks)
|
|
if ! ensure_worker_running "$worker_port"; then
|
|
# Worker not ready - exit gracefully (don't block Cursor)
|
|
exit 0
|
|
fi
|
|
|
|
# Send observation to claude-mem worker (fire-and-forget)
|
|
curl -s -X POST \
|
|
"http://127.0.0.1:${worker_port}/api/sessions/observations" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$payload" \
|
|
>/dev/null 2>&1 || true
|
|
|
|
exit 0
|
|
|