Claude Code Memory Compiler

This commit is contained in:
Cole Medin 2026-04-06 09:26:30 -05:00
commit f83d38d787
15 changed files with 2819 additions and 0 deletions

92
hooks/session-start.py Normal file
View file

@ -0,0 +1,92 @@
"""
SessionStart hook - injects knowledge base context into every conversation.
This is the "context injection" layer. When Claude Code starts a session,
this hook reads the knowledge base index and recent daily log, then injects
them as additional context so Claude always "remembers" what it has learned.
Configure in .claude/settings.json:
{
"hooks": {
"SessionStart": [{
"matcher": "",
"command": "uv run python hooks/session-start.py"
}]
}
}
"""
import json
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
# Paths relative to project root
ROOT = Path(__file__).resolve().parent.parent
KNOWLEDGE_DIR = ROOT / "knowledge"
DAILY_DIR = ROOT / "daily"
INDEX_FILE = KNOWLEDGE_DIR / "index.md"
MAX_CONTEXT_CHARS = 20_000
MAX_LOG_LINES = 30
def get_recent_log() -> str:
"""Read the most recent daily log (today or yesterday)."""
today = datetime.now(timezone.utc).astimezone()
for offset in range(2):
date = today - timedelta(days=offset)
log_path = DAILY_DIR / f"{date.strftime('%Y-%m-%d')}.md"
if log_path.exists():
lines = log_path.read_text(encoding="utf-8").splitlines()
# Return last N lines to keep context small
recent = lines[-MAX_LOG_LINES:] if len(lines) > MAX_LOG_LINES else lines
return "\n".join(recent)
return "(no recent daily log)"
def build_context() -> str:
"""Assemble the context to inject into the conversation."""
parts = []
# Today's date
today = datetime.now(timezone.utc).astimezone()
parts.append(f"## Today\n{today.strftime('%A, %B %d, %Y')}")
# Knowledge base index (the core retrieval mechanism)
if INDEX_FILE.exists():
index_content = INDEX_FILE.read_text(encoding="utf-8")
parts.append(f"## Knowledge Base Index\n\n{index_content}")
else:
parts.append("## Knowledge Base Index\n\n(empty - no articles compiled yet)")
# Recent daily log
recent_log = get_recent_log()
parts.append(f"## Recent Daily Log\n\n{recent_log}")
context = "\n\n---\n\n".join(parts)
# Truncate if too long
if len(context) > MAX_CONTEXT_CHARS:
context = context[:MAX_CONTEXT_CHARS] + "\n\n...(truncated)"
return context
def main():
context = build_context()
output = {
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": context,
}
}
print(json.dumps(output))
if __name__ == "__main__":
main()