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.
- package/.env.example +107 -0
- package/README.md +200 -0
- package/agent_card.py +512 -0
- package/bin/cli.js +181 -0
- package/bin/postinstall.js +216 -0
- package/config.py +104 -0
- package/dashboard.html +2689 -0
- package/hooks/README.md +196 -0
- package/hooks/__pycache__/auto-detect-response.cpython-312.pyc +0 -0
- package/hooks/__pycache__/auto_capture.cpython-312.pyc +0 -0
- package/hooks/__pycache__/session_end.cpython-312.pyc +0 -0
- package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
- package/hooks/auto-detect-response.py +348 -0
- package/hooks/auto_capture.py +255 -0
- package/hooks/detect-correction.py +173 -0
- package/hooks/grounding-hook.py +348 -0
- package/hooks/log-tool-use.py +234 -0
- package/hooks/log-user-request.py +208 -0
- package/hooks/pre-tool-decision.py +218 -0
- package/hooks/problem-detector.py +343 -0
- package/hooks/session_end.py +192 -0
- package/hooks/session_start.py +227 -0
- package/install.py +887 -0
- package/main.py +2859 -0
- package/manager.py +997 -0
- package/package.json +55 -0
- package/requirements.txt +8 -0
- package/run_server.py +136 -0
- package/services/__init__.py +50 -0
- package/services/__pycache__/__init__.cpython-312.pyc +0 -0
- package/services/__pycache__/agent_registry.cpython-312.pyc +0 -0
- package/services/__pycache__/auth.cpython-312.pyc +0 -0
- package/services/__pycache__/auto_inject.cpython-312.pyc +0 -0
- package/services/__pycache__/claude_md_sync.cpython-312.pyc +0 -0
- package/services/__pycache__/cleanup.cpython-312.pyc +0 -0
- package/services/__pycache__/compaction_flush.cpython-312.pyc +0 -0
- package/services/__pycache__/confidence.cpython-312.pyc +0 -0
- package/services/__pycache__/daily_log.cpython-312.pyc +0 -0
- package/services/__pycache__/database.cpython-312.pyc +0 -0
- package/services/__pycache__/embeddings.cpython-312.pyc +0 -0
- package/services/__pycache__/insights.cpython-312.pyc +0 -0
- package/services/__pycache__/llm_analyzer.cpython-312.pyc +0 -0
- package/services/__pycache__/memory_md_sync.cpython-312.pyc +0 -0
- package/services/__pycache__/retry_queue.cpython-312.pyc +0 -0
- package/services/__pycache__/timeline.cpython-312.pyc +0 -0
- package/services/__pycache__/vector_index.cpython-312.pyc +0 -0
- package/services/__pycache__/websocket.cpython-312.pyc +0 -0
- package/services/agent_registry.py +753 -0
- package/services/auth.py +331 -0
- package/services/auto_inject.py +250 -0
- package/services/claude_md_sync.py +275 -0
- package/services/cleanup.py +667 -0
- package/services/compaction_flush.py +447 -0
- package/services/confidence.py +301 -0
- package/services/daily_log.py +333 -0
- package/services/database.py +2485 -0
- package/services/embeddings.py +358 -0
- package/services/insights.py +632 -0
- package/services/llm_analyzer.py +595 -0
- package/services/memory_md_sync.py +409 -0
- package/services/retry_queue.py +453 -0
- package/services/timeline.py +579 -0
- package/services/vector_index.py +398 -0
- package/services/websocket.py +257 -0
- package/skills/__init__.py +6 -0
- package/skills/__pycache__/__init__.cpython-312.pyc +0 -0
- package/skills/__pycache__/admin.cpython-312.pyc +0 -0
- package/skills/__pycache__/checkpoint.cpython-312.pyc +0 -0
- package/skills/__pycache__/claude_md.cpython-312.pyc +0 -0
- package/skills/__pycache__/cleanup.cpython-312.pyc +0 -0
- package/skills/__pycache__/grounding.cpython-312.pyc +0 -0
- package/skills/__pycache__/insights.cpython-312.pyc +0 -0
- package/skills/__pycache__/natural_language.cpython-312.pyc +0 -0
- package/skills/__pycache__/retrieve.cpython-312.pyc +0 -0
- package/skills/__pycache__/search.cpython-312.pyc +0 -0
- package/skills/__pycache__/state.cpython-312.pyc +0 -0
- package/skills/__pycache__/store.cpython-312.pyc +0 -0
- package/skills/__pycache__/summarize.cpython-312.pyc +0 -0
- package/skills/__pycache__/timeline.cpython-312.pyc +0 -0
- package/skills/__pycache__/verification.cpython-312.pyc +0 -0
- package/skills/admin.py +469 -0
- package/skills/checkpoint.py +198 -0
- package/skills/claude_md.py +363 -0
- package/skills/cleanup.py +241 -0
- package/skills/grounding.py +801 -0
- package/skills/insights.py +231 -0
- package/skills/natural_language.py +277 -0
- package/skills/retrieve.py +67 -0
- package/skills/search.py +213 -0
- package/skills/state.py +182 -0
- package/skills/store.py +179 -0
- package/skills/summarize.py +588 -0
- package/skills/timeline.py +387 -0
- package/skills/verification.py +391 -0
- package/start_daemon.py +155 -0
- package/test_automation.py +221 -0
- package/test_complete.py +338 -0
- package/test_full.py +322 -0
- package/update_system.py +817 -0
- 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())
|