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,221 @@
1
+ """Test suite for automation features."""
2
+ import asyncio
3
+ import sys
4
+ sys.path.insert(0, '.')
5
+
6
+ async def test_all():
7
+ print('=' * 60)
8
+ print('AUTOMATION FEATURES TEST SUITE')
9
+ print('=' * 60)
10
+
11
+ results = []
12
+
13
+ # Test 1: Natural Language Parser
14
+ print('\n[1] Testing Natural Language Parser...')
15
+ from skills.natural_language import parse_intent
16
+ tests = [
17
+ ('remember this: testing works', 'store'),
18
+ ('what did I learn about Python', 'search'),
19
+ ('show me past errors', 'list_errors'),
20
+ ('show me decisions', 'list_decisions'),
21
+ ('memory stats', 'stats'),
22
+ ('forget about old stuff', 'forget'),
23
+ ('show me patterns', 'list_patterns'),
24
+ ('project info', 'project_context'),
25
+ ('search for authentication', 'search'),
26
+ ]
27
+
28
+ passed = 0
29
+ for text, expected_intent in tests:
30
+ intent, _ = parse_intent(text)
31
+ if intent == expected_intent:
32
+ passed += 1
33
+ else:
34
+ print(f' FAIL: "{text}" -> got {intent}, expected {expected_intent}')
35
+
36
+ results.append(('Natural Language Parser', passed, len(tests)))
37
+ print(f' {passed}/{len(tests)} patterns correct')
38
+
39
+ # Test 2: Confidence Service
40
+ print('\n[2] Testing Confidence Service...')
41
+ from services.confidence import ConfidenceService
42
+ from datetime import datetime
43
+
44
+ class MockDB:
45
+ class Conn:
46
+ def cursor(self): return self
47
+ def execute(self, *args): pass
48
+ def fetchone(self): return None
49
+ def fetchall(self): return []
50
+ def commit(self): pass
51
+ conn = Conn()
52
+
53
+ cs = ConfidenceService(MockDB(), None)
54
+
55
+ age_score = cs._calculate_age_score(datetime.now().isoformat())
56
+ access_score = cs._calculate_access_score(10)
57
+ importance_score = cs._calculate_importance_score(8)
58
+
59
+ passed = 0
60
+ if 0.9 <= age_score <= 1.0: passed += 1
61
+ else: print(f' FAIL: age_score = {age_score}, expected 0.9-1.0')
62
+ if 0.5 <= access_score <= 1.0: passed += 1
63
+ else: print(f' FAIL: access_score = {access_score}, expected 0.5-1.0')
64
+ if importance_score == 0.8: passed += 1
65
+ else: print(f' FAIL: importance_score = {importance_score}, expected 0.8')
66
+
67
+ results.append(('Confidence Scoring', passed, 3))
68
+ print(f' {passed}/3 calculations correct')
69
+
70
+ # Test 3: Auto Injector
71
+ print('\n[3] Testing Auto Injector...')
72
+ from services.auto_inject import AutoInjector
73
+
74
+ ai = AutoInjector(MockDB(), None)
75
+
76
+ # Test keyword extraction
77
+ keywords = ai._extract_keywords('Fix the authentication bug in login.py')
78
+ passed = 0
79
+ if 'authentication' in keywords or 'login' in keywords: passed += 1
80
+ else: print(f' FAIL: Expected auth keywords, got {keywords}')
81
+
82
+ # Test should_inject logic
83
+ should = ai._should_inject('test query')
84
+ if should: passed += 1
85
+ else: print(f' FAIL: Should inject on first query')
86
+
87
+ # Test format
88
+ test_context = {
89
+ 'injected': True,
90
+ 'patterns': [{'name': 'Test Pattern', 'solution': 'Test solution'}],
91
+ 'memories': [{'type': 'decision', 'content': 'Decision 1'}],
92
+ 'warnings': []
93
+ }
94
+ formatted = ai.format_injection(test_context)
95
+ if 'Test Pattern' in formatted: passed += 1
96
+ else: print(f' FAIL: Pattern not in formatted output')
97
+
98
+ results.append(('Auto Injector', passed, 3))
99
+ print(f' {passed}/3 tests correct')
100
+
101
+ # Test 4: CLAUDE.md Sync
102
+ print('\n[4] Testing CLAUDE.md Sync...')
103
+ from services.claude_md_sync import ClaudeMdSync
104
+
105
+ cms = ClaudeMdSync(MockDB(), None)
106
+
107
+ test_md = """# Header
108
+ ## Preferences
109
+ - Pref 1
110
+ - Pref 2
111
+
112
+ ## Other Section
113
+ Content here
114
+ """
115
+ start, end = cms._find_section(test_md, 'Preferences')
116
+ passed = 0
117
+ if start > 0: passed += 1
118
+ else: print(f' FAIL: Could not find Preferences section')
119
+
120
+ if cms._content_exists(test_md, 'Pref 1'): passed += 1
121
+ else: print(f' FAIL: Should detect existing content')
122
+
123
+ if not cms._content_exists(test_md, 'New Content'): passed += 1
124
+ else: print(f' FAIL: Should not detect non-existing content')
125
+
126
+ results.append(('CLAUDE.md Sync', passed, 3))
127
+ print(f' {passed}/3 tests correct')
128
+
129
+ # Test 5: Hook Files Exist
130
+ print('\n[5] Testing Hook Files...')
131
+ import os
132
+ hook_dir = 'hooks'
133
+ hooks = ['auto_capture.py', 'session_start.py', 'session_end.py']
134
+ passed = 0
135
+ for hook in hooks:
136
+ path = os.path.join(hook_dir, hook)
137
+ if os.path.exists(path):
138
+ passed += 1
139
+ else:
140
+ print(f' FAIL: {path} not found')
141
+
142
+ results.append(('Hook Files', passed, len(hooks)))
143
+ print(f' {passed}/{len(hooks)} hooks exist')
144
+
145
+ # Test 6: Service Imports
146
+ print('\n[6] Testing Service Imports...')
147
+ passed = 0
148
+ try:
149
+ from services.auto_inject import get_auto_injector
150
+ passed += 1
151
+ except Exception as e:
152
+ print(f' FAIL: auto_inject import: {e}')
153
+
154
+ try:
155
+ from services.confidence import get_confidence_service
156
+ passed += 1
157
+ except Exception as e:
158
+ print(f' FAIL: confidence import: {e}')
159
+
160
+ try:
161
+ from services.claude_md_sync import get_claude_md_sync
162
+ passed += 1
163
+ except Exception as e:
164
+ print(f' FAIL: claude_md_sync import: {e}')
165
+
166
+ try:
167
+ from skills.natural_language import process_natural_command
168
+ passed += 1
169
+ except Exception as e:
170
+ print(f' FAIL: natural_language import: {e}')
171
+
172
+ results.append(('Service Imports', passed, 4))
173
+ print(f' {passed}/4 imports successful')
174
+
175
+ # Test 7: Main.py Endpoint Integration
176
+ print('\n[7] Testing Main.py Endpoints...')
177
+ passed = 0
178
+
179
+ with open('main.py', 'r') as f:
180
+ main_content = f.read()
181
+
182
+ endpoints_to_check = [
183
+ '/api/inject',
184
+ '/api/memory/natural',
185
+ '/api/memory/{memory_id}/confidence',
186
+ '/api/memory/{memory_id}/verify',
187
+ '/api/claude-md/sync',
188
+ '/api/claude-md/suggestions',
189
+ ]
190
+
191
+ for endpoint in endpoints_to_check:
192
+ if endpoint in main_content:
193
+ passed += 1
194
+ else:
195
+ print(f' FAIL: Endpoint {endpoint} not found in main.py')
196
+
197
+ results.append(('Main.py Endpoints', passed, len(endpoints_to_check)))
198
+ print(f' {passed}/{len(endpoints_to_check)} endpoints defined')
199
+
200
+ # Summary
201
+ print('\n' + '=' * 60)
202
+ print('SUMMARY')
203
+ print('=' * 60)
204
+
205
+ total_passed = sum(r[1] for r in results)
206
+ total_tests = sum(r[2] for r in results)
207
+
208
+ for name, passed, total in results:
209
+ status = 'PASS' if passed == total else 'PARTIAL' if passed > 0 else 'FAIL'
210
+ print(f' [{status}] {name}: {passed}/{total}')
211
+
212
+ pct = 100*total_passed//total_tests
213
+ print(f'\nTotal: {total_passed}/{total_tests} ({pct}%)')
214
+
215
+ if pct == 100:
216
+ print('\n*** ALL AUTOMATION TESTS PASSED! ***')
217
+
218
+ return total_passed == total_tests
219
+
220
+ if __name__ == '__main__':
221
+ asyncio.run(test_all())
@@ -0,0 +1,338 @@
1
+ """Complete test suite matching actual codebase structure."""
2
+ import asyncio
3
+ import sys
4
+ import os
5
+ sys.path.insert(0, '.')
6
+
7
+ async def test_all():
8
+ print('=' * 70)
9
+ print('COMPLETE MEMORY SYSTEM TEST SUITE')
10
+ print('=' * 70)
11
+
12
+ results = []
13
+
14
+ # ===== CORE SERVICES =====
15
+ print('\n' + '-' * 70)
16
+ print('SECTION A: CORE SERVICES')
17
+ print('-' * 70)
18
+
19
+ # [1] Database Service
20
+ print('\n[A1] Database Service...')
21
+ passed = 0
22
+ try:
23
+ from services.database import DatabaseService
24
+ db = DatabaseService()
25
+ await db.connect()
26
+ await db.initialize_schema()
27
+
28
+ cursor = db.conn.cursor()
29
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
30
+ tables = [r[0] for r in cursor.fetchall()]
31
+
32
+ key_tables = ['memories', 'patterns', 'projects', 'anchors', 'timeline']
33
+ for t in key_tables:
34
+ if t in tables:
35
+ passed += 1
36
+ except Exception as e:
37
+ print(f' ERROR: {e}')
38
+ results.append(('A1. Database', passed, 5))
39
+ print(f' {passed}/5 core tables exist')
40
+
41
+ # [2] Embeddings Service
42
+ print('\n[A2] Embeddings Service...')
43
+ passed = 0
44
+ try:
45
+ from services.embeddings import OllamaEmbeddings
46
+ passed += 1
47
+ except Exception as e:
48
+ print(f' ERROR: {e}')
49
+ results.append(('A2. Embeddings', passed, 1))
50
+ print(f' {passed}/1 class verified')
51
+
52
+ # [3] Auth Service
53
+ print('\n[A3] Auth Service...')
54
+ passed = 0
55
+ try:
56
+ from services.auth import AuthService
57
+ passed += 1
58
+ except Exception as e:
59
+ print(f' ERROR: {e}')
60
+ results.append(('A3. Auth', passed, 1))
61
+ print(f' {passed}/1 service verified')
62
+
63
+ # [4] Retry Queue
64
+ print('\n[A4] Retry Queue...')
65
+ passed = 0
66
+ try:
67
+ from services.retry_queue import RetryQueue
68
+ passed += 1
69
+ except Exception as e:
70
+ print(f' ERROR: {e}')
71
+ results.append(('A4. Retry Queue', passed, 1))
72
+ print(f' {passed}/1 service verified')
73
+
74
+ # [5] Cleanup Service
75
+ print('\n[A5] Cleanup Service...')
76
+ passed = 0
77
+ try:
78
+ from services.cleanup import CleanupService
79
+ passed += 1
80
+ except Exception as e:
81
+ print(f' ERROR: {e}')
82
+ results.append(('A5. Cleanup', passed, 1))
83
+ print(f' {passed}/1 service verified')
84
+
85
+ # [6] Insights Service
86
+ print('\n[A6] Insights Service...')
87
+ passed = 0
88
+ try:
89
+ from services.insights import InsightService
90
+ passed += 1
91
+ except Exception as e:
92
+ print(f' ERROR: {e}')
93
+ results.append(('A6. Insights', passed, 1))
94
+ print(f' {passed}/1 service verified')
95
+
96
+ # [7] WebSocket Service
97
+ print('\n[A7] WebSocket Service...')
98
+ passed = 0
99
+ try:
100
+ from services.websocket import WebSocketManager, broadcast_event, EventTypes
101
+ passed += 3
102
+ except Exception as e:
103
+ print(f' ERROR: {e}')
104
+ results.append(('A7. WebSocket', passed, 3))
105
+ print(f' {passed}/3 imports verified')
106
+
107
+ # ===== SKILLS =====
108
+ print('\n' + '-' * 70)
109
+ print('SECTION B: SKILLS')
110
+ print('-' * 70)
111
+
112
+ # [B1] Store/Search Skills
113
+ print('\n[B1] Store/Search Skills...')
114
+ passed = 0
115
+ try:
116
+ from skills.store import store_memory
117
+ passed += 1
118
+ except Exception as e:
119
+ print(f' ERROR (store): {e}')
120
+ try:
121
+ from skills.search import semantic_search
122
+ passed += 1
123
+ except Exception as e:
124
+ print(f' ERROR (search): {e}')
125
+ results.append(('B1. Store/Search', passed, 2))
126
+ print(f' {passed}/2 functions verified')
127
+
128
+ # [B2] Timeline Skills
129
+ print('\n[B2] Timeline Skills...')
130
+ passed = 0
131
+ try:
132
+ from skills.timeline import timeline_log, get_timeline, get_session_timeline
133
+ passed += 3
134
+ except Exception as e:
135
+ print(f' ERROR: {e}')
136
+ results.append(('B2. Timeline', passed, 3))
137
+ print(f' {passed}/3 functions verified')
138
+
139
+ # [B3] Grounding Skills
140
+ print('\n[B3] Grounding Skills...')
141
+ passed = 0
142
+ try:
143
+ from skills.grounding import mark_anchor, get_anchors, check_anchor_conflicts
144
+ passed += 3
145
+ except Exception as e:
146
+ print(f' ERROR: {e}')
147
+ results.append(('B3. Grounding', passed, 3))
148
+ print(f' {passed}/3 functions verified')
149
+
150
+ # [B4] Admin Skills
151
+ print('\n[B4] Admin Skills...')
152
+ passed = 0
153
+ try:
154
+ from skills.admin import get_embedding_status, switch_embedding_model
155
+ passed += 2
156
+ except Exception as e:
157
+ print(f' ERROR: {e}')
158
+ results.append(('B4. Admin', passed, 2))
159
+ print(f' {passed}/2 functions verified')
160
+
161
+ # ===== AUTOMATION FEATURES =====
162
+ print('\n' + '-' * 70)
163
+ print('SECTION C: AUTOMATION FEATURES (NEW)')
164
+ print('-' * 70)
165
+
166
+ # [C1] Auto-Capture Hook
167
+ print('\n[C1] Auto-Capture Hook...')
168
+ passed = 1 if os.path.exists('hooks/auto_capture.py') else 0
169
+ results.append(('C1. Auto-Capture', passed, 1))
170
+ print(f' {passed}/1 file exists')
171
+
172
+ # [C2] Session Hooks
173
+ print('\n[C2] Session Start/End Hooks...')
174
+ passed = 0
175
+ if os.path.exists('hooks/session_start.py'): passed += 1
176
+ if os.path.exists('hooks/session_end.py'): passed += 1
177
+ results.append(('C2. Session Hooks', passed, 2))
178
+ print(f' {passed}/2 files exist')
179
+
180
+ # [C3] Auto-Injector
181
+ print('\n[C3] Auto-Injector...')
182
+ passed = 0
183
+ try:
184
+ from services.auto_inject import AutoInjector, get_auto_injector
185
+ ai = AutoInjector(None, None)
186
+ kw = ai._extract_keywords('Fix authentication bug')
187
+ if 'authentication' in kw or 'fix' in kw:
188
+ passed += 1
189
+ if ai._should_inject('test'):
190
+ passed += 1
191
+ passed += 1 # Import works
192
+ except Exception as e:
193
+ print(f' ERROR: {e}')
194
+ results.append(('C3. Auto-Injector', passed, 3))
195
+ print(f' {passed}/3 tests passed')
196
+
197
+ # [C4] Natural Language Interface
198
+ print('\n[C4] Natural Language Interface...')
199
+ passed = 0
200
+ try:
201
+ from skills.natural_language import parse_intent
202
+ tests = [
203
+ ('remember this: test', 'store'),
204
+ ('show me past errors', 'list_errors'),
205
+ ('what did I learn about Python', 'search'),
206
+ ('memory stats', 'stats'),
207
+ ]
208
+ for text, expected in tests:
209
+ intent, _ = parse_intent(text)
210
+ if intent == expected:
211
+ passed += 1
212
+ except Exception as e:
213
+ print(f' ERROR: {e}')
214
+ results.append(('C4. Natural Language', passed, 4))
215
+ print(f' {passed}/4 patterns verified')
216
+
217
+ # [C5] CLAUDE.md Sync
218
+ print('\n[C5] CLAUDE.md Sync...')
219
+ passed = 0
220
+ try:
221
+ from services.claude_md_sync import ClaudeMdSync
222
+ cms = ClaudeMdSync(None, None)
223
+ # Test section finding
224
+ test_md = "# Test\n## Preferences\n- Item"
225
+ start, _ = cms._find_section(test_md, 'Preferences')
226
+ if start > 0: passed += 1
227
+ # Test content check
228
+ if cms._content_exists(test_md, 'Item'): passed += 1
229
+ if not cms._content_exists(test_md, 'Missing'): passed += 1
230
+ except Exception as e:
231
+ print(f' ERROR: {e}')
232
+ results.append(('C5. CLAUDE.md Sync', passed, 3))
233
+ print(f' {passed}/3 tests passed')
234
+
235
+ # [C6] Confidence Scoring
236
+ print('\n[C6] Confidence Scoring...')
237
+ passed = 0
238
+ try:
239
+ from services.confidence import ConfidenceService
240
+ from datetime import datetime
241
+
242
+ class MockDB:
243
+ class Conn:
244
+ def cursor(self): return self
245
+ conn = Conn()
246
+
247
+ cs = ConfidenceService(MockDB(), None)
248
+ age = cs._calculate_age_score(datetime.now().isoformat())
249
+ if 0.9 <= age <= 1.0: passed += 1
250
+ access = cs._calculate_access_score(5)
251
+ if 0.5 <= access <= 1.0: passed += 1
252
+ importance = cs._calculate_importance_score(10)
253
+ if importance == 1.0: passed += 1
254
+ except Exception as e:
255
+ print(f' ERROR: {e}')
256
+ results.append(('C6. Confidence', passed, 3))
257
+ print(f' {passed}/3 calculations verified')
258
+
259
+ # ===== INTEGRATION =====
260
+ print('\n' + '-' * 70)
261
+ print('SECTION D: INTEGRATION')
262
+ print('-' * 70)
263
+
264
+ # [D1] Main.py Endpoints
265
+ print('\n[D1] API Endpoints in main.py...')
266
+ passed = 0
267
+ with open('main.py', 'r') as f:
268
+ content = f.read()
269
+
270
+ endpoints = [
271
+ '/health', '/api/stats',
272
+ '/api/inject', '/api/memory/natural',
273
+ '/api/memory/{memory_id}/confidence',
274
+ '/api/claude-md/sync', '/ws',
275
+ ]
276
+ for ep in endpoints:
277
+ if ep in content:
278
+ passed += 1
279
+ results.append(('D1. API Endpoints', passed, len(endpoints)))
280
+ print(f' {passed}/{len(endpoints)} endpoints defined')
281
+
282
+ # [D2] MCP Tool Handlers
283
+ print('\n[D2] MCP Tool Handlers...')
284
+ passed = 0
285
+ handlers = [
286
+ 'memory_store', 'memory_search', 'memory_context',
287
+ 'timeline_log', 'mark_anchor',
288
+ ]
289
+ for h in handlers:
290
+ if h in content:
291
+ passed += 1
292
+ results.append(('D2. MCP Handlers', passed, len(handlers)))
293
+ print(f' {passed}/{len(handlers)} handlers defined')
294
+
295
+ # ===== SUMMARY =====
296
+ print('\n' + '=' * 70)
297
+ print('FINAL SUMMARY')
298
+ print('=' * 70)
299
+
300
+ sections = {
301
+ 'A': 'Core Services',
302
+ 'B': 'Skills',
303
+ 'C': 'Automation (NEW)',
304
+ 'D': 'Integration'
305
+ }
306
+
307
+ total_passed = 0
308
+ total_tests = 0
309
+
310
+ for section_key, section_name in sections.items():
311
+ section_results = [(n, p, t) for n, p, t in results if n.startswith(section_key)]
312
+ s_passed = sum(r[1] for r in section_results)
313
+ s_total = sum(r[2] for r in section_results)
314
+ total_passed += s_passed
315
+ total_tests += s_total
316
+
317
+ pct = 100 * s_passed // s_total if s_total > 0 else 0
318
+ print(f'\n{section_name}: {s_passed}/{s_total} ({pct}%)')
319
+ for name, passed, total in section_results:
320
+ status = 'PASS' if passed == total else 'PARTIAL' if passed > 0 else 'FAIL'
321
+ print(f' [{status}] {name}: {passed}/{total}')
322
+
323
+ pct = 100 * total_passed // total_tests if total_tests > 0 else 0
324
+ print(f'\n{"=" * 70}')
325
+ print(f'OVERALL: {total_passed}/{total_tests} ({pct}%)')
326
+ print(f'{"=" * 70}')
327
+
328
+ if pct == 100:
329
+ print('\n*** ALL TESTS PASSED! MEMORY SYSTEM FULLY OPERATIONAL ***')
330
+ elif pct >= 90:
331
+ print('\n*** MEMORY SYSTEM READY FOR PRODUCTION ***')
332
+ elif pct >= 75:
333
+ print('\n*** MEMORY SYSTEM READY FOR USE ***')
334
+
335
+ return pct
336
+
337
+ if __name__ == '__main__':
338
+ asyncio.run(test_all())