|
1 | 1 | #!/usr/bin/env python3 |
2 | | -"""Hook: Inject memory capture context at session start. |
| 2 | +"""Hook: Inject memory context at session start. |
3 | 3 |
|
4 | 4 | This hook runs at the start of each Claude Code session and injects |
5 | | -context that reminds the user and assistant about memory capture markers. |
6 | | -
|
7 | | -The hook: |
8 | | -1. Provides memory system status (index health, memory count) |
9 | | -2. Reminds about capture markers: [remember], [capture], @memory |
10 | | -3. Lists available namespaces and their purposes |
11 | | -4. Suggests relevant memories if spec context is available |
| 5 | +context that includes: |
| 6 | +1. Memory system status (index health, memory count) |
| 7 | +2. Relevant memories for the current project/spec |
| 8 | +3. Response guidance for memory capture markers |
| 9 | +4. Available namespaces and their purposes |
| 10 | +
|
| 11 | +Environment Variables: |
| 12 | + HOOK_ENABLED: Master switch for hooks (default: true) |
| 13 | + HOOK_SESSION_START_ENABLED: Enable this hook (default: true) |
| 14 | + HOOK_DEBUG: Enable debug logging (default: false) |
| 15 | +
|
| 16 | +Exit codes: |
| 17 | + 0 - Success (non-blocking) |
12 | 18 | """ |
13 | 19 |
|
14 | 20 | from __future__ import annotations |
|
24 | 30 | sys.path.insert(0, str(_src_path)) |
25 | 31 |
|
26 | 32 |
|
27 | | -def get_memory_status() -> dict: |
28 | | - """Get current memory system status. |
| 33 | +def main() -> None: |
| 34 | + """Main hook entry point. |
29 | 35 |
|
30 | | - Returns: |
31 | | - Dict with status information. |
| 36 | + Delegates to the session_start_handler module for actual processing. |
| 37 | + Falls back gracefully if the library is not installed. |
32 | 38 | """ |
33 | 39 | try: |
34 | | - from git_notes_memory.config import get_index_path |
35 | | - from git_notes_memory.index import IndexService |
36 | | - |
37 | | - index_path = get_index_path() |
38 | | - if not index_path.exists(): |
39 | | - return { |
40 | | - "initialized": False, |
41 | | - "total_memories": 0, |
42 | | - "message": "Memory index not initialized. Run `/memory:sync` to initialize.", |
43 | | - } |
44 | | - |
45 | | - index = IndexService(index_path) |
46 | | - index.initialize() |
47 | | - stats = index.get_stats() |
48 | | - index.close() |
49 | | - |
50 | | - return { |
51 | | - "initialized": True, |
52 | | - "total_memories": stats.total_memories, |
53 | | - "by_namespace": dict(stats.by_namespace) if stats.by_namespace else {}, |
54 | | - } |
| 40 | + from git_notes_memory.hooks.session_start_handler import main as handler_main |
55 | 41 |
|
| 42 | + handler_main() |
56 | 43 | except ImportError: |
57 | | - return { |
58 | | - "initialized": False, |
59 | | - "total_memories": 0, |
60 | | - "error": "git-notes-memory library not installed", |
61 | | - } |
62 | | - except Exception as e: |
63 | | - return { |
64 | | - "initialized": False, |
65 | | - "total_memories": 0, |
66 | | - "error": str(e), |
67 | | - } |
68 | | - |
69 | | - |
70 | | -def build_context_message(status: dict) -> str: |
71 | | - """Build the context injection message. |
72 | | -
|
73 | | - Args: |
74 | | - status: Memory system status dict. |
75 | | -
|
76 | | - Returns: |
77 | | - Formatted context message for Claude. |
78 | | - """ |
79 | | - lines = [] |
80 | | - |
81 | | - # Memory system status |
82 | | - if status.get("initialized"): |
83 | | - total = status.get("total_memories", 0) |
84 | | - lines.append(f"Memory system active: {total} memories indexed.") |
85 | | - if status.get("by_namespace"): |
86 | | - ns_summary = ", ".join( |
87 | | - f"{ns}: {count}" for ns, count in list(status["by_namespace"].items())[:5] |
88 | | - ) |
89 | | - lines.append(f"Namespaces: {ns_summary}") |
90 | | - else: |
91 | | - lines.append("Memory system: not initialized.") |
92 | | - if status.get("error"): |
93 | | - lines.append(f"Note: {status['error']}") |
94 | | - |
95 | | - lines.append("") |
96 | | - lines.append("MEMORY CAPTURE MARKERS:") |
97 | | - lines.append( |
98 | | - "When you discover important information worth preserving, prefix your response with:" |
99 | | - ) |
100 | | - lines.append("- [remember] <content> - Captures as a 'learnings' memory") |
101 | | - lines.append("- [capture] <content> - Captures as a 'learnings' memory") |
102 | | - lines.append("- @memory <content> - Same as [capture]") |
103 | | - lines.append("") |
104 | | - lines.append( |
105 | | - "Examples of what to capture: decisions, learnings, blockers, " |
106 | | - "patterns, progress, and key insights." |
107 | | - ) |
108 | | - |
109 | | - return "\n".join(lines) |
110 | | - |
111 | | - |
112 | | -def build_user_message(status: dict) -> str: |
113 | | - """Build a concise user-visible status message. |
114 | | -
|
115 | | - Args: |
116 | | - status: Memory system status dict. |
117 | | -
|
118 | | - Returns: |
119 | | - Short status message with emoji indicator. |
120 | | - """ |
121 | | - if status.get("initialized"): |
122 | | - total = status.get("total_memories", 0) |
123 | | - return f"📚 Memory system: {total} memories indexed" |
124 | | - else: |
125 | | - return "📚 Memory system: not initialized" |
126 | | - |
127 | | - |
128 | | -def main() -> None: |
129 | | - """Main hook entry point.""" |
130 | | - # Read hook input from stdin (not used but must be consumed) |
131 | | - try: |
132 | | - _input_data = json.load(sys.stdin) |
133 | | - except (json.JSONDecodeError, ValueError): |
134 | | - _input_data = {} # noqa: F841 |
135 | | - |
136 | | - # Get memory system status |
137 | | - status = get_memory_status() |
138 | | - |
139 | | - # Build context message (for Claude) and user message (visible) |
140 | | - context_message = build_context_message(status) |
141 | | - user_message = build_user_message(status) |
142 | | - |
143 | | - # Output the context injection with user-visible message |
144 | | - output = { |
145 | | - "continue": True, |
146 | | - "additionalContext": context_message, |
147 | | - "message": user_message, |
148 | | - } |
149 | | - |
150 | | - print(json.dumps(output)) |
| 44 | + # Library not installed, exit silently (non-blocking) |
| 45 | + print(json.dumps({"continue": True})) |
| 46 | + sys.exit(0) |
| 47 | + except Exception: |
| 48 | + # Any unexpected error, exit silently (non-blocking) |
| 49 | + print(json.dumps({"continue": True})) |
| 50 | + sys.exit(0) |
151 | 51 |
|
152 | 52 |
|
153 | 53 | if __name__ == "__main__": |
|
0 commit comments