-
Notifications
You must be signed in to change notification settings - Fork 7.6k
feat: two-layer hook capture, auto-mine transcripts, hook settings #633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
73e08d5
9c28949
f9c6983
f9f573a
14b260e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,20 @@ | ||
| #!/bin/bash | ||
| # MemPalace PreCompact Hook — thin wrapper calling Python CLI | ||
| # All logic lives in mempalace.hooks_cli for cross-harness extensibility | ||
| # | ||
| # Python resolution order: | ||
| # 1. MEMPALACE_PYTHON env var (user override) | ||
| # 2. Plugin root's venv (development installs) | ||
| # 3. System python3 (pip install --user / pipx) | ||
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| PLUGIN_ROOT="$(dirname "$SCRIPT_DIR")" | ||
|
|
||
| if [ -n "$MEMPALACE_PYTHON" ] && [ -x "$MEMPALACE_PYTHON" ]; then | ||
| PYTHON="$MEMPALACE_PYTHON" | ||
| elif [ -x "$PLUGIN_ROOT/venv/bin/python3" ]; then | ||
| PYTHON="$PLUGIN_ROOT/venv/bin/python3" | ||
| else | ||
| PYTHON="python3" | ||
| fi | ||
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,20 @@ | ||
| #!/bin/bash | ||
| # MemPalace Stop Hook — thin wrapper calling Python CLI | ||
| # All logic lives in mempalace.hooks_cli for cross-harness extensibility | ||
| # | ||
| # Python resolution order: | ||
| # 1. MEMPALACE_PYTHON env var (user override) | ||
| # 2. Plugin root's venv (development installs) | ||
| # 3. System python3 (pip install --user / pipx) | ||
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| PLUGIN_ROOT="$(dirname "$SCRIPT_DIR")" | ||
|
|
||
| if [ -n "$MEMPALACE_PYTHON" ] && [ -x "$MEMPALACE_PYTHON" ]; then | ||
| PYTHON="$MEMPALACE_PYTHON" | ||
| elif [ -x "$PLUGIN_ROOT/venv/bin/python3" ]; then | ||
| PYTHON="$PLUGIN_ROOT/venv/bin/python3" | ||
| else | ||
| PYTHON="python3" | ||
| fi | ||
| 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 |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -61,6 +61,16 @@ mkdir -p "$STATE_DIR" | |||
| # Leave empty to skip auto-ingest (AI handles saving via the block reason). | ||||
| MEMPAL_DIR="" | ||||
|
|
||||
| # Python interpreter with mempalace + chromadb installed. | ||||
| # Auto-detects: MEMPALACE_PYTHON env var → repo venv → system python3 | ||||
| if [ -n "$MEMPALACE_PYTHON" ]; then | ||||
| MP_PYTHON="$MEMPALACE_PYTHON" | ||||
| elif [ -f "$(dirname "$(dirname "${BASH_SOURCE[0]}")")/venv/bin/python3" ]; then | ||||
| MP_PYTHON="$(dirname "$(dirname "${BASH_SOURCE[0]}")")/venv/bin/python3" | ||||
| else | ||||
| MP_PYTHON="python3" | ||||
| fi | ||||
|
|
||||
| # Read JSON input from stdin | ||||
| INPUT=$(cat) | ||||
|
|
||||
|
|
@@ -137,15 +147,61 @@ 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 & | ||||
| "$MP_PYTHON" -m mempalace mine "$MEMPAL_DIR" >> "$STATE_DIR/hook.log" 2>&1 & | ||||
| fi | ||||
|
|
||||
| # Auto-mine the transcript — captures tool output that the AI would summarize away | ||||
| if [ -f "$TRANSCRIPT_PATH" ]; then | ||||
| "$MP_PYTHON" - "$TRANSCRIPT_PATH" <<'PYMINE' | ||||
| import sys | ||||
| try: | ||||
| import hashlib | ||||
| from datetime import datetime | ||||
| from mempalace.normalize import normalize | ||||
| from mempalace.convo_miner import chunk_exchanges, detect_convo_room | ||||
| from mempalace.palace import get_collection | ||||
| from mempalace.config import MempalaceConfig | ||||
| palace = MempalaceConfig().palace_path | ||||
| content = normalize(sys.argv[1]) | ||||
| if content and len(content.strip()) >= 50: | ||||
| collection = get_collection(palace) | ||||
| source = sys.argv[1] | ||||
| # No file_already_mined check — transcript grows during session. | ||||
| # upsert is idempotent: same chunk_index → same ID → overwrite. | ||||
| chunks = chunk_exchanges(content) | ||||
| if chunks: | ||||
| room = detect_convo_room(content) or "session" | ||||
| wing = "conversations" | ||||
| docs, ids, metas = [], [], [] | ||||
| for chunk in chunks: | ||||
| cid = hashlib.sha256( | ||||
| (source + str(chunk["chunk_index"])).encode() | ||||
| ).hexdigest()[:24] | ||||
| docs.append(chunk["content"]) | ||||
| ids.append(f"drawer_{wing}_{room}_{cid}") | ||||
| metas.append({ | ||||
| "wing": wing, "room": room, "source_file": source, | ||||
| "chunk_index": chunk["chunk_index"], | ||||
| "added_by": "hook", "filed_at": datetime.now().isoformat(), | ||||
| "ingest_mode": "convos", "extract_mode": "exchange", | ||||
| }) | ||||
| for i in range(0, len(docs), 100): | ||||
| collection.upsert( | ||||
| documents=docs[i:i+100], ids=ids[i:i+100], | ||||
| metadatas=metas[i:i+100], | ||||
| ) | ||||
| except Exception: | ||||
| pass # Hook must never crash the AI | ||||
| PYMINE | ||||
| >> "$STATE_DIR/hook.log" 2>&1 | ||||
|
||||
| >> "$STATE_DIR/hook.log" 2>&1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is valid bash syntax. The redirection >> "$STATE_DIR/hook.log" 2>&1 on the line after the heredoc delimiter redirects the entire heredoc command's output. This is how shell heredocs work — the redirection applies to the command that opened the heredoc (the python3 - invocation on line 155).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same heredoc redirection issue as the save hook: the
>> "$STATE_DIR/hook.log" 2>&1line appears after thePYMINEdelimiter, which will produce a bash syntax error. Attach the redirection to the python command line (before the heredoc body) or redirect a grouped block.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above — this is valid bash heredoc redirection syntax.