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,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())
|
package/test_complete.py
ADDED
|
@@ -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())
|