#!/bin/bash # # Claude Code Hook: task-complete (v2 - with offline support) # Runs AFTER a task is completed # Saves conversation context to the database for future recall # FALLBACK: Queues locally when API is unavailable, syncs later # # Expected environment variables: # CLAUDE_PROJECT_ID - UUID of the current project # JWT_TOKEN - Authentication token for API # CLAUDE_API_URL - API base URL (default: http://172.16.3.30:8001) # CONTEXT_RECALL_ENABLED - Set to "false" to disable (default: true) # TASK_SUMMARY - Summary of completed task (auto-generated by Claude) # TASK_FILES - Files modified during task (comma-separated) # # Load configuration if exists CONFIG_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/context-recall-config.env" if [ -f "$CONFIG_FILE" ]; then source "$CONFIG_FILE" fi # Default values API_URL="${CLAUDE_API_URL:-http://172.16.3.30:8001}" ENABLED="${CONTEXT_RECALL_ENABLED:-true}" # Local storage paths CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" QUEUE_DIR="$CLAUDE_DIR/context-queue" PENDING_DIR="$QUEUE_DIR/pending" UPLOADED_DIR="$QUEUE_DIR/uploaded" # Exit early if disabled if [ "$ENABLED" != "true" ]; then exit 0 fi # Detect project ID (same logic as user-prompt-submit) if [ -z "$CLAUDE_PROJECT_ID" ]; then PROJECT_ID=$(git config --local claude.projectid 2>/dev/null) if [ -z "$PROJECT_ID" ]; then GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null) if [ -n "$GIT_REMOTE" ]; then PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1) fi fi else PROJECT_ID="$CLAUDE_PROJECT_ID" fi # Exit if no project ID if [ -z "$PROJECT_ID" ]; then exit 0 fi # Create queue directories if they don't exist mkdir -p "$PENDING_DIR" "$UPLOADED_DIR" 2>/dev/null # Gather task information TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") TIMESTAMP_FILENAME=$(date -u +"%Y%m%d_%H%M%S") GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "none") # Get recent git changes CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null | head -10 | tr '\n' ',' | sed 's/,$//') if [ -z "$CHANGED_FILES" ]; then CHANGED_FILES="${TASK_FILES:-}" fi # Create task summary if [ -z "$TASK_SUMMARY" ]; then # Generate basic summary from git log if no summary provided TASK_SUMMARY=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "Task completed") fi # Build context payload CONTEXT_TITLE="Session: ${TIMESTAMP}" CONTEXT_TYPE="session_summary" RELEVANCE_SCORE=7.0 # Create dense summary DENSE_SUMMARY="Task completed on branch '${GIT_BRANCH}' (commit: ${GIT_COMMIT}). Summary: ${TASK_SUMMARY} Modified files: ${CHANGED_FILES:-none} Timestamp: ${TIMESTAMP}" # Escape JSON strings escape_json() { echo "$1" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read())[1:-1])" } ESCAPED_TITLE=$(escape_json "$CONTEXT_TITLE") ESCAPED_SUMMARY=$(escape_json "$DENSE_SUMMARY") # Save context to database CONTEXT_PAYLOAD=$(cat </dev/null) HTTP_CODE=$(echo "$RESPONSE" | tail -n1) RESPONSE_BODY=$(echo "$RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then API_SUCCESS=true # Also update project state curl -s --max-time 5 \ -X POST "${API_URL}/api/project-states" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -H "Content-Type: application/json" \ -d "$PROJECT_STATE_PAYLOAD" 2>/dev/null >/dev/null fi fi # If API call failed, queue locally if [ "$API_SUCCESS" = "false" ]; then # Save context to pending queue QUEUE_FILE="$PENDING_DIR/${PROJECT_ID}_${TIMESTAMP_FILENAME}_context.json" echo "$CONTEXT_PAYLOAD" > "$QUEUE_FILE" # Save project state to pending queue STATE_QUEUE_FILE="$PENDING_DIR/${PROJECT_ID}_${TIMESTAMP_FILENAME}_state.json" echo "$PROJECT_STATE_PAYLOAD" > "$STATE_QUEUE_FILE" echo "[WARNING] Context queued locally (API unavailable) - will sync when online" >&2 # Try to sync in background (opportunistic) if [ -n "$JWT_TOKEN" ]; then bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 & fi else echo "[OK] Context saved to database" >&2 # Trigger background sync of any queued items if [ -n "$JWT_TOKEN" ]; then bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 & fi fi exit 0