claude-memory-agent 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.env.example +107 -0
  2. package/README.md +200 -0
  3. package/agent_card.py +512 -0
  4. package/bin/cli.js +181 -0
  5. package/bin/postinstall.js +216 -0
  6. package/config.py +104 -0
  7. package/dashboard.html +2689 -0
  8. package/hooks/README.md +196 -0
  9. package/hooks/__pycache__/auto-detect-response.cpython-312.pyc +0 -0
  10. package/hooks/__pycache__/auto_capture.cpython-312.pyc +0 -0
  11. package/hooks/__pycache__/session_end.cpython-312.pyc +0 -0
  12. package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
  13. package/hooks/auto-detect-response.py +348 -0
  14. package/hooks/auto_capture.py +255 -0
  15. package/hooks/detect-correction.py +173 -0
  16. package/hooks/grounding-hook.py +348 -0
  17. package/hooks/log-tool-use.py +234 -0
  18. package/hooks/log-user-request.py +208 -0
  19. package/hooks/pre-tool-decision.py +218 -0
  20. package/hooks/problem-detector.py +343 -0
  21. package/hooks/session_end.py +192 -0
  22. package/hooks/session_start.py +227 -0
  23. package/install.py +887 -0
  24. package/main.py +2859 -0
  25. package/manager.py +997 -0
  26. package/package.json +55 -0
  27. package/requirements.txt +8 -0
  28. package/run_server.py +136 -0
  29. package/services/__init__.py +50 -0
  30. package/services/__pycache__/__init__.cpython-312.pyc +0 -0
  31. package/services/__pycache__/agent_registry.cpython-312.pyc +0 -0
  32. package/services/__pycache__/auth.cpython-312.pyc +0 -0
  33. package/services/__pycache__/auto_inject.cpython-312.pyc +0 -0
  34. package/services/__pycache__/claude_md_sync.cpython-312.pyc +0 -0
  35. package/services/__pycache__/cleanup.cpython-312.pyc +0 -0
  36. package/services/__pycache__/compaction_flush.cpython-312.pyc +0 -0
  37. package/services/__pycache__/confidence.cpython-312.pyc +0 -0
  38. package/services/__pycache__/daily_log.cpython-312.pyc +0 -0
  39. package/services/__pycache__/database.cpython-312.pyc +0 -0
  40. package/services/__pycache__/embeddings.cpython-312.pyc +0 -0
  41. package/services/__pycache__/insights.cpython-312.pyc +0 -0
  42. package/services/__pycache__/llm_analyzer.cpython-312.pyc +0 -0
  43. package/services/__pycache__/memory_md_sync.cpython-312.pyc +0 -0
  44. package/services/__pycache__/retry_queue.cpython-312.pyc +0 -0
  45. package/services/__pycache__/timeline.cpython-312.pyc +0 -0
  46. package/services/__pycache__/vector_index.cpython-312.pyc +0 -0
  47. package/services/__pycache__/websocket.cpython-312.pyc +0 -0
  48. package/services/agent_registry.py +753 -0
  49. package/services/auth.py +331 -0
  50. package/services/auto_inject.py +250 -0
  51. package/services/claude_md_sync.py +275 -0
  52. package/services/cleanup.py +667 -0
  53. package/services/compaction_flush.py +447 -0
  54. package/services/confidence.py +301 -0
  55. package/services/daily_log.py +333 -0
  56. package/services/database.py +2485 -0
  57. package/services/embeddings.py +358 -0
  58. package/services/insights.py +632 -0
  59. package/services/llm_analyzer.py +595 -0
  60. package/services/memory_md_sync.py +409 -0
  61. package/services/retry_queue.py +453 -0
  62. package/services/timeline.py +579 -0
  63. package/services/vector_index.py +398 -0
  64. package/services/websocket.py +257 -0
  65. package/skills/__init__.py +6 -0
  66. package/skills/__pycache__/__init__.cpython-312.pyc +0 -0
  67. package/skills/__pycache__/admin.cpython-312.pyc +0 -0
  68. package/skills/__pycache__/checkpoint.cpython-312.pyc +0 -0
  69. package/skills/__pycache__/claude_md.cpython-312.pyc +0 -0
  70. package/skills/__pycache__/cleanup.cpython-312.pyc +0 -0
  71. package/skills/__pycache__/grounding.cpython-312.pyc +0 -0
  72. package/skills/__pycache__/insights.cpython-312.pyc +0 -0
  73. package/skills/__pycache__/natural_language.cpython-312.pyc +0 -0
  74. package/skills/__pycache__/retrieve.cpython-312.pyc +0 -0
  75. package/skills/__pycache__/search.cpython-312.pyc +0 -0
  76. package/skills/__pycache__/state.cpython-312.pyc +0 -0
  77. package/skills/__pycache__/store.cpython-312.pyc +0 -0
  78. package/skills/__pycache__/summarize.cpython-312.pyc +0 -0
  79. package/skills/__pycache__/timeline.cpython-312.pyc +0 -0
  80. package/skills/__pycache__/verification.cpython-312.pyc +0 -0
  81. package/skills/admin.py +469 -0
  82. package/skills/checkpoint.py +198 -0
  83. package/skills/claude_md.py +363 -0
  84. package/skills/cleanup.py +241 -0
  85. package/skills/grounding.py +801 -0
  86. package/skills/insights.py +231 -0
  87. package/skills/natural_language.py +277 -0
  88. package/skills/retrieve.py +67 -0
  89. package/skills/search.py +213 -0
  90. package/skills/state.py +182 -0
  91. package/skills/store.py +179 -0
  92. package/skills/summarize.py +588 -0
  93. package/skills/timeline.py +387 -0
  94. package/skills/verification.py +391 -0
  95. package/start_daemon.py +155 -0
  96. package/test_automation.py +221 -0
  97. package/test_complete.py +338 -0
  98. package/test_full.py +322 -0
  99. package/update_system.py +817 -0
  100. package/verify_db.py +134 -0
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env python3
2
+ """Session start hook - auto-loads relevant context.
3
+
4
+ This hook runs when a Claude Code session starts and:
5
+ - Loads project info and preferences
6
+ - Retrieves recent decisions and patterns
7
+ - Gets unresolved items from previous sessions
8
+ - Injects relevant context into the session
9
+ - Loads daily logs (Moltbot-inspired)
10
+ - Loads MEMORY.md core facts (Moltbot-inspired)
11
+
12
+ Configure in Claude Code settings:
13
+ {
14
+ "hooks": {
15
+ "SessionStart": ["python /path/to/session_start.py"]
16
+ }
17
+ }
18
+
19
+ Output is printed to stdout and injected into Claude's context.
20
+ """
21
+ import os
22
+ import sys
23
+ import json
24
+ import asyncio
25
+ from datetime import datetime
26
+ from pathlib import Path
27
+ from typing import Dict, Any, List, Optional
28
+
29
+ sys.path.insert(0, str(Path(__file__).parent.parent))
30
+
31
+ import httpx
32
+
33
+ MEMORY_AGENT_URL = os.getenv("MEMORY_AGENT_URL", "http://localhost:8102")
34
+ API_KEY = os.getenv("MEMORY_API_KEY", "")
35
+
36
+
37
+ async def call_memory_skill(skill_id: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
38
+ """Call a memory agent skill."""
39
+ headers = {"Content-Type": "application/json"}
40
+ if API_KEY:
41
+ headers["X-Memory-Key"] = API_KEY
42
+
43
+ payload = {
44
+ "jsonrpc": "2.0",
45
+ "method": "skills/call",
46
+ "params": {
47
+ "skill_id": skill_id,
48
+ "params": params
49
+ },
50
+ "id": f"session-start-{datetime.now().isoformat()}"
51
+ }
52
+
53
+ try:
54
+ async with httpx.AsyncClient(timeout=10.0) as client:
55
+ response = await client.post(
56
+ f"{MEMORY_AGENT_URL}/a2a",
57
+ json=payload,
58
+ headers=headers
59
+ )
60
+ if response.status_code == 200:
61
+ data = response.json()
62
+ return data.get("result", {}).get("result", {})
63
+ except Exception as e:
64
+ pass
65
+ return None
66
+
67
+
68
+ async def load_session_context(project_path: str) -> str:
69
+ """Load all relevant context for a session start."""
70
+ context_parts = []
71
+
72
+ # ============================================================
73
+ # MOLTBOT-INSPIRED: Load MEMORY.md first (core facts)
74
+ # ============================================================
75
+ memory_md = await call_memory_skill("read_memory_md", {
76
+ "project_path": project_path
77
+ })
78
+
79
+ if memory_md and memory_md.get("exists"):
80
+ context_parts.append("## Core Facts (from MEMORY.md)")
81
+ # Include the summary or first part of content
82
+ content = memory_md.get("content", "")
83
+ # Truncate if too long
84
+ if len(content) > 2000:
85
+ content = content[:2000] + "\n...(truncated)"
86
+ context_parts.append(content)
87
+
88
+ # ============================================================
89
+ # MOLTBOT-INSPIRED: Load recent daily logs
90
+ # ============================================================
91
+ daily_logs = await call_memory_skill("daily_log_read", {
92
+ "project_path": project_path,
93
+ "days": 2,
94
+ "max_chars": 3000
95
+ })
96
+
97
+ if daily_logs and daily_logs.get("logs"):
98
+ context_parts.append("\n## Recent Activity (from Daily Logs)")
99
+ for log in daily_logs["logs"]:
100
+ log_date = log.get("date", "Unknown")
101
+ log_content = log.get("content", "")
102
+ # Show just the highlights, not full content
103
+ if len(log_content) > 1500:
104
+ log_content = log_content[:1500] + "\n...(truncated)"
105
+ context_parts.append(f"\n### {log_date}")
106
+ context_parts.append(log_content)
107
+
108
+ # ============================================================
109
+ # ORIGINAL MEMORY SYSTEM CONTEXT
110
+ # ============================================================
111
+
112
+ # 1. Get project info
113
+ project_info = await call_memory_skill("get_project_context", {
114
+ "project_path": project_path,
115
+ "limit": 5
116
+ })
117
+
118
+ if project_info and project_info.get("project"):
119
+ proj = project_info["project"]
120
+ context_parts.append(f"\n## Project: {proj.get('name', project_path)}")
121
+ if proj.get("tech_stack"):
122
+ context_parts.append(f"Tech Stack: {', '.join(proj['tech_stack'])}")
123
+ if proj.get("conventions"):
124
+ context_parts.append(f"Conventions: {json.dumps(proj['conventions'], indent=2)}")
125
+
126
+ # 2. Get recent decisions
127
+ decisions = await call_memory_skill("semantic_search", {
128
+ "query": "decision architecture approach",
129
+ "project_path": project_path,
130
+ "type": "decision",
131
+ "limit": 5
132
+ })
133
+
134
+ if decisions and decisions.get("results"):
135
+ context_parts.append("\n## Recent Decisions")
136
+ for d in decisions["results"][:3]:
137
+ context_parts.append(f"- {d['content'][:150]}")
138
+
139
+ # 3. Get recent errors (to avoid repeating)
140
+ errors = await call_memory_skill("semantic_search", {
141
+ "query": "error bug fix problem",
142
+ "project_path": project_path,
143
+ "type": "error",
144
+ "success_only": True, # Only get solved errors
145
+ "limit": 5
146
+ })
147
+
148
+ if errors and errors.get("results"):
149
+ context_parts.append("\n## Past Errors & Solutions")
150
+ for e in errors["results"][:3]:
151
+ context_parts.append(f"- {e['content'][:150]}")
152
+
153
+ # 4. Get session handoff (unresolved items)
154
+ handoff = await call_memory_skill("get_session_handoff", {
155
+ "project_path": project_path,
156
+ "include_last_n_sessions": 2
157
+ })
158
+
159
+ if handoff:
160
+ if handoff.get("unresolved_questions"):
161
+ context_parts.append("\n## Unresolved from Previous Sessions")
162
+ for q in handoff["unresolved_questions"][:3]:
163
+ context_parts.append(f"- {q}")
164
+
165
+ if handoff.get("recent_summaries"):
166
+ context_parts.append("\n## Recent Session Summaries")
167
+ for s in handoff["recent_summaries"][:2]:
168
+ context_parts.append(f"- {s.get('summary', '')[:200]}")
169
+
170
+ # 5. Get relevant patterns
171
+ patterns = await call_memory_skill("search_patterns", {
172
+ "query": "common patterns solutions",
173
+ "limit": 3
174
+ })
175
+
176
+ if patterns and patterns.get("patterns"):
177
+ context_parts.append("\n## Useful Patterns")
178
+ for p in patterns["patterns"][:2]:
179
+ context_parts.append(f"- **{p['name']}**: {p['solution'][:100]}")
180
+
181
+ # 6. Check for anchor conflicts
182
+ conflicts = await call_memory_skill("get_unresolved_conflicts", {
183
+ "project_path": project_path,
184
+ "limit": 3
185
+ })
186
+
187
+ if conflicts and conflicts.get("conflicts"):
188
+ context_parts.append("\n## Unresolved Fact Conflicts")
189
+ for c in conflicts["conflicts"]:
190
+ context_parts.append(f"- {c.get('anchor1_summary', '')} vs {c.get('anchor2_summary', '')}")
191
+
192
+ if context_parts:
193
+ return "\n".join(context_parts)
194
+ return ""
195
+
196
+
197
+ async def main():
198
+ # Get project path from environment or current directory
199
+ project_path = os.getenv("PROJECT_PATH") or os.getcwd()
200
+
201
+ # Try to read from stdin if available
202
+ try:
203
+ if not sys.stdin.isatty():
204
+ data = sys.stdin.read()
205
+ if data:
206
+ hook_data = json.loads(data)
207
+ project_path = hook_data.get("project_path", project_path)
208
+ except:
209
+ pass
210
+
211
+ context = await load_session_context(project_path)
212
+
213
+ if context:
214
+ # Output context for Claude to see
215
+ print("\n<memory-context>")
216
+ print("# Loaded from Memory System")
217
+ print(context)
218
+ print("</memory-context>\n")
219
+ else:
220
+ print("\n<memory-context>")
221
+ print("# No prior context found for this project")
222
+ print("Starting fresh session.")
223
+ print("</memory-context>\n")
224
+
225
+
226
+ if __name__ == "__main__":
227
+ asyncio.run(main())