fix(hooks): always mine the active transcript as convos, additive to MEMPAL_DIR

#1230 fixed --mode convos for the case where MEMPAL_DIR was unset, but
left two configurations broken:

  - MEMPAL_DIR set to a project dir: convos never mined (MEMPAL_DIR
    overrode the transcript path); only project files were ingested.
  - MEMPAL_DIR set to a conversations dir per the old hooks/README: the
    projects miner ran on JSONL — same wrong-miner behaviour.

The shell hooks (mempal_save_hook.sh, mempal_precompact_hook.sh) had
the same MEMPAL_DIR-overrides-transcript bug AND were missing --mode
on every spawned `mempalace mine` call.

Make the auto-ingest *additive*. _get_mine_dir → _get_mine_targets,
returning a list of (dir, mode) pairs:

  - MEMPAL_DIR (when valid) contributes (dir, "projects")
  - A valid transcript JSONL contributes (parent, "convos")
  - Both can appear together; the hook spawns one ingest per target

Same change applied to the shell save and precompact hooks. Precompact
also gained transcript_path parsing so it can run the convos mine
synchronously before context is compressed. hooks/README.md updated to
describe MEMPAL_DIR as a project-files target, never a convos target.
This commit is contained in:
Igor Lins e Silva
2026-04-27 00:32:35 -03:00
parent 6a8beef604
commit eb4de04339
6 changed files with 197 additions and 113 deletions
+13 -18
View File
@@ -16,7 +16,8 @@ class TestSaveHookAutoMines:
def test_hook_mines_transcript_path(self):
"""The hook receives TRANSCRIPT_PATH from Claude Code.
It should use that to mine the conversation, not depend on MEMPAL_DIR."""
It should use that to mine the conversation as --mode convos,
independently of MEMPAL_DIR (which is for project files only)."""
hook_path = os.path.join(
os.path.dirname(os.path.dirname(__file__)),
"hooks",
@@ -24,23 +25,17 @@ class TestSaveHookAutoMines:
)
src = open(hook_path).read()
# The hook ALREADY receives TRANSCRIPT_PATH in the JSON input.
# It should use this to mine the current session's transcript
# regardless of whether MEMPAL_DIR is set.
# The hook must have a path that uses TRANSCRIPT_PATH to determine
# what to mine, separate from the MEMPAL_DIR path.
uses_transcript = "TRANSCRIPT_PATH" in src
has_mine = "mempalace mine" in src
# TRANSCRIPT_PATH must appear in the mining logic, not just the parse block
transcript_drives_mine = "MINE_DIR" in src and "dirname" in src and "TRANSCRIPT_PATH" in src
assert uses_transcript and has_mine and transcript_drives_mine, (
"Save hook only mines when MEMPAL_DIR is set (defaults to empty). "
"The hook receives TRANSCRIPT_PATH from Claude Code — it should "
"mine that file automatically so conversations are saved without "
"the user setting an env var. Currently the hook says 'saved in "
"background' but nothing actually saves."
)
# The hook must drive the conversation mine off TRANSCRIPT_PATH,
# using `dirname` to derive the parent dir, and tagging it with
# `--mode convos` so the convo miner runs (not the projects miner).
assert "TRANSCRIPT_PATH" in src, "hook must read transcript_path"
assert "mempalace mine" in src, "hook must invoke `mempalace mine`"
assert (
'dirname "$TRANSCRIPT_PATH"' in src
), "hook must mine the transcript's parent directory"
assert (
"--mode convos" in src
), "transcript mine must use --mode convos, not the projects miner"
def test_mempal_dir_default_not_empty(self):
"""If MEMPAL_DIR is still used, it should have a sensible default,