Summary
On Windows (hooks running under Git Bash), kill_orphaned_index in hooks/common.sh cannot reap the memsearch index processes spawned by the Stop hook. It reaps via Linux pgrep/kill, but the index it spawns is a native Windows python.exe (installed via Scoop/pip), which pgrep cannot see. The Stop hook spawns a fresh index every turn, so on Windows they are never reaped and accumulate until the machine runs out of memory.
Relationship to existing issues (not a duplicate)
Environment
- Windows 11; Claude Code hooks executed under Git Bash (MSYS2)
- memsearch plugin 0.4.11, server mode (Milvus at
localhost:19530, WSL2 Docker)
- embedding: onnx
bge-m3 int8
- memsearch installed as a native Windows app (Scoop Python), so
command -v memsearch in Git Bash resolves to …\scoop\shims\memsearch.exe
Observed
After one long session:
- 10 live
python.exe … memsearch.exe index <MEMORY_DIR> --collection ms_… processes, ~4.5 GB combined working set.
- Oldest was 24.7 hours old — it survived a
SessionEnd and a full Claude Code restart, proving nothing reaps it.
- The resulting memory pressure crashed the browser twice and Claude Code once.
The singleton memsearch watch process is fine (one process); the leak is purely the per-turn Stop-hook index.
Root cause
hooks/stop.sh runs on every turn and ends with:
kill_orphaned_index
run_memsearch index "$MEMORY_DIR"
kill_orphaned_index (hooks/common.sh) reaps with pgrep -f "memsearch index $MEMORY_DIR" + kill. Under Git Bash these only see POSIX-emulated processes, not the native Windows python.exe that actually runs the index. So on a Windows host the reaper is a silent no-op and every turn's index orphans.
Suggested fix
Add a Windows branch to kill_orphaned_index that reaps native processes when powershell.exe is on PATH:
# Windows host: reap native index processes that pgrep/kill (Linux) cannot see
# when the hook runs under Git Bash. Matches `index` only, so the singleton
# `watch` process is left alone.
if command -v powershell.exe >/dev/null 2>&1; then
powershell.exe -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Process | Where-Object { \$_.Name -like 'python*' -and \$_.CommandLine -match 'memsearch' -and \$_.CommandLine -match 'index' } | ForEach-Object { Stop-Process -Id \$_.ProcessId -Force -ErrorAction SilentlyContinue }" >/dev/null 2>&1 || true
fi
A tighter match on $MEMORY_DIR / collection name would scope it per-project (the broad match above kills any memsearch index, which is acceptable for a single-collection setup but not for multiple concurrent projects).
Same blind spot affects stop_watch's pgrep sweep, though that one matters less since watch is a singleton.
Happy to send a PR if useful.
Summary
On Windows (hooks running under Git Bash),
kill_orphaned_indexinhooks/common.shcannot reap thememsearch indexprocesses spawned by the Stop hook. It reaps via Linuxpgrep/kill, but the index it spawns is a native Windowspython.exe(installed via Scoop/pip), whichpgrepcannot see. The Stop hook spawns a fresh index every turn, so on Windows they are never reaped and accumulate until the machine runs out of memory.Relationship to existing issues (not a duplicate)
Indexing leaks ~9 MB anon-rss per chunk in single process). That is RSS growth within a single index process — "per-process restart releases memory normally." This bug is the inverse: wholememsearch indexprocesses accumulate becausekill_orphaned_indexnever reaps them on Windows, so the restart that would release memory never happens.Environment
localhost:19530, WSL2 Docker)bge-m3int8command -v memsearchin Git Bash resolves to…\scoop\shims\memsearch.exeObserved
After one long session:
python.exe … memsearch.exe index <MEMORY_DIR> --collection ms_…processes, ~4.5 GB combined working set.SessionEndand a full Claude Code restart, proving nothing reaps it.The singleton
memsearch watchprocess is fine (one process); the leak is purely the per-turn Stop-hook index.Root cause
hooks/stop.shruns on every turn and ends with:kill_orphaned_index run_memsearch index "$MEMORY_DIR"kill_orphaned_index(hooks/common.sh) reaps withpgrep -f "memsearch index $MEMORY_DIR"+kill. Under Git Bash these only see POSIX-emulated processes, not the native Windowspython.exethat actually runs the index. So on a Windows host the reaper is a silent no-op and every turn's index orphans.Suggested fix
Add a Windows branch to
kill_orphaned_indexthat reaps native processes whenpowershell.exeis on PATH:A tighter match on
$MEMORY_DIR/ collection name would scope it per-project (the broad match above kills any memsearch index, which is acceptable for a single-collection setup but not for multiple concurrent projects).Same blind spot affects
stop_watch'spgrepsweep, though that one matters less sincewatchis a singleton.Happy to send a PR if useful.