Files
claude-mem/cursor-hooks/save-file-edit.sh
T
Alex Newman 8d485890b9 feat(cursor): Add Claude-Mem Cursor hooks installation and management
- 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.
2025-12-29 20:14:23 -05:00

113 lines
2.9 KiB
Bash
Executable File

#!/bin/bash
# Save File Edit Hook for Cursor
# Captures file edits made by the agent
# Maps file edits to claude-mem observations
# 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" "")
file_path=$(json_get "$input" "file_path" "")
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
# Exit if no file_path
if is_empty "$file_path"; then
exit 0
fi
# Use conversation_id as session_id, 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)
# Extract edits array, defaulting to [] if invalid
edits=$(echo "$input" | jq -c '.edits // []' 2>/dev/null || echo "[]")
# Validate edits is a valid JSON array
if ! echo "$edits" | jq 'type == "array"' 2>/dev/null | grep -q true; then
edits="[]"
fi
# Exit if no edits
if [ "$edits" = "[]" ] || is_empty "$edits"; then
exit 0
fi
# Create a summary of the edits for the observation (with error handling)
edit_summary=$(echo "$edits" | jq -r '[.[] | "\(.old_string[0:50] // "")... → \(.new_string[0:50] // "")..."] | join("; ")' 2>/dev/null || echo "File edited")
# Treat file edits as a "write_file" tool usage
tool_input=$(jq -n \
--arg path "$file_path" \
--argjson edits "$edits" \
'{
file_path: $path,
edits: $edits
}' 2>/dev/null || echo '{}')
tool_response=$(jq -n \
--arg summary "$edit_summary" \
'{
success: true,
summary: $summary
}' 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: "write_file",
tool_input: $toolInput,
tool_response: $toolResponse,
cwd: $cwd
}' 2>/dev/null)
# 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