Files
claudetools/.claude/hooks/user-prompt-submit-v2
Mike Swanson fce1345a40 [Fix] Remove all emoji violations from code files
- Replaced emojis with ASCII text markers ([OK], [ERROR], [WARNING], etc.)
- Fixed 38+ violations across 20 files (7 Python, 6 shell scripts, 6 hooks, 1 API)
- All modified files pass syntax verification
- Conforms to CODING_GUIDELINES.md NO EMOJIS rule

Details:
- Python test files: check_record_counts.py, test_*.py (31 fixes)
- API utils: context_compression.py regex pattern updated
- Shell scripts: setup/test/install/upgrade scripts (64+ fixes)
- Hook scripts: task-complete, user-prompt-submit, sync-contexts (10 fixes)

Verification: All files pass syntax checks (python -m py_compile, bash -n)
Report: FIXES_APPLIED.md contains complete change log

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 13:06:33 -07:00

163 lines
5.3 KiB
Bash

#!/bin/bash
#
# Claude Code Hook: user-prompt-submit (v2 - with offline support)
# Runs BEFORE each user message is processed
# Injects relevant context from the database into the conversation
# FALLBACK: Uses local cache when API is unavailable
#
# 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)
# MIN_RELEVANCE_SCORE - Minimum score for context (default: 5.0)
# MAX_CONTEXTS - Maximum number of contexts to retrieve (default: 10)
#
# 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}"
MIN_SCORE="${MIN_RELEVANCE_SCORE:-5.0}"
MAX_ITEMS="${MAX_CONTEXTS:-10}"
# Local storage paths
CLAUDE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CACHE_DIR="$CLAUDE_DIR/context-cache"
QUEUE_DIR="$CLAUDE_DIR/context-queue"
# Exit early if disabled
if [ "$ENABLED" != "true" ]; then
exit 0
fi
# Detect project ID from git repo if not set
if [ -z "$CLAUDE_PROJECT_ID" ]; then
# Try to get from git config
PROJECT_ID=$(git config --local claude.projectid 2>/dev/null)
if [ -z "$PROJECT_ID" ]; then
# Try to derive from git remote URL
GIT_REMOTE=$(git config --get remote.origin.url 2>/dev/null)
if [ -n "$GIT_REMOTE" ]; then
# Hash the remote URL to create a consistent ID
PROJECT_ID=$(echo -n "$GIT_REMOTE" | md5sum | cut -d' ' -f1)
fi
fi
else
PROJECT_ID="$CLAUDE_PROJECT_ID"
fi
# Exit if no project ID available
if [ -z "$PROJECT_ID" ]; then
# Silent exit - no context available
exit 0
fi
# Create cache directory if it doesn't exist
PROJECT_CACHE_DIR="$CACHE_DIR/$PROJECT_ID"
mkdir -p "$PROJECT_CACHE_DIR" 2>/dev/null
# Try to sync any queued contexts first (opportunistic)
if [ -d "$QUEUE_DIR/pending" ] && [ -n "$JWT_TOKEN" ]; then
bash "$(dirname "${BASH_SOURCE[0]}")/sync-contexts" >/dev/null 2>&1 &
fi
# Build API request URL
RECALL_URL="${API_URL}/api/conversation-contexts/recall"
QUERY_PARAMS="project_id=${PROJECT_ID}&limit=${MAX_ITEMS}&min_relevance_score=${MIN_SCORE}"
# Try to fetch context from API (with timeout and error handling)
API_AVAILABLE=false
if [ -n "$JWT_TOKEN" ]; then
CONTEXT_RESPONSE=$(curl -s --max-time 3 \
"${RECALL_URL}?${QUERY_PARAMS}" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Accept: application/json" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$CONTEXT_RESPONSE" ]; then
# Check if response is valid JSON (not an error)
echo "$CONTEXT_RESPONSE" | python3 -c "import sys, json; json.load(sys.stdin)" 2>/dev/null
if [ $? -eq 0 ]; then
API_AVAILABLE=true
# Save to cache for offline use
echo "$CONTEXT_RESPONSE" > "$PROJECT_CACHE_DIR/latest.json"
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" > "$PROJECT_CACHE_DIR/last_updated"
fi
fi
fi
# Fallback to local cache if API unavailable
if [ "$API_AVAILABLE" = "false" ]; then
if [ -f "$PROJECT_CACHE_DIR/latest.json" ]; then
CONTEXT_RESPONSE=$(cat "$PROJECT_CACHE_DIR/latest.json")
CACHE_AGE="unknown"
if [ -f "$PROJECT_CACHE_DIR/last_updated" ]; then
CACHE_AGE=$(cat "$PROJECT_CACHE_DIR/last_updated")
fi
echo "<!-- Using cached context (API unavailable) - Last updated: $CACHE_AGE -->" >&2
else
# No cache available, exit silently
exit 0
fi
fi
# Parse and format context
CONTEXT_COUNT=$(echo "$CONTEXT_RESPONSE" | grep -o '"id"' | wc -l)
if [ "$CONTEXT_COUNT" -gt 0 ]; then
if [ "$API_AVAILABLE" = "true" ]; then
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) from API -->"
else
echo "<!-- Context Recall: Retrieved $CONTEXT_COUNT relevant context(s) from LOCAL CACHE (offline mode) -->"
fi
echo ""
echo "## Previous Context"
echo ""
if [ "$API_AVAILABLE" = "false" ]; then
echo "[WARNING] **Offline Mode** - Using cached context (API unavailable)"
echo ""
fi
echo "The following context has been automatically recalled:"
echo ""
# Extract and format each context entry
echo "$CONTEXT_RESPONSE" | python3 -c "
import sys, json
try:
contexts = json.load(sys.stdin)
if isinstance(contexts, list):
for i, ctx in enumerate(contexts, 1):
title = ctx.get('title', 'Untitled')
summary = ctx.get('dense_summary', '')
score = ctx.get('relevance_score', 0)
ctx_type = ctx.get('context_type', 'unknown')
print(f'### {i}. {title} (Score: {score}/10)')
print(f'*Type: {ctx_type}*')
print()
print(summary)
print()
print('---')
print()
except:
pass
" 2>/dev/null
echo ""
if [ "$API_AVAILABLE" = "true" ]; then
echo "*Context automatically injected to maintain continuity across sessions.*"
else
echo "*Context from local cache - new context will sync when API is available.*"
fi
echo ""
fi
# Exit successfully
exit 0