memorygraphMCP 0.11.7__py3-none-any.whl

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 (65) hide show
  1. memorygraph/__init__.py +50 -0
  2. memorygraph/__main__.py +12 -0
  3. memorygraph/advanced_tools.py +509 -0
  4. memorygraph/analytics/__init__.py +46 -0
  5. memorygraph/analytics/advanced_queries.py +727 -0
  6. memorygraph/backends/__init__.py +21 -0
  7. memorygraph/backends/base.py +179 -0
  8. memorygraph/backends/cloud.py +75 -0
  9. memorygraph/backends/cloud_backend.py +858 -0
  10. memorygraph/backends/factory.py +577 -0
  11. memorygraph/backends/falkordb_backend.py +749 -0
  12. memorygraph/backends/falkordblite_backend.py +746 -0
  13. memorygraph/backends/ladybugdb_backend.py +242 -0
  14. memorygraph/backends/memgraph_backend.py +327 -0
  15. memorygraph/backends/neo4j_backend.py +298 -0
  16. memorygraph/backends/sqlite_fallback.py +463 -0
  17. memorygraph/backends/turso.py +448 -0
  18. memorygraph/cli.py +743 -0
  19. memorygraph/cloud_database.py +297 -0
  20. memorygraph/config.py +295 -0
  21. memorygraph/database.py +933 -0
  22. memorygraph/graph_analytics.py +631 -0
  23. memorygraph/integration/__init__.py +69 -0
  24. memorygraph/integration/context_capture.py +426 -0
  25. memorygraph/integration/project_analysis.py +583 -0
  26. memorygraph/integration/workflow_tracking.py +492 -0
  27. memorygraph/intelligence/__init__.py +59 -0
  28. memorygraph/intelligence/context_retrieval.py +447 -0
  29. memorygraph/intelligence/entity_extraction.py +386 -0
  30. memorygraph/intelligence/pattern_recognition.py +420 -0
  31. memorygraph/intelligence/temporal.py +374 -0
  32. memorygraph/migration/__init__.py +27 -0
  33. memorygraph/migration/manager.py +579 -0
  34. memorygraph/migration/models.py +142 -0
  35. memorygraph/migration/scripts/__init__.py +17 -0
  36. memorygraph/migration/scripts/bitemporal_migration.py +595 -0
  37. memorygraph/migration/scripts/multitenancy_migration.py +452 -0
  38. memorygraph/migration_tools_module.py +146 -0
  39. memorygraph/models.py +684 -0
  40. memorygraph/proactive/__init__.py +46 -0
  41. memorygraph/proactive/outcome_learning.py +444 -0
  42. memorygraph/proactive/predictive.py +410 -0
  43. memorygraph/proactive/session_briefing.py +399 -0
  44. memorygraph/relationships.py +668 -0
  45. memorygraph/server.py +883 -0
  46. memorygraph/sqlite_database.py +1876 -0
  47. memorygraph/tools/__init__.py +59 -0
  48. memorygraph/tools/activity_tools.py +262 -0
  49. memorygraph/tools/memory_tools.py +315 -0
  50. memorygraph/tools/migration_tools.py +181 -0
  51. memorygraph/tools/relationship_tools.py +147 -0
  52. memorygraph/tools/search_tools.py +406 -0
  53. memorygraph/tools/temporal_tools.py +339 -0
  54. memorygraph/utils/__init__.py +10 -0
  55. memorygraph/utils/context_extractor.py +429 -0
  56. memorygraph/utils/error_handling.py +151 -0
  57. memorygraph/utils/export_import.py +425 -0
  58. memorygraph/utils/graph_algorithms.py +200 -0
  59. memorygraph/utils/pagination.py +149 -0
  60. memorygraph/utils/project_detection.py +133 -0
  61. memorygraphmcp-0.11.7.dist-info/METADATA +970 -0
  62. memorygraphmcp-0.11.7.dist-info/RECORD +65 -0
  63. memorygraphmcp-0.11.7.dist-info/WHEEL +4 -0
  64. memorygraphmcp-0.11.7.dist-info/entry_points.txt +2 -0
  65. memorygraphmcp-0.11.7.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,399 @@
1
+ """
2
+ Session Start Intelligence for Claude Code Memory Server.
3
+
4
+ Provides automatic briefing when Claude Code starts, including:
5
+ - Recent project activity
6
+ - Unresolved problems
7
+ - Relevant patterns
8
+ - Deprecation warnings
9
+ - Recommended next steps
10
+
11
+ Phase 7 Implementation - Session Start Intelligence
12
+ """
13
+
14
+ from datetime import datetime, timedelta
15
+ from typing import Optional, List, Dict, Any
16
+ from dataclasses import dataclass
17
+ import logging
18
+
19
+ from pydantic import BaseModel, Field
20
+
21
+ from ..backends.base import GraphBackend
22
+ from ..models import Memory, MemoryType, RelationshipType
23
+ from ..integration.project_analysis import detect_project, ProjectInfo
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class RecentActivity(BaseModel):
29
+ """Recent activity in a project."""
30
+
31
+ memory_id: str
32
+ memory_type: str
33
+ title: str
34
+ summary: Optional[str] = None
35
+ timestamp: datetime
36
+ tags: List[str] = Field(default_factory=list)
37
+
38
+
39
+ class UnresolvedProblem(BaseModel):
40
+ """Unresolved problem without a solution."""
41
+
42
+ problem_id: str
43
+ title: str
44
+ description: str
45
+ created_at: datetime
46
+ tags: List[str] = Field(default_factory=list)
47
+ related_memories: int = 0
48
+
49
+
50
+ class RelevantPattern(BaseModel):
51
+ """Relevant pattern for current project."""
52
+
53
+ pattern_id: str
54
+ pattern_type: str
55
+ description: str
56
+ effectiveness: float = 0.5
57
+ usage_count: int = 0
58
+ last_used: Optional[datetime] = None
59
+
60
+
61
+ class DeprecationWarning(BaseModel):
62
+ """Warning about deprecated approaches."""
63
+
64
+ deprecated_id: str
65
+ deprecated_title: str
66
+ reason: str
67
+ replacement_id: Optional[str] = None
68
+ replacement_title: Optional[str] = None
69
+
70
+
71
+ class SessionBriefing(BaseModel):
72
+ """
73
+ Complete session briefing for a project.
74
+
75
+ Provides developers with relevant context when starting work.
76
+ """
77
+
78
+ project_name: str
79
+ project_path: str
80
+ project_type: str
81
+ briefing_timestamp: datetime = Field(default_factory=datetime.now)
82
+
83
+ # Activity
84
+ recent_activities: List[RecentActivity] = Field(default_factory=list)
85
+ total_memories: int = 0
86
+
87
+ # Issues
88
+ unresolved_problems: List[UnresolvedProblem] = Field(default_factory=list)
89
+
90
+ # Recommendations
91
+ relevant_patterns: List[RelevantPattern] = Field(default_factory=list)
92
+
93
+ # Warnings
94
+ deprecation_warnings: List[DeprecationWarning] = Field(default_factory=list)
95
+
96
+ # Summary
97
+ has_active_issues: bool = False
98
+ has_warnings: bool = False
99
+
100
+ def format_as_text(self, verbosity: str = "standard") -> str:
101
+ """
102
+ Format briefing as human-readable text.
103
+
104
+ Args:
105
+ verbosity: "minimal", "standard", or "detailed"
106
+
107
+ Returns:
108
+ Formatted briefing text
109
+ """
110
+ lines = []
111
+ lines.append(f"# Session Briefing for {self.project_name}")
112
+ lines.append(f"Path: {self.project_path}")
113
+ lines.append(f"Type: {self.project_type}")
114
+ lines.append(f"Time: {self.briefing_timestamp.strftime('%Y-%m-%d %H:%M')}")
115
+ lines.append("")
116
+
117
+ # Recent Activity
118
+ if self.recent_activities:
119
+ lines.append("## Recent Activity")
120
+ count = 5 if verbosity == "minimal" else (10 if verbosity == "standard" else len(self.recent_activities))
121
+ for activity in self.recent_activities[:count]:
122
+ age = (datetime.now() - activity.timestamp).days
123
+ age_str = f"{age}d ago" if age > 0 else "today"
124
+ lines.append(f"- [{activity.memory_type}] {activity.title} ({age_str})")
125
+ if verbosity == "detailed" and activity.summary:
126
+ lines.append(f" {activity.summary}")
127
+ lines.append("")
128
+
129
+ # Active Issues
130
+ if self.unresolved_problems:
131
+ lines.append("## Active Issues ⚠️")
132
+ for problem in self.unresolved_problems:
133
+ age = (datetime.now() - problem.created_at).days
134
+ lines.append(f"- {problem.title} ({age}d old)")
135
+ if verbosity in ["standard", "detailed"]:
136
+ lines.append(f" {problem.description[:200]}...")
137
+ lines.append("")
138
+
139
+ # Recommended Patterns
140
+ if self.relevant_patterns:
141
+ lines.append("## Recommended Patterns")
142
+ count = 3 if verbosity == "minimal" else 5
143
+ for pattern in self.relevant_patterns[:count]:
144
+ eff_pct = int(pattern.effectiveness * 100)
145
+ lines.append(f"- {pattern.pattern_type}: {pattern.description}")
146
+ if verbosity in ["standard", "detailed"]:
147
+ lines.append(f" Effectiveness: {eff_pct}%, Used {pattern.usage_count} times")
148
+ lines.append("")
149
+
150
+ # Warnings
151
+ if self.deprecation_warnings:
152
+ lines.append("## Deprecation Warnings ⚠️")
153
+ for warning in self.deprecation_warnings:
154
+ lines.append(f"- {warning.deprecated_title} is deprecated")
155
+ if warning.replacement_title:
156
+ lines.append(f" Use instead: {warning.replacement_title}")
157
+ if verbosity == "detailed":
158
+ lines.append(f" Reason: {warning.reason}")
159
+ lines.append("")
160
+
161
+ # Summary
162
+ lines.append("## Summary")
163
+ lines.append(f"- Total memories: {self.total_memories}")
164
+ lines.append(f"- Active issues: {len(self.unresolved_problems)}")
165
+ lines.append(f"- Patterns available: {len(self.relevant_patterns)}")
166
+ if self.deprecation_warnings:
167
+ lines.append(f"- ⚠️ {len(self.deprecation_warnings)} deprecation warnings")
168
+
169
+ return "\n".join(lines)
170
+
171
+
172
+ async def generate_session_briefing(
173
+ backend: GraphBackend,
174
+ project_dir: str,
175
+ recency_days: int = 7,
176
+ max_activities: int = 10,
177
+ ) -> Optional[SessionBriefing]:
178
+ """
179
+ Generate a session briefing for a project.
180
+
181
+ Args:
182
+ backend: Database backend
183
+ project_dir: Project directory path
184
+ recency_days: How many days back to look for recent activity
185
+ max_activities: Maximum number of recent activities to include
186
+
187
+ Returns:
188
+ SessionBriefing if project detected, None otherwise
189
+
190
+ Example:
191
+ >>> briefing = await generate_session_briefing(backend, "/Users/me/my-app")
192
+ >>> print(briefing.format_as_text("standard"))
193
+ """
194
+ # Detect project
195
+ project = await detect_project(backend, project_dir)
196
+ if not project:
197
+ logger.warning(f"Could not detect project at {project_dir}")
198
+ return None
199
+
200
+ logger.info(f"Generating session briefing for project: {project.name}")
201
+
202
+ # Initialize briefing
203
+ briefing = SessionBriefing(
204
+ project_name=project.name,
205
+ project_path=project.path,
206
+ project_type=project.project_type,
207
+ )
208
+
209
+ # Get total memory count for project
210
+ total_count_query = """
211
+ MATCH (m:Memory)
212
+ WHERE m.context IS NOT NULL
213
+ AND (m.context CONTAINS $project_path OR m.context CONTAINS $project_name)
214
+ RETURN count(m) as total
215
+ """
216
+
217
+ try:
218
+ result = await backend.execute_query(
219
+ total_count_query,
220
+ {
221
+ "project_path": project.path,
222
+ "project_name": project.name,
223
+ }
224
+ )
225
+ briefing.total_memories = result[0]["total"] if result else 0
226
+ except Exception as e:
227
+ logger.error(f"Error counting memories: {e}")
228
+
229
+ # Get recent activities
230
+ cutoff_date = datetime.now() - timedelta(days=recency_days)
231
+
232
+ recent_query = """
233
+ MATCH (m:Memory)
234
+ WHERE m.context IS NOT NULL
235
+ AND (m.context CONTAINS $project_path OR m.context CONTAINS $project_name)
236
+ AND datetime(m.created_at) >= datetime($cutoff)
237
+ RETURN m.id as id, m.type as type, m.title as title,
238
+ m.summary as summary, m.created_at as created_at,
239
+ m.tags as tags
240
+ ORDER BY m.created_at DESC
241
+ LIMIT $limit
242
+ """
243
+
244
+ try:
245
+ results = await backend.execute_query(
246
+ recent_query,
247
+ {
248
+ "project_path": project.path,
249
+ "project_name": project.name,
250
+ "cutoff": cutoff_date.isoformat(),
251
+ "limit": max_activities,
252
+ }
253
+ )
254
+
255
+ for record in results:
256
+ briefing.recent_activities.append(RecentActivity(
257
+ memory_id=record["id"],
258
+ memory_type=record["type"],
259
+ title=record["title"],
260
+ summary=record.get("summary"),
261
+ timestamp=datetime.fromisoformat(record["created_at"]),
262
+ tags=record.get("tags", []),
263
+ ))
264
+ except Exception as e:
265
+ logger.error(f"Error fetching recent activities: {e}")
266
+
267
+ # Get unresolved problems (problems without solutions)
268
+ problems_query = """
269
+ MATCH (p:Memory {type: 'problem'})
270
+ WHERE p.context IS NOT NULL
271
+ AND (p.context CONTAINS $project_path OR p.context CONTAINS $project_name)
272
+ AND NOT EXISTS {
273
+ MATCH (p)-[:SOLVES|ADDRESSES]->(:Memory)
274
+ }
275
+ OPTIONAL MATCH (p)-[r]-()
276
+ RETURN p.id as id, p.title as title, p.content as content,
277
+ p.created_at as created_at, p.tags as tags,
278
+ count(r) as related_count
279
+ ORDER BY p.created_at DESC
280
+ LIMIT 5
281
+ """
282
+
283
+ try:
284
+ results = await backend.execute_query(
285
+ problems_query,
286
+ {
287
+ "project_path": project.path,
288
+ "project_name": project.name,
289
+ }
290
+ )
291
+
292
+ for record in results:
293
+ briefing.unresolved_problems.append(UnresolvedProblem(
294
+ problem_id=record["id"],
295
+ title=record["title"],
296
+ description=record["content"][:200],
297
+ created_at=datetime.fromisoformat(record["created_at"]),
298
+ tags=record.get("tags", []),
299
+ related_memories=record.get("related_count", 0),
300
+ ))
301
+
302
+ briefing.has_active_issues = len(briefing.unresolved_problems) > 0
303
+ except Exception as e:
304
+ logger.error(f"Error fetching unresolved problems: {e}")
305
+
306
+ # Get relevant patterns
307
+ patterns_query = """
308
+ MATCH (m:Memory {type: 'code_pattern'})
309
+ WHERE m.context IS NOT NULL
310
+ AND (m.context CONTAINS $project_path OR m.context CONTAINS $project_name)
311
+ RETURN m.id as id, m.title as type, m.content as description,
312
+ m.effectiveness as effectiveness, m.usage_count as usage_count,
313
+ m.last_accessed as last_used
314
+ ORDER BY m.effectiveness DESC, m.usage_count DESC
315
+ LIMIT 5
316
+ """
317
+
318
+ try:
319
+ results = await backend.execute_query(
320
+ patterns_query,
321
+ {
322
+ "project_path": project.path,
323
+ "project_name": project.name,
324
+ }
325
+ )
326
+
327
+ for record in results:
328
+ briefing.relevant_patterns.append(RelevantPattern(
329
+ pattern_id=record["id"],
330
+ pattern_type=record["type"],
331
+ description=record["description"][:200],
332
+ effectiveness=record.get("effectiveness", 0.5),
333
+ usage_count=record.get("usage_count", 0),
334
+ last_used=datetime.fromisoformat(record["last_used"]) if record.get("last_used") else None,
335
+ ))
336
+ except Exception as e:
337
+ logger.error(f"Error fetching patterns: {e}")
338
+
339
+ # Get deprecation warnings (memories with DEPRECATED_BY relationships)
340
+ deprecated_query = """
341
+ MATCH (old:Memory)-[r:DEPRECATED_BY]->(new:Memory)
342
+ WHERE old.context IS NOT NULL
343
+ AND (old.context CONTAINS $project_path OR old.context CONTAINS $project_name)
344
+ RETURN old.id as old_id, old.title as old_title,
345
+ new.id as new_id, new.title as new_title,
346
+ r.context as reason
347
+ LIMIT 5
348
+ """
349
+
350
+ try:
351
+ results = await backend.execute_query(
352
+ deprecated_query,
353
+ {
354
+ "project_path": project.path,
355
+ "project_name": project.name,
356
+ }
357
+ )
358
+
359
+ for record in results:
360
+ briefing.deprecation_warnings.append(DeprecationWarning(
361
+ deprecated_id=record["old_id"],
362
+ deprecated_title=record["old_title"],
363
+ reason=record.get("reason", "No longer recommended"),
364
+ replacement_id=record.get("new_id"),
365
+ replacement_title=record.get("new_title"),
366
+ ))
367
+
368
+ briefing.has_warnings = len(briefing.deprecation_warnings) > 0
369
+ except Exception as e:
370
+ logger.error(f"Error fetching deprecation warnings: {e}")
371
+
372
+ logger.info(f"Session briefing generated: {len(briefing.recent_activities)} activities, "
373
+ f"{len(briefing.unresolved_problems)} problems, "
374
+ f"{len(briefing.relevant_patterns)} patterns")
375
+
376
+ return briefing
377
+
378
+
379
+ def get_session_briefing_resource(briefing: SessionBriefing, verbosity: str = "standard") -> Dict[str, Any]:
380
+ """
381
+ Format session briefing as MCP resource.
382
+
383
+ Args:
384
+ briefing: Session briefing to format
385
+ verbosity: Verbosity level ("minimal", "standard", "detailed")
386
+
387
+ Returns:
388
+ MCP resource dictionary
389
+
390
+ Example:
391
+ >>> resource = get_session_briefing_resource(briefing, "standard")
392
+ """
393
+ return {
394
+ "uri": f"memory://session/briefing/{briefing.project_name}",
395
+ "name": f"Session Briefing: {briefing.project_name}",
396
+ "description": f"Automatic session briefing for {briefing.project_name}",
397
+ "mimeType": "text/markdown",
398
+ "text": briefing.format_as_text(verbosity),
399
+ }