Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .claude-plugin/hooks/mempal-precompact-hook.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#!/bin/bash
# MemPalace PreCompact Hook — thin wrapper calling Python CLI
# All logic lives in mempalace.hooks_cli for cross-harness extensibility
find_python() {
if [ -n "${MEMPALACE_PYTHON:-}" ]; then echo "$MEMPALACE_PYTHON"
elif command -v python3 &>/dev/null; then echo "python3"
elif command -v python &>/dev/null; then echo "python"
else echo "python3"; fi
}
PYTHON=$(find_python)
INPUT=$(cat)
echo "$INPUT" | python3 -m mempalace hook run --hook precompact --harness claude-code
echo "$INPUT" | $PYTHON -m mempalace hook run --hook precompact --harness claude-code
9 changes: 8 additions & 1 deletion .claude-plugin/hooks/mempal-stop-hook.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#!/bin/bash
# MemPalace Stop Hook — thin wrapper calling Python CLI
# All logic lives in mempalace.hooks_cli for cross-harness extensibility
find_python() {
if [ -n "${MEMPALACE_PYTHON:-}" ]; then echo "$MEMPALACE_PYTHON"
elif command -v python3 &>/dev/null; then echo "python3"
elif command -v python &>/dev/null; then echo "python"
else echo "python3"; fi
}
PYTHON=$(find_python)
INPUT=$(cat)
echo "$INPUT" | python3 -m mempalace hook run --hook stop --harness claude-code
echo "$INPUT" | $PYTHON -m mempalace hook run --hook stop --harness claude-code
9 changes: 8 additions & 1 deletion .codex-plugin/hooks/mempal-hook.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
HOOK_NAME="${1:?Usage: mempal-hook.sh <hook-name>}"
find_python() {
if [ -n "${MEMPALACE_PYTHON:-}" ]; then echo "$MEMPALACE_PYTHON"
elif command -v python3 &>/dev/null; then echo "python3"
elif command -v python &>/dev/null; then echo "python"
else echo "python3"; fi
}
PYTHON=$(find_python)
INPUT_FILE=$(mktemp) || { echo "Failed to create temp file" >&2; exit 1; }
cat > "$INPUT_FILE"
cat "$INPUT_FILE" | python3 -m mempalace hook run --hook "$HOOK_NAME" --harness codex
cat "$INPUT_FILE" | $PYTHON -m mempalace hook run --hook "$HOOK_NAME" --harness codex
EXIT_CODE=$?
rm -f "$INPUT_FILE" 2>/dev/null
exit $EXIT_CODE
13 changes: 11 additions & 2 deletions hooks/mempal_precompact_hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,27 @@ mkdir -p "$STATE_DIR"
# Leave empty to skip auto-ingest (AI handles saving via the block reason).
MEMPAL_DIR=""

# Find the right Python interpreter (fixes #545 — bare python3 breaks on Windows/pipx/uv)
find_python() {
if [ -n "${MEMPALACE_PYTHON:-}" ]; then echo "$MEMPALACE_PYTHON"
elif command -v python3 &>/dev/null; then echo "python3"
elif command -v python &>/dev/null; then echo "python"
else echo "python3"; fi
}
PYTHON=$(find_python)

# Read JSON input from stdin
INPUT=$(cat)

SESSION_ID=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('session_id','unknown'))" 2>/dev/null)
SESSION_ID=$(echo "$INPUT" | $PYTHON -c "import sys,json; print(json.load(sys.stdin).get('session_id','unknown'))" 2>/dev/null)

echo "[$(date '+%H:%M:%S')] PRE-COMPACT triggered for session $SESSION_ID" >> "$STATE_DIR/hook.log"

# Optional: run mempalace ingest synchronously so memories land before compaction
if [ -n "$MEMPAL_DIR" ] && [ -d "$MEMPAL_DIR" ]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "$SCRIPT_DIR")"
python3 -m mempalace mine "$MEMPAL_DIR" >> "$STATE_DIR/hook.log" 2>&1
$PYTHON -m mempalace mine "$MEMPAL_DIR" >> "$STATE_DIR/hook.log" 2>&1
fi

# Always block — compaction = save everything
Expand Down
15 changes: 12 additions & 3 deletions hooks/mempal_save_hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,20 @@ mkdir -p "$STATE_DIR"
# Leave empty to skip auto-ingest (AI handles saving via the block reason).
MEMPAL_DIR=""

# Find the right Python interpreter (fixes #545 — bare python3 breaks on Windows/pipx/uv)
find_python() {
if [ -n "${MEMPALACE_PYTHON:-}" ]; then echo "$MEMPALACE_PYTHON"
elif command -v python3 &>/dev/null; then echo "python3"
elif command -v python &>/dev/null; then echo "python"
else echo "python3"; fi
}
PYTHON=$(find_python)

# Read JSON input from stdin
INPUT=$(cat)

# Parse all fields in a single Python call (3x faster than separate invocations)
eval $(echo "$INPUT" | python3 -c "
eval $(echo "$INPUT" | $PYTHON -c "
import sys, json
data = json.load(sys.stdin)
sid = data.get('session_id', 'unknown')
Expand All @@ -92,7 +101,7 @@ fi
# Count human messages in the JSONL transcript
# SECURITY: Pass transcript path as sys.argv to avoid shell injection via crafted paths
if [ -f "$TRANSCRIPT_PATH" ]; then
EXCHANGE_COUNT=$(python3 - "$TRANSCRIPT_PATH" <<'PYEOF'
EXCHANGE_COUNT=$($PYTHON - "$TRANSCRIPT_PATH" <<'PYEOF'
import json, sys
count = 0
with open(sys.argv[1]) as f:
Expand Down Expand Up @@ -137,7 +146,7 @@ if [ "$SINCE_LAST" -ge "$SAVE_INTERVAL" ] && [ "$EXCHANGE_COUNT" -gt 0 ]; then
if [ -n "$MEMPAL_DIR" ] && [ -d "$MEMPAL_DIR" ]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "$SCRIPT_DIR")"
python3 -m mempalace mine "$MEMPAL_DIR" >> "$STATE_DIR/hook.log" 2>&1 &
$PYTHON -m mempalace mine "$MEMPAL_DIR" >> "$STATE_DIR/hook.log" 2>&1 &
fi

# Block the AI and tell it to save
Expand Down
21 changes: 21 additions & 0 deletions mempalace/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@
from datetime import datetime
from pathlib import Path

# Python version guard — chromadb 0.5-0.6.x uses pydantic v1 which is
# incompatible with Python 3.14+ (type inference fails at import time).
# Emit a clear message instead of a cryptic pydantic ConfigError.
if sys.version_info >= (3, 14):
print(
json.dumps({
"jsonrpc": "2.0",
"id": None,
"error": {
"code": -32002,
"message": (
f"MemPalace requires Python 3.9-3.13 but found {sys.version.split()[0]}. "
"ChromaDB is incompatible with Python 3.14+. "
"Set MEMPALACE_PYTHON to a supported interpreter or install in a 3.13 venv."
),
},
}),
flush=True,
)
sys.exit(1)

from .config import MempalaceConfig, sanitize_name, sanitize_content
from .version import __version__
from .query_sanitizer import sanitize_query
Expand Down
Loading