claude-memory-agent 2.0.1 → 2.2.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/README.md +206 -206
- package/agent_card.py +186 -0
- package/bin/cli.js +327 -185
- package/bin/lib/banner.js +39 -0
- package/bin/lib/environment.js +166 -0
- package/bin/lib/installer.js +291 -0
- package/bin/lib/models.js +95 -0
- package/bin/lib/steps/advanced.js +101 -0
- package/bin/lib/steps/confirm.js +87 -0
- package/bin/lib/steps/model.js +57 -0
- package/bin/lib/steps/provider.js +65 -0
- package/bin/lib/steps/scope.js +59 -0
- package/bin/lib/steps/server.js +74 -0
- package/bin/lib/ui.js +75 -0
- package/bin/onboarding.js +164 -0
- package/bin/postinstall.js +35 -270
- package/config.py +103 -4
- package/dashboard.html +4902 -2689
- package/hooks/extract_memories.py +439 -0
- package/hooks/grounding-hook.py +422 -348
- package/hooks/pre_compact_hook.py +76 -0
- package/hooks/session_end.py +293 -192
- package/hooks/session_end_hook.py +149 -0
- package/hooks/session_start.py +227 -227
- package/hooks/stop_hook.py +372 -0
- package/install.py +972 -902
- package/main.py +5240 -2859
- package/mcp_server.py +451 -0
- package/package.json +58 -47
- package/requirements.txt +12 -8
- package/services/__init__.py +50 -50
- package/services/adaptive_ranker.py +272 -0
- package/services/agent_catalog.json +153 -0
- package/services/agent_registry.py +245 -730
- package/services/claude_md_sync.py +320 -4
- package/services/consolidation.py +417 -0
- package/services/curator.py +1606 -0
- package/services/database.py +4118 -2485
- package/services/embedding_pipeline.py +262 -0
- package/services/embeddings.py +493 -85
- package/services/memory_decay.py +408 -0
- package/services/native_memory_paths.py +86 -0
- package/services/native_memory_sync.py +496 -0
- package/services/response_manager.py +183 -0
- package/services/terminal_ui.py +199 -0
- package/services/tier_manager.py +235 -0
- package/services/websocket.py +26 -6
- package/skills/__init__.py +21 -1
- package/skills/confidence_tracker.py +441 -0
- package/skills/context.py +675 -0
- package/skills/curator.py +348 -0
- package/skills/search.py +444 -213
- package/skills/session_review.py +605 -0
- package/skills/store.py +484 -179
- package/terminal_dashboard.py +474 -0
- package/update_system.py +829 -817
- 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/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/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/test_automation.py +0 -221
- package/test_complete.py +0 -338
- package/test_full.py +0 -322
- package/verify_db.py +0 -134
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PreCompact hook for Claude Code.
|
|
4
|
+
|
|
5
|
+
Called before context compaction. Extracts memories from the conversation
|
|
6
|
+
transcript so important information is preserved even if compaction
|
|
7
|
+
discards conversation turns.
|
|
8
|
+
|
|
9
|
+
This script:
|
|
10
|
+
1. Reads hook JSON from stdin (session_id, transcript_path, etc.)
|
|
11
|
+
2. Delegates to extract_memories.py for the actual extraction
|
|
12
|
+
3. Exits 0 on success OR failure (never blocks compaction)
|
|
13
|
+
|
|
14
|
+
Timing budget: < 5 seconds total.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import time
|
|
21
|
+
|
|
22
|
+
# Ensure the hooks directory is on the path
|
|
23
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main():
|
|
27
|
+
start = time.time()
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Read hook data from stdin
|
|
31
|
+
hook_data = {}
|
|
32
|
+
if not sys.stdin.isatty():
|
|
33
|
+
raw = sys.stdin.read()
|
|
34
|
+
if raw.strip():
|
|
35
|
+
hook_data = json.loads(raw)
|
|
36
|
+
|
|
37
|
+
# Ensure hook_event_name is set
|
|
38
|
+
if "hook_event_name" not in hook_data:
|
|
39
|
+
hook_data["hook_event_name"] = "PreCompact"
|
|
40
|
+
|
|
41
|
+
session_id = hook_data.get("session_id", "")
|
|
42
|
+
transcript_path = hook_data.get("transcript_path", "")
|
|
43
|
+
|
|
44
|
+
if not transcript_path:
|
|
45
|
+
# No transcript available, nothing to extract
|
|
46
|
+
print("[PreCompact] No transcript_path provided, skipping extraction.", file=sys.stderr)
|
|
47
|
+
sys.exit(0)
|
|
48
|
+
|
|
49
|
+
# Import and run extraction
|
|
50
|
+
from extract_memories import run_extraction
|
|
51
|
+
|
|
52
|
+
results = run_extraction(
|
|
53
|
+
session_id=session_id,
|
|
54
|
+
transcript_path=transcript_path,
|
|
55
|
+
project_path=hook_data.get("cwd") or hook_data.get("project_path", ""),
|
|
56
|
+
is_session_end=False,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
elapsed = round(time.time() - start, 2)
|
|
60
|
+
print(
|
|
61
|
+
f"[PreCompact] Extraction complete: "
|
|
62
|
+
f"extracted={results['extracted']} stored={results['stored']} "
|
|
63
|
+
f"errors={results['errors']} total_time={elapsed}s",
|
|
64
|
+
file=sys.stderr,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
elapsed = round(time.time() - start, 2)
|
|
69
|
+
print(f"[PreCompact] Error (non-fatal): {e} [{elapsed}s]", file=sys.stderr)
|
|
70
|
+
|
|
71
|
+
# Always exit 0 - never block compaction
|
|
72
|
+
sys.exit(0)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
main()
|
package/hooks/session_end.py
CHANGED
|
@@ -1,192 +1,293 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Session end hook - auto-summarizes and stores session.
|
|
3
|
-
|
|
4
|
-
This hook runs when a Claude Code session ends and:
|
|
5
|
-
- Summarizes the session automatically
|
|
6
|
-
- Stores important decisions and learnings
|
|
7
|
-
- Updates project insights
|
|
8
|
-
- Syncs to CLAUDE.md if needed
|
|
9
|
-
- Appends session summary to daily log (Moltbot-inspired)
|
|
10
|
-
- Triggers MEMORY.md sync (Moltbot-inspired)
|
|
11
|
-
- Executes pre-compaction flush (Moltbot-inspired)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
|
|
25
|
-
from
|
|
26
|
-
from
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if
|
|
192
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Session end hook - auto-summarizes and stores session.
|
|
3
|
+
|
|
4
|
+
This hook runs when a Claude Code session ends and:
|
|
5
|
+
- Summarizes the session automatically
|
|
6
|
+
- Stores important decisions and learnings
|
|
7
|
+
- Updates project insights
|
|
8
|
+
- Syncs to CLAUDE.md if needed
|
|
9
|
+
- Appends session summary to daily log (Moltbot-inspired)
|
|
10
|
+
- Triggers MEMORY.md sync (Moltbot-inspired)
|
|
11
|
+
- Executes pre-compaction flush (Moltbot-inspired)
|
|
12
|
+
- Outputs session review summary for user verification
|
|
13
|
+
|
|
14
|
+
Configure in Claude Code settings:
|
|
15
|
+
{
|
|
16
|
+
"hooks": {
|
|
17
|
+
"SessionEnd": ["python /path/to/session_end.py"]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
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, Optional, List
|
|
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-end-{datetime.now().isoformat()}"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
async with httpx.AsyncClient(timeout=15.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:
|
|
64
|
+
pass
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def call_api(endpoint: str, method: str = "GET", params: Dict = None) -> Optional[Dict[str, Any]]:
|
|
69
|
+
"""Call a memory agent REST API endpoint."""
|
|
70
|
+
headers = {"Content-Type": "application/json"}
|
|
71
|
+
if API_KEY:
|
|
72
|
+
headers["X-Memory-Key"] = API_KEY
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
76
|
+
url = f"{MEMORY_AGENT_URL}{endpoint}"
|
|
77
|
+
if method == "GET":
|
|
78
|
+
response = await client.get(url, headers=headers, params=params)
|
|
79
|
+
else:
|
|
80
|
+
response = await client.post(url, headers=headers, json=params)
|
|
81
|
+
|
|
82
|
+
if response.status_code == 200:
|
|
83
|
+
return response.json()
|
|
84
|
+
except Exception:
|
|
85
|
+
pass
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def get_session_review_summary(session_id: str) -> Dict[str, Any]:
|
|
90
|
+
"""Get a summary of session memories for review."""
|
|
91
|
+
# Get session memories
|
|
92
|
+
memories = await call_api(f"/api/session/{session_id}/memories")
|
|
93
|
+
if not memories or not memories.get("success"):
|
|
94
|
+
return {"success": False, "memory_count": 0}
|
|
95
|
+
|
|
96
|
+
# Get suggestions for review
|
|
97
|
+
suggestions = await call_api(f"/api/session/{session_id}/suggestions")
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
"success": True,
|
|
101
|
+
"memory_count": memories.get("memory_count", 0),
|
|
102
|
+
"memories": memories.get("memories", []),
|
|
103
|
+
"summary": memories.get("summary", {}),
|
|
104
|
+
"suggestions": suggestions.get("suggestions", []) if suggestions else [],
|
|
105
|
+
"suggestion_summary": suggestions.get("summary", {}) if suggestions else {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def format_review_output(review_data: Dict[str, Any], session_id: str) -> List[str]:
|
|
110
|
+
"""Format the session review data for output."""
|
|
111
|
+
lines = []
|
|
112
|
+
|
|
113
|
+
memory_count = review_data.get("memory_count", 0)
|
|
114
|
+
if memory_count == 0:
|
|
115
|
+
return ["No memories created this session"]
|
|
116
|
+
|
|
117
|
+
lines.append(f"Session created {memory_count} memories for review")
|
|
118
|
+
|
|
119
|
+
# Type breakdown
|
|
120
|
+
summary = review_data.get("summary", {})
|
|
121
|
+
by_type = summary.get("by_type", {})
|
|
122
|
+
if by_type:
|
|
123
|
+
type_parts = [f"{count} {mtype}" for mtype, count in by_type.items()]
|
|
124
|
+
lines.append(f" Types: {', '.join(type_parts)}")
|
|
125
|
+
|
|
126
|
+
# Suggestion summary
|
|
127
|
+
sugg_summary = review_data.get("suggestion_summary", {})
|
|
128
|
+
if sugg_summary:
|
|
129
|
+
keep = sugg_summary.get("suggested_keep", 0)
|
|
130
|
+
discard = sugg_summary.get("suggested_discard", 0)
|
|
131
|
+
partial = sugg_summary.get("suggested_partial", 0)
|
|
132
|
+
lines.append(f" Suggestions: {keep} keep, {partial} review, {discard} discard")
|
|
133
|
+
|
|
134
|
+
# List high-importance memories
|
|
135
|
+
memories = review_data.get("memories", [])
|
|
136
|
+
high_importance = [m for m in memories if m.get("importance", 5) >= 7]
|
|
137
|
+
if high_importance:
|
|
138
|
+
lines.append("")
|
|
139
|
+
lines.append(" High-importance memories to verify:")
|
|
140
|
+
for m in high_importance[:5]:
|
|
141
|
+
mtype = m.get("type", "chunk")
|
|
142
|
+
content = m.get("content", "")[:60]
|
|
143
|
+
importance = m.get("importance", 5)
|
|
144
|
+
lines.append(f" [{mtype}] (imp:{importance}) {content}...")
|
|
145
|
+
|
|
146
|
+
# Review URL
|
|
147
|
+
lines.append("")
|
|
148
|
+
lines.append(f" Review at: {MEMORY_AGENT_URL}/dashboard#review/{session_id}")
|
|
149
|
+
|
|
150
|
+
return lines
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
async def end_session(session_id: str, project_path: str):
|
|
154
|
+
"""Handle session end - summarize and store."""
|
|
155
|
+
results = []
|
|
156
|
+
session_data = {
|
|
157
|
+
"decisions": [],
|
|
158
|
+
"accomplishments": [],
|
|
159
|
+
"errors_solved": [],
|
|
160
|
+
"notes": []
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# 1. Auto-summarize the session
|
|
164
|
+
summary = await call_memory_skill("auto_summarize_session", {
|
|
165
|
+
"session_id": session_id,
|
|
166
|
+
"project_path": project_path
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
if summary and summary.get("success"):
|
|
170
|
+
results.append(f"Session summarized: {summary.get('summary', '')[:100]}...")
|
|
171
|
+
# Extract decisions and accomplishments from summary if available
|
|
172
|
+
if summary.get("key_decisions"):
|
|
173
|
+
session_data["decisions"] = summary["key_decisions"][:5]
|
|
174
|
+
|
|
175
|
+
# 2. Create diary entry
|
|
176
|
+
diary = await call_memory_skill("create_diary_entry", {
|
|
177
|
+
"session_id": session_id,
|
|
178
|
+
"project_path": project_path
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
if diary and diary.get("success"):
|
|
182
|
+
results.append(f"Diary entry created: ID {diary.get('memory_id')}")
|
|
183
|
+
|
|
184
|
+
# 3. Run insight aggregation
|
|
185
|
+
insights = await call_memory_skill("run_aggregation", {
|
|
186
|
+
"project_path": project_path
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
if insights and insights.get("success"):
|
|
190
|
+
new_insights = insights.get("new_insights", 0)
|
|
191
|
+
if new_insights > 0:
|
|
192
|
+
results.append(f"Generated {new_insights} new insights")
|
|
193
|
+
|
|
194
|
+
# 4. Check for CLAUDE.md suggestions
|
|
195
|
+
suggestions = await call_memory_skill("suggest_improvements", {
|
|
196
|
+
"project_path": project_path
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
if suggestions and suggestions.get("suggestions"):
|
|
200
|
+
results.append(f"CLAUDE.md suggestions: {len(suggestions['suggestions'])} available")
|
|
201
|
+
|
|
202
|
+
# 5. Auto-resolve any obvious anchor conflicts
|
|
203
|
+
resolved = await call_memory_skill("auto_resolve_conflicts", {
|
|
204
|
+
"project_path": project_path
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
if resolved and resolved.get("resolved_count", 0) > 0:
|
|
208
|
+
results.append(f"Auto-resolved {resolved['resolved_count']} conflicts")
|
|
209
|
+
|
|
210
|
+
# ============================================================
|
|
211
|
+
# MOLTBOT-INSPIRED FEATURES
|
|
212
|
+
# ============================================================
|
|
213
|
+
|
|
214
|
+
# 6. Append session summary to daily log
|
|
215
|
+
daily_log = await call_memory_skill("daily_log_append_session", {
|
|
216
|
+
"project_path": project_path,
|
|
217
|
+
"session_id": session_id,
|
|
218
|
+
"decisions": session_data["decisions"],
|
|
219
|
+
"accomplishments": session_data["accomplishments"],
|
|
220
|
+
"errors_solved": session_data["errors_solved"],
|
|
221
|
+
"notes": session_data["notes"]
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
if daily_log and daily_log.get("success"):
|
|
225
|
+
results.append(f"Daily log updated: {daily_log.get('file_path', 'unknown')}")
|
|
226
|
+
|
|
227
|
+
# 7. Sync MEMORY.md with high-importance items
|
|
228
|
+
memory_md = await call_memory_skill("sync_memory_md", {
|
|
229
|
+
"project_path": project_path,
|
|
230
|
+
"min_importance": 7,
|
|
231
|
+
"min_pattern_success": 3
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
if memory_md and memory_md.get("success"):
|
|
235
|
+
counts = memory_md.get("counts", {})
|
|
236
|
+
total_synced = sum(counts.values())
|
|
237
|
+
if total_synced > 0:
|
|
238
|
+
results.append(f"MEMORY.md synced: {total_synced} items")
|
|
239
|
+
|
|
240
|
+
# 8. Execute pre-compaction flush
|
|
241
|
+
flush = await call_memory_skill("pre_compaction_flush", {
|
|
242
|
+
"project_path": project_path,
|
|
243
|
+
"session_id": session_id
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
if flush and flush.get("success"):
|
|
247
|
+
results.append(f"Memory flush created: {flush.get('file_path', 'unknown')}")
|
|
248
|
+
|
|
249
|
+
# ============================================================
|
|
250
|
+
# SESSION REVIEW SUMMARY
|
|
251
|
+
# ============================================================
|
|
252
|
+
|
|
253
|
+
# 9. Get session review summary for user verification
|
|
254
|
+
review_data = await get_session_review_summary(session_id)
|
|
255
|
+
if review_data.get("success") and review_data.get("memory_count", 0) > 0:
|
|
256
|
+
review_lines = format_review_output(review_data, session_id)
|
|
257
|
+
results.append("")
|
|
258
|
+
results.append("--- Session Memory Review ---")
|
|
259
|
+
results.extend(review_lines)
|
|
260
|
+
|
|
261
|
+
return results
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
async def main():
|
|
265
|
+
session_id = os.getenv("SESSION_ID") or f"session-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
266
|
+
project_path = os.getenv("PROJECT_PATH") or os.getcwd()
|
|
267
|
+
|
|
268
|
+
# Try to read from stdin
|
|
269
|
+
try:
|
|
270
|
+
if not sys.stdin.isatty():
|
|
271
|
+
data = sys.stdin.read()
|
|
272
|
+
if data:
|
|
273
|
+
hook_data = json.loads(data)
|
|
274
|
+
session_id = hook_data.get("session_id", session_id)
|
|
275
|
+
project_path = hook_data.get("project_path", project_path)
|
|
276
|
+
except:
|
|
277
|
+
pass
|
|
278
|
+
|
|
279
|
+
results = await end_session(session_id, project_path)
|
|
280
|
+
|
|
281
|
+
if results:
|
|
282
|
+
print("\n[Memory System] Session ended:")
|
|
283
|
+
for r in results:
|
|
284
|
+
if r.startswith("---") or r.startswith(" "):
|
|
285
|
+
print(r)
|
|
286
|
+
else:
|
|
287
|
+
print(f" - {r}")
|
|
288
|
+
else:
|
|
289
|
+
print("\n[Memory System] Session ended (no data captured)")
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
if __name__ == "__main__":
|
|
293
|
+
asyncio.run(main())
|