workstation: provision homelab-memory hooks for all users (retire claude-memory MCP)
Roll the wizard MCP->homelab-CLI memory migration out to every devvm user. Adds install_memory() to t3-provision-users.sh (mirrors install_playwright: per-user, idempotent, if-absent, as-the-user): installs the 4 memory hook scripts into ~/.claude/hooks, wires them into settings.json additively (wire-memory-hooks.py never touches env / the per-user MEMORY_API_KEY), and removes ONLY the claude_memory MCP + plugin if present. Reuses each user's existing key (no minting; per-user isolation stays deferred per the 2026-06-07 design). The homelab CLI hits the same remote HTTP API the MCP used; recall runs via the homelab-memory-recall.py UserPromptSubmit hook. Shared instructions (rules/skills symlinked from base; root+infra CLAUDE.md) already cover all users. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
79749d7324
commit
44562535a2
6 changed files with 500 additions and 0 deletions
70
scripts/workstation/claude-hooks/homelab-memory-recall.py
Executable file
70
scripts/workstation/claude-hooks/homelab-memory-recall.py
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env python3
|
||||
"""UserPromptSubmit hook: inject relevant memories via `homelab memory recall`.
|
||||
|
||||
Replaces the claude-memory MCP recall path. Instead of instructing the model to
|
||||
call the memory_recall MCP tool, this hook runs the homelab CLI (a direct client
|
||||
to the same claude-memory HTTP API) and injects the ACTUAL results as context —
|
||||
so recall is automatic, needs no model tool-call, and works with the MCP
|
||||
uninstalled. Best-effort: any failure exits 0 silently (recall just doesn't
|
||||
happen that turn, exactly like the MCP being unavailable).
|
||||
|
||||
Wizard-only trial of the MCP deprecation (2026-06-20). Reversible: restore the
|
||||
plugin command in ~/.claude/settings.json (backup: settings.json.bak-pre-homelab-memory).
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
hook_input = json.load(sys.stdin)
|
||||
except (json.JSONDecodeError, EOFError):
|
||||
return
|
||||
|
||||
prompt = ""
|
||||
if isinstance(hook_input, dict):
|
||||
prompt = hook_input.get("prompt") or hook_input.get("user_prompt") or ""
|
||||
if not prompt and isinstance(hook_input.get("content"), str):
|
||||
prompt = hook_input["content"]
|
||||
prompt = (prompt or "").strip()
|
||||
|
||||
# Same gates as the original recall hook: skip short prompts, code/JSON/XML blobs.
|
||||
if len(prompt) < 10 or prompt[0] in "`{<":
|
||||
return
|
||||
|
||||
homelab = shutil.which("homelab") or "/usr/local/bin/homelab"
|
||||
if not os.path.exists(homelab):
|
||||
return
|
||||
if not (os.environ.get("CLAUDE_MEMORY_API_KEY") or os.environ.get("MEMORY_API_KEY")):
|
||||
return
|
||||
|
||||
try:
|
||||
res = subprocess.run(
|
||||
[homelab, "memory", "recall", prompt, "--limit", "5"],
|
||||
capture_output=True, text=True, timeout=4, env=os.environ,
|
||||
)
|
||||
except (subprocess.TimeoutExpired, OSError):
|
||||
return
|
||||
|
||||
out = (res.stdout or "").strip()
|
||||
if res.returncode != 0 or not out:
|
||||
return
|
||||
|
||||
context = (
|
||||
"Relevant stored memories (via `homelab memory recall`) — incorporate "
|
||||
"naturally if useful; do NOT mention this lookup to the user:\n\n" + out
|
||||
)
|
||||
print(json.dumps({
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "UserPromptSubmit",
|
||||
"additionalContext": context,
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue