From e47fa1b5bf3f6bb9e6f6921f042dc2a1022a2e74 Mon Sep 17 00:00:00 2001 From: Tal Muskal Date: Wed, 8 Apr 2026 20:17:23 +0300 Subject: [PATCH] refactor: consolidate hook scripts and fixed issue from review --- .codex-plugin/README.md | 2 +- .codex-plugin/hooks.json | 6 +-- .codex-plugin/hooks/mempal-hook.sh | 9 ++++ .codex-plugin/hooks/mempal-precompact-hook.sh | 15 ------- .../hooks/mempal-session-start-hook.sh | 15 ------- .codex-plugin/hooks/mempal-stop-hook.sh | 15 ------- mempalace/hooks_cli.py | 45 ++++++++----------- 7 files changed, 32 insertions(+), 75 deletions(-) create mode 100644 .codex-plugin/hooks/mempal-hook.sh delete mode 100644 .codex-plugin/hooks/mempal-precompact-hook.sh delete mode 100644 .codex-plugin/hooks/mempal-session-start-hook.sh delete mode 100644 .codex-plugin/hooks/mempal-stop-hook.sh diff --git a/.codex-plugin/README.md b/.codex-plugin/README.md index a7ec1de..59e01e9 100644 --- a/.codex-plugin/README.md +++ b/.codex-plugin/README.md @@ -4,7 +4,7 @@ Give your AI a persistent memory -- mine projects and conversations into a searc ## Prerequisites -- Python 3.10+ +- Python 3.9+ - Codex CLI installed and configured - `pip install mempalace` diff --git a/.codex-plugin/hooks.json b/.codex-plugin/hooks.json index 1c235f2..46f7e66 100644 --- a/.codex-plugin/hooks.json +++ b/.codex-plugin/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "./hooks/mempal-session-start-hook.sh" + "command": "${CODEX_PLUGIN_ROOT}/hooks/mempal-hook.sh session-start" } ] } @@ -17,7 +17,7 @@ "hooks": [ { "type": "command", - "command": "./hooks/mempal-stop-hook.sh" + "command": "${CODEX_PLUGIN_ROOT}/hooks/mempal-hook.sh stop" } ] } @@ -28,7 +28,7 @@ "hooks": [ { "type": "command", - "command": "./hooks/mempal-precompact-hook.sh" + "command": "${CODEX_PLUGIN_ROOT}/hooks/mempal-hook.sh precompact" } ] } diff --git a/.codex-plugin/hooks/mempal-hook.sh b/.codex-plugin/hooks/mempal-hook.sh new file mode 100644 index 0000000..1cc0050 --- /dev/null +++ b/.codex-plugin/hooks/mempal-hook.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail +HOOK_NAME="${1:?Usage: mempal-hook.sh }" +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 +EXIT_CODE=$? +rm -f "$INPUT_FILE" 2>/dev/null +exit $EXIT_CODE diff --git a/.codex-plugin/hooks/mempal-precompact-hook.sh b/.codex-plugin/hooks/mempal-precompact-hook.sh deleted file mode 100644 index 46af49d..0000000 --- a/.codex-plugin/hooks/mempal-precompact-hook.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" - -# Capture stdin (hook input from Codex) -INPUT_FILE=$(mktemp 2>/dev/null || echo "/tmp/mempal-precompact-hook-$$.json") -cat > "$INPUT_FILE" - -# Pipe to Python CLI with codex harness -cat "$INPUT_FILE" | python3 -m mempalace hook run --hook precompact --harness codex -EXIT_CODE=$? - -# Cleanup -rm -f "$INPUT_FILE" 2>/dev/null -exit $EXIT_CODE diff --git a/.codex-plugin/hooks/mempal-session-start-hook.sh b/.codex-plugin/hooks/mempal-session-start-hook.sh deleted file mode 100644 index 7c7b5c2..0000000 --- a/.codex-plugin/hooks/mempal-session-start-hook.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" - -# Capture stdin (hook input from Codex) -INPUT_FILE=$(mktemp 2>/dev/null || echo "/tmp/mempal-session-start-hook-$$.json") -cat > "$INPUT_FILE" - -# Pipe to Python CLI with codex harness -cat "$INPUT_FILE" | python3 -m mempalace hook run --hook session-start --harness codex -EXIT_CODE=$? - -# Cleanup -rm -f "$INPUT_FILE" 2>/dev/null -exit $EXIT_CODE diff --git a/.codex-plugin/hooks/mempal-stop-hook.sh b/.codex-plugin/hooks/mempal-stop-hook.sh deleted file mode 100644 index 2d38932..0000000 --- a/.codex-plugin/hooks/mempal-stop-hook.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" - -# Capture stdin (hook input from Codex) -INPUT_FILE=$(mktemp 2>/dev/null || echo "/tmp/mempal-stop-hook-$$.json") -cat > "$INPUT_FILE" - -# Pipe to Python CLI with codex harness -cat "$INPUT_FILE" | python3 -m mempalace hook run --hook stop --harness codex -EXIT_CODE=$? - -# Cleanup -rm -f "$INPUT_FILE" 2>/dev/null -exit $EXIT_CODE diff --git a/mempalace/hooks_cli.py b/mempalace/hooks_cli.py index 7d46d1a..fe6e4eb 100644 --- a/mempalace/hooks_cli.py +++ b/mempalace/hooks_cli.py @@ -53,8 +53,15 @@ def _count_human_messages(transcript_path: str) -> int: msg = entry.get("message", {}) if isinstance(msg, dict) and msg.get("role") == "user": content = msg.get("content", "") - if isinstance(content, str) and "" in content: - continue + if isinstance(content, str): + if "" in content: + continue + elif isinstance(content, list): + text = " ".join( + b.get("text", "") for b in content if isinstance(b, dict) + ) + if "" in text: + continue count += 1 except (json.JSONDecodeError, AttributeError): pass @@ -96,35 +103,19 @@ def _maybe_auto_ingest(): pass -def _parse_claude_code_input(data: dict) -> dict: - """Parse stdin JSON for the claude-code harness.""" - return { - "session_id": _sanitize_session_id(str(data.get("session_id", "unknown"))), - "stop_hook_active": data.get("stop_hook_active", False), - "transcript_path": str(data.get("transcript_path", "")), - } - - -def _parse_codex_input(data: dict) -> dict: - """Parse stdin JSON for the codex harness.""" - return { - "session_id": _sanitize_session_id(str(data.get("session_id", "unknown"))), - "stop_hook_active": data.get("stop_hook_active", False), - "transcript_path": str(data.get("transcript_path", "")), - } +SUPPORTED_HARNESSES = {"claude-code", "codex"} def _parse_harness_input(data: dict, harness: str) -> dict: """Parse stdin JSON according to the harness type.""" - parsers = { - "claude-code": _parse_claude_code_input, - "codex": _parse_codex_input, - } - parser = parsers.get(harness) - if parser is None: + if harness not in SUPPORTED_HARNESSES: print(f"Unknown harness: {harness}", file=sys.stderr) sys.exit(1) - return parser(data) + return { + "session_id": _sanitize_session_id(str(data.get("session_id", "unknown"))), + "stop_hook_active": data.get("stop_hook_active", False), + "transcript_path": str(data.get("transcript_path", "")), + } def hook_stop(data: dict, harness: str): @@ -135,7 +126,7 @@ def hook_stop(data: dict, harness: str): transcript_path = parsed["transcript_path"] # If already in a save cycle, let through (infinite-loop prevention) - if stop_hook_active in (True, "True", "true"): + if str(stop_hook_active).lower() in ("true", "1", "yes"): _output({}) return @@ -204,6 +195,7 @@ def hook_precompact(data: dict, harness: str): [sys.executable, "-m", "mempalace", "mine", mempal_dir], stdout=log_f, stderr=log_f, + timeout=60, ) except OSError: pass @@ -217,6 +209,7 @@ def run_hook(hook_name: str, harness: str): try: data = json.load(sys.stdin) except (json.JSONDecodeError, EOFError): + _log("WARNING: Failed to parse stdin JSON, proceeding with empty data") data = {} hooks = {