claude-mpm 4.1.2__py3-none-any.whl → 4.1.4__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +16 -19
- claude_mpm/agents/MEMORY.md +21 -49
- claude_mpm/agents/templates/OPTIMIZATION_REPORT.md +156 -0
- claude_mpm/agents/templates/api_qa.json +36 -116
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +42 -9
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +29 -6
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +34 -6
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +41 -9
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +30 -8
- claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +2 -2
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +29 -6
- claude_mpm/agents/templates/backup/research_memory_efficient.json +2 -2
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +41 -9
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +23 -7
- claude_mpm/agents/templates/code_analyzer.json +18 -36
- claude_mpm/agents/templates/data_engineer.json +43 -14
- claude_mpm/agents/templates/documentation.json +55 -74
- claude_mpm/agents/templates/engineer.json +57 -40
- claude_mpm/agents/templates/imagemagick.json +7 -2
- claude_mpm/agents/templates/memory_manager.json +1 -1
- claude_mpm/agents/templates/ops.json +36 -4
- claude_mpm/agents/templates/project_organizer.json +23 -71
- claude_mpm/agents/templates/qa.json +34 -2
- claude_mpm/agents/templates/refactoring_engineer.json +9 -5
- claude_mpm/agents/templates/research.json +36 -4
- claude_mpm/agents/templates/security.json +29 -2
- claude_mpm/agents/templates/ticketing.json +3 -3
- claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
- claude_mpm/agents/templates/version_control.json +28 -2
- claude_mpm/agents/templates/web_qa.json +38 -151
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/commands/agent_manager.py +221 -1
- claude_mpm/cli/commands/agents.py +556 -1009
- claude_mpm/cli/commands/memory.py +248 -927
- claude_mpm/cli/commands/run.py +139 -484
- claude_mpm/cli/parsers/agent_manager_parser.py +34 -0
- claude_mpm/cli/startup_logging.py +76 -0
- claude_mpm/core/agent_registry.py +6 -10
- claude_mpm/core/framework_loader.py +205 -595
- claude_mpm/core/log_manager.py +49 -1
- claude_mpm/core/logging_config.py +2 -4
- claude_mpm/hooks/claude_hooks/event_handlers.py +7 -117
- claude_mpm/hooks/claude_hooks/hook_handler.py +91 -755
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +42 -454
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/memory/agent_memory_manager.py +42 -508
- claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/registry/__init__.py +1 -1
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- claude_mpm/services/core/cache_manager.py +311 -0
- claude_mpm/services/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +65 -19
- claude_mpm/services/memory/router.py +116 -10
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/RECORD +86 -55
- claude_mpm/cli/commands/run_config_checker.py +0 -159
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Memory CRUD Service
|
|
3
|
+
===================
|
|
4
|
+
|
|
5
|
+
WHY: This service encapsulates all CRUD operations for agent memories, extracting
|
|
6
|
+
logic from the CLI commands to follow Single Responsibility Principle. It provides
|
|
7
|
+
a clean interface for memory initialization, reading, updating, and deletion operations.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISIONS:
|
|
10
|
+
- Separates memory CRUD logic from CLI command implementation
|
|
11
|
+
- Provides structured data returns for programmatic access
|
|
12
|
+
- Handles memory validation and error recovery
|
|
13
|
+
- Supports both text and structured output formats
|
|
14
|
+
- Integrates with AgentMemoryManager for actual operations
|
|
15
|
+
- Maintains backward compatibility with existing memory formats
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
from abc import ABC, abstractmethod
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any, Dict, List, Optional
|
|
23
|
+
|
|
24
|
+
from ...core.logger import get_logger
|
|
25
|
+
from ...services.agents.memory.agent_memory_manager import AgentMemoryManager
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class IMemoryCRUDService(ABC):
|
|
29
|
+
"""Interface for memory CRUD operations."""
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def create_memory(
|
|
33
|
+
self, agent_id: str, template_type: str = "default"
|
|
34
|
+
) -> Dict[str, Any]:
|
|
35
|
+
"""
|
|
36
|
+
Create new memory file for an agent.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
agent_id: ID of the agent
|
|
40
|
+
template_type: Type of template to use
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Dictionary with success status and file path
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def read_memory(self, agent_id: Optional[str] = None) -> Dict[str, Any]:
|
|
48
|
+
"""
|
|
49
|
+
Read memory content for one or all agents.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
agent_id: Optional agent ID, None for all agents
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dictionary with memory content and metadata
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def update_memory(
|
|
60
|
+
self, agent_id: str, section: str, content: str
|
|
61
|
+
) -> Dict[str, Any]:
|
|
62
|
+
"""
|
|
63
|
+
Add learning entry to agent memory.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
agent_id: ID of the agent
|
|
67
|
+
section: Memory section to update
|
|
68
|
+
content: Learning content to add
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dictionary with success status and update details
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def delete_memory(self, agent_id: str, confirm: bool = False) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
Delete memory file for an agent.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
agent_id: ID of the agent
|
|
81
|
+
confirm: Confirmation flag for deletion
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Dictionary with deletion status
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def list_memories(self, include_stats: bool = True) -> Dict[str, Any]:
|
|
89
|
+
"""
|
|
90
|
+
List all memory files with optional statistics.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
include_stats: Include file statistics
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Dictionary with memory files list and stats
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
@abstractmethod
|
|
100
|
+
def clean_memory(
|
|
101
|
+
self, agent_id: Optional[str] = None, dry_run: bool = True
|
|
102
|
+
) -> Dict[str, Any]:
|
|
103
|
+
"""
|
|
104
|
+
Clean up memory files (remove old/unused).
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
agent_id: Optional specific agent to clean
|
|
108
|
+
dry_run: Preview changes without executing
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Dictionary with cleanup results
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
@abstractmethod
|
|
115
|
+
def init_project_memories(self) -> Dict[str, Any]:
|
|
116
|
+
"""
|
|
117
|
+
Initialize project-specific memories task.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Dictionary with initialization task details
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class MemoryCRUDService(IMemoryCRUDService):
|
|
125
|
+
"""Service for managing memory CRUD operations with robust error handling."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, memory_manager: Optional[AgentMemoryManager] = None):
|
|
128
|
+
"""
|
|
129
|
+
Initialize the memory CRUD service.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
memory_manager: Optional memory manager instance
|
|
133
|
+
"""
|
|
134
|
+
self.logger = get_logger(__name__)
|
|
135
|
+
self._memory_manager = memory_manager
|
|
136
|
+
|
|
137
|
+
def _get_memory_manager(self) -> AgentMemoryManager:
|
|
138
|
+
"""Get or create memory manager instance."""
|
|
139
|
+
if self._memory_manager is None:
|
|
140
|
+
from ...core.shared.config_loader import ConfigLoader
|
|
141
|
+
|
|
142
|
+
config_loader = ConfigLoader()
|
|
143
|
+
config = config_loader.load_main_config()
|
|
144
|
+
# Use CLAUDE_MPM_USER_PWD if available
|
|
145
|
+
user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD", os.getcwd())
|
|
146
|
+
current_dir = Path(user_pwd)
|
|
147
|
+
self._memory_manager = AgentMemoryManager(config, current_dir)
|
|
148
|
+
return self._memory_manager
|
|
149
|
+
|
|
150
|
+
def create_memory(
|
|
151
|
+
self, agent_id: str, template_type: str = "default"
|
|
152
|
+
) -> Dict[str, Any]:
|
|
153
|
+
"""
|
|
154
|
+
Create new memory file for an agent.
|
|
155
|
+
|
|
156
|
+
WHY: Initializes a new memory file with appropriate template structure
|
|
157
|
+
for the specified agent, ensuring consistent format across all memories.
|
|
158
|
+
"""
|
|
159
|
+
try:
|
|
160
|
+
memory_manager = self._get_memory_manager()
|
|
161
|
+
|
|
162
|
+
# Check if memory already exists
|
|
163
|
+
existing_content = memory_manager.load_agent_memory(agent_id)
|
|
164
|
+
if existing_content:
|
|
165
|
+
return {
|
|
166
|
+
"success": False,
|
|
167
|
+
"error": f"Memory already exists for agent: {agent_id}",
|
|
168
|
+
"agent_id": agent_id,
|
|
169
|
+
"existing_file": str(
|
|
170
|
+
memory_manager.memories_dir / f"{agent_id}_memories.md"
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Create memory with default template
|
|
175
|
+
template_content = memory_manager.template_generator.generate_template(
|
|
176
|
+
agent_id, template_type
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
memory_file = memory_manager.memories_dir / f"{agent_id}_memories.md"
|
|
180
|
+
memory_file.parent.mkdir(parents=True, exist_ok=True)
|
|
181
|
+
memory_file.write_text(template_content)
|
|
182
|
+
|
|
183
|
+
self.logger.info(f"Created memory file for agent: {agent_id}")
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
"success": True,
|
|
187
|
+
"agent_id": agent_id,
|
|
188
|
+
"file_path": str(memory_file),
|
|
189
|
+
"template_type": template_type,
|
|
190
|
+
"message": f"Memory created for agent: {agent_id}",
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
self.logger.error(
|
|
195
|
+
f"Error creating memory for {agent_id}: {e}", exc_info=True
|
|
196
|
+
)
|
|
197
|
+
return {"success": False, "error": str(e), "agent_id": agent_id}
|
|
198
|
+
|
|
199
|
+
def read_memory(self, agent_id: Optional[str] = None) -> Dict[str, Any]:
|
|
200
|
+
"""
|
|
201
|
+
Read memory content for one or all agents.
|
|
202
|
+
|
|
203
|
+
WHY: Provides access to memory content for inspection, debugging,
|
|
204
|
+
or integration with other tools.
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
memory_manager = self._get_memory_manager()
|
|
208
|
+
|
|
209
|
+
if agent_id:
|
|
210
|
+
# Read single agent memory
|
|
211
|
+
memory_content = memory_manager.load_agent_memory(agent_id)
|
|
212
|
+
|
|
213
|
+
if not memory_content:
|
|
214
|
+
return {
|
|
215
|
+
"success": False,
|
|
216
|
+
"error": f"No memory found for agent: {agent_id}",
|
|
217
|
+
"agent_id": agent_id,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
memory_file = memory_manager.memories_dir / f"{agent_id}_memories.md"
|
|
221
|
+
file_stats = None
|
|
222
|
+
if memory_file.exists():
|
|
223
|
+
stat = memory_file.stat()
|
|
224
|
+
file_stats = {
|
|
225
|
+
"size_kb": stat.st_size / 1024,
|
|
226
|
+
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
|
|
227
|
+
"path": str(memory_file),
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
"success": True,
|
|
232
|
+
"agent_id": agent_id,
|
|
233
|
+
"content": memory_content,
|
|
234
|
+
"file_stats": file_stats,
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
# Read all agent memories
|
|
238
|
+
memory_dir = memory_manager.memories_dir
|
|
239
|
+
if not memory_dir.exists():
|
|
240
|
+
return {
|
|
241
|
+
"success": True,
|
|
242
|
+
"agents": {},
|
|
243
|
+
"memory_directory": str(memory_dir),
|
|
244
|
+
"exists": False,
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
agents = {}
|
|
248
|
+
for memory_file in self._get_memory_files(memory_dir):
|
|
249
|
+
agent_id = self._extract_agent_id(memory_file)
|
|
250
|
+
try:
|
|
251
|
+
memory_content = memory_manager.load_agent_memory(agent_id)
|
|
252
|
+
if memory_content:
|
|
253
|
+
stat = memory_file.stat()
|
|
254
|
+
agents[agent_id] = {
|
|
255
|
+
"content": memory_content,
|
|
256
|
+
"file_stats": {
|
|
257
|
+
"size_kb": stat.st_size / 1024,
|
|
258
|
+
"modified": datetime.fromtimestamp(
|
|
259
|
+
stat.st_mtime
|
|
260
|
+
).isoformat(),
|
|
261
|
+
"path": str(memory_file),
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
except Exception as e:
|
|
265
|
+
self.logger.warning(f"Error reading memory for {agent_id}: {e}")
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
"success": True,
|
|
269
|
+
"agents": agents,
|
|
270
|
+
"memory_directory": str(memory_dir),
|
|
271
|
+
"exists": True,
|
|
272
|
+
"agent_count": len(agents),
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
self.logger.error(f"Error reading memory: {e}", exc_info=True)
|
|
277
|
+
return {"success": False, "error": str(e), "agent_id": agent_id}
|
|
278
|
+
|
|
279
|
+
def update_memory(
|
|
280
|
+
self, agent_id: str, section: str, content: str
|
|
281
|
+
) -> Dict[str, Any]:
|
|
282
|
+
"""
|
|
283
|
+
Add learning entry to agent memory.
|
|
284
|
+
|
|
285
|
+
WHY: Allows manual injection of learnings for testing, correction,
|
|
286
|
+
or knowledge enhancement purposes.
|
|
287
|
+
"""
|
|
288
|
+
try:
|
|
289
|
+
memory_manager = self._get_memory_manager()
|
|
290
|
+
|
|
291
|
+
# Map learning types to appropriate sections
|
|
292
|
+
section_map = {
|
|
293
|
+
"pattern": "Project Architecture",
|
|
294
|
+
"error": "Common Mistakes to Avoid",
|
|
295
|
+
"optimization": "Implementation Guidelines",
|
|
296
|
+
"preference": "Implementation Guidelines",
|
|
297
|
+
"context": "Current Technical Context",
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
section_name = section_map.get(section, section)
|
|
301
|
+
|
|
302
|
+
# Attempt to update memory
|
|
303
|
+
success = memory_manager.update_agent_memory(
|
|
304
|
+
agent_id, section_name, content
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
if success:
|
|
308
|
+
self.logger.info(
|
|
309
|
+
f"Added learning to {agent_id} memory in section: {section_name}"
|
|
310
|
+
)
|
|
311
|
+
return {
|
|
312
|
+
"success": True,
|
|
313
|
+
"agent_id": agent_id,
|
|
314
|
+
"section": section_name,
|
|
315
|
+
"content_preview": content[:100]
|
|
316
|
+
+ ("..." if len(content) > 100 else ""),
|
|
317
|
+
"message": f"Learning added to {agent_id} memory",
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
"success": False,
|
|
321
|
+
"agent_id": agent_id,
|
|
322
|
+
"section": section_name,
|
|
323
|
+
"error": "Failed to add learning - memory file may be at size limit or section may be full",
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
except Exception as e:
|
|
327
|
+
self.logger.error(
|
|
328
|
+
f"Error updating memory for {agent_id}: {e}", exc_info=True
|
|
329
|
+
)
|
|
330
|
+
return {
|
|
331
|
+
"success": False,
|
|
332
|
+
"error": str(e),
|
|
333
|
+
"agent_id": agent_id,
|
|
334
|
+
"section": section,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
def delete_memory(self, agent_id: str, confirm: bool = False) -> Dict[str, Any]:
|
|
338
|
+
"""
|
|
339
|
+
Delete memory file for an agent.
|
|
340
|
+
|
|
341
|
+
WHY: Provides controlled deletion of memory files with safety checks
|
|
342
|
+
to prevent accidental data loss.
|
|
343
|
+
"""
|
|
344
|
+
try:
|
|
345
|
+
if not confirm:
|
|
346
|
+
return {
|
|
347
|
+
"success": False,
|
|
348
|
+
"error": "Deletion requires confirmation flag",
|
|
349
|
+
"agent_id": agent_id,
|
|
350
|
+
"hint": "Use --confirm flag to delete",
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
memory_manager = self._get_memory_manager()
|
|
354
|
+
memory_file = memory_manager.memories_dir / f"{agent_id}_memories.md"
|
|
355
|
+
|
|
356
|
+
if not memory_file.exists():
|
|
357
|
+
# Check for alternative formats
|
|
358
|
+
alt_files = [
|
|
359
|
+
memory_manager.memories_dir / f"{agent_id}_agent.md",
|
|
360
|
+
memory_manager.memories_dir / f"{agent_id}.md",
|
|
361
|
+
]
|
|
362
|
+
|
|
363
|
+
for alt_file in alt_files:
|
|
364
|
+
if alt_file.exists():
|
|
365
|
+
memory_file = alt_file
|
|
366
|
+
break
|
|
367
|
+
else:
|
|
368
|
+
return {
|
|
369
|
+
"success": False,
|
|
370
|
+
"error": f"No memory file found for agent: {agent_id}",
|
|
371
|
+
"agent_id": agent_id,
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
# Get file size before deletion
|
|
375
|
+
file_size_kb = memory_file.stat().st_size / 1024
|
|
376
|
+
|
|
377
|
+
# Delete the file
|
|
378
|
+
memory_file.unlink()
|
|
379
|
+
self.logger.info(f"Deleted memory file for agent: {agent_id}")
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
"success": True,
|
|
383
|
+
"agent_id": agent_id,
|
|
384
|
+
"deleted_file": str(memory_file),
|
|
385
|
+
"file_size_kb": file_size_kb,
|
|
386
|
+
"message": f"Memory deleted for agent: {agent_id}",
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
except Exception as e:
|
|
390
|
+
self.logger.error(
|
|
391
|
+
f"Error deleting memory for {agent_id}: {e}", exc_info=True
|
|
392
|
+
)
|
|
393
|
+
return {"success": False, "error": str(e), "agent_id": agent_id}
|
|
394
|
+
|
|
395
|
+
def list_memories(self, include_stats: bool = True) -> Dict[str, Any]:
|
|
396
|
+
"""
|
|
397
|
+
List all memory files with optional statistics.
|
|
398
|
+
|
|
399
|
+
WHY: Provides overview of all memories in the system for management
|
|
400
|
+
and monitoring purposes.
|
|
401
|
+
"""
|
|
402
|
+
try:
|
|
403
|
+
memory_manager = self._get_memory_manager()
|
|
404
|
+
memory_dir = memory_manager.memories_dir
|
|
405
|
+
|
|
406
|
+
if not memory_dir.exists():
|
|
407
|
+
return {
|
|
408
|
+
"success": True,
|
|
409
|
+
"memory_directory": str(memory_dir),
|
|
410
|
+
"exists": False,
|
|
411
|
+
"memories": [],
|
|
412
|
+
"total_size_kb": 0,
|
|
413
|
+
"total_files": 0,
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
memories = []
|
|
417
|
+
total_size = 0
|
|
418
|
+
|
|
419
|
+
for memory_file in self._get_memory_files(memory_dir):
|
|
420
|
+
agent_id = self._extract_agent_id(memory_file)
|
|
421
|
+
|
|
422
|
+
memory_info = {
|
|
423
|
+
"agent_id": agent_id,
|
|
424
|
+
"file": memory_file.name,
|
|
425
|
+
"path": str(memory_file),
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if include_stats:
|
|
429
|
+
stat = memory_file.stat()
|
|
430
|
+
memory_info.update(
|
|
431
|
+
{
|
|
432
|
+
"size_kb": stat.st_size / 1024,
|
|
433
|
+
"modified": datetime.fromtimestamp(
|
|
434
|
+
stat.st_mtime
|
|
435
|
+
).isoformat(),
|
|
436
|
+
"created": datetime.fromtimestamp(
|
|
437
|
+
stat.st_ctime
|
|
438
|
+
).isoformat(),
|
|
439
|
+
}
|
|
440
|
+
)
|
|
441
|
+
total_size += stat.st_size
|
|
442
|
+
|
|
443
|
+
memories.append(memory_info)
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
"success": True,
|
|
447
|
+
"memory_directory": str(memory_dir),
|
|
448
|
+
"exists": True,
|
|
449
|
+
"memories": sorted(memories, key=lambda x: x["agent_id"]),
|
|
450
|
+
"total_size_kb": total_size / 1024 if include_stats else None,
|
|
451
|
+
"total_files": len(memories),
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
except Exception as e:
|
|
455
|
+
self.logger.error(f"Error listing memories: {e}", exc_info=True)
|
|
456
|
+
return {"success": False, "error": str(e)}
|
|
457
|
+
|
|
458
|
+
def clean_memory(
|
|
459
|
+
self, agent_id: Optional[str] = None, dry_run: bool = True
|
|
460
|
+
) -> Dict[str, Any]:
|
|
461
|
+
"""
|
|
462
|
+
Clean up memory files (remove old/unused).
|
|
463
|
+
|
|
464
|
+
WHY: Memory files can accumulate over time. This provides controlled
|
|
465
|
+
cleanup to save disk space while preserving important data.
|
|
466
|
+
|
|
467
|
+
DESIGN DECISION: For Phase 1, this is a stub implementation.
|
|
468
|
+
Full cleanup logic will be implemented based on usage patterns.
|
|
469
|
+
"""
|
|
470
|
+
try:
|
|
471
|
+
memory_manager = self._get_memory_manager()
|
|
472
|
+
memory_dir = memory_manager.memories_dir
|
|
473
|
+
|
|
474
|
+
if not memory_dir.exists():
|
|
475
|
+
return {
|
|
476
|
+
"success": True,
|
|
477
|
+
"message": "No memory directory found - nothing to clean",
|
|
478
|
+
"cleaned_files": [],
|
|
479
|
+
"dry_run": dry_run,
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
memory_files = list(self._get_memory_files(memory_dir))
|
|
483
|
+
if not memory_files:
|
|
484
|
+
return {
|
|
485
|
+
"success": True,
|
|
486
|
+
"message": "No memory files found - nothing to clean",
|
|
487
|
+
"cleaned_files": [],
|
|
488
|
+
"dry_run": dry_run,
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
# For Phase 1, just identify candidates for cleanup
|
|
492
|
+
cleanup_candidates = []
|
|
493
|
+
|
|
494
|
+
for memory_file in memory_files:
|
|
495
|
+
agent_id_file = self._extract_agent_id(memory_file)
|
|
496
|
+
|
|
497
|
+
# Skip if specific agent requested and doesn't match
|
|
498
|
+
if agent_id and agent_id_file != agent_id:
|
|
499
|
+
continue
|
|
500
|
+
|
|
501
|
+
stat = memory_file.stat()
|
|
502
|
+
age_days = (datetime.now() - datetime.fromtimestamp(stat.st_mtime)).days
|
|
503
|
+
|
|
504
|
+
# Identify files older than 30 days as candidates
|
|
505
|
+
if age_days > 30:
|
|
506
|
+
cleanup_candidates.append(
|
|
507
|
+
{
|
|
508
|
+
"agent_id": agent_id_file,
|
|
509
|
+
"file": str(memory_file),
|
|
510
|
+
"size_kb": stat.st_size / 1024,
|
|
511
|
+
"age_days": age_days,
|
|
512
|
+
"reason": "File older than 30 days with no recent access",
|
|
513
|
+
}
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
if dry_run:
|
|
517
|
+
return {
|
|
518
|
+
"success": True,
|
|
519
|
+
"message": "Cleanup preview (dry run)",
|
|
520
|
+
"cleanup_candidates": cleanup_candidates,
|
|
521
|
+
"dry_run": True,
|
|
522
|
+
"note": "Cleanup not yet implemented in Phase 1",
|
|
523
|
+
}
|
|
524
|
+
# Phase 1: Don't actually delete anything
|
|
525
|
+
return {
|
|
526
|
+
"success": True,
|
|
527
|
+
"message": "Cleanup not yet implemented in Phase 1",
|
|
528
|
+
"cleanup_candidates": cleanup_candidates,
|
|
529
|
+
"dry_run": False,
|
|
530
|
+
"cleaned_files": [],
|
|
531
|
+
"note": "Future cleanup will remove old/corrupted files",
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
except Exception as e:
|
|
535
|
+
self.logger.error(f"Error cleaning memory: {e}", exc_info=True)
|
|
536
|
+
return {"success": False, "error": str(e), "agent_id": agent_id}
|
|
537
|
+
|
|
538
|
+
def init_project_memories(self) -> Dict[str, Any]:
|
|
539
|
+
"""
|
|
540
|
+
Initialize project-specific memories task.
|
|
541
|
+
|
|
542
|
+
WHY: When starting with a new project, agents need project-specific
|
|
543
|
+
knowledge beyond what automatic analysis provides. This creates a
|
|
544
|
+
task description for comprehensive project scanning.
|
|
545
|
+
"""
|
|
546
|
+
try:
|
|
547
|
+
task_data = {
|
|
548
|
+
"task": "Initialize Project-Specific Memories",
|
|
549
|
+
"description": "Analyze project structure and create targeted memories for agents",
|
|
550
|
+
"instructions": [
|
|
551
|
+
"Scan the project structure, documentation, and source code",
|
|
552
|
+
"Identify key patterns, conventions, and project-specific knowledge",
|
|
553
|
+
"Create targeted memories for each agent type",
|
|
554
|
+
"Use 'claude-mpm memory add <agent> <type> \"<content>\"' commands",
|
|
555
|
+
],
|
|
556
|
+
"focus_areas": [
|
|
557
|
+
"Architectural patterns and design decisions",
|
|
558
|
+
"Coding conventions from actual source code",
|
|
559
|
+
"Key modules, APIs, and integration points",
|
|
560
|
+
"Testing patterns and quality standards",
|
|
561
|
+
"Performance considerations specific to this project",
|
|
562
|
+
"Common pitfalls based on the codebase",
|
|
563
|
+
"Domain-specific terminology and concepts",
|
|
564
|
+
],
|
|
565
|
+
"example_commands": [
|
|
566
|
+
'claude-mpm memory add engineer pattern "Use dependency injection with @inject"',
|
|
567
|
+
'claude-mpm memory add qa pattern "Test files follow test_<module>_<feature>.py"',
|
|
568
|
+
'claude-mpm memory add research context "Project uses microservices architecture"',
|
|
569
|
+
],
|
|
570
|
+
"analysis_targets": [
|
|
571
|
+
"Project structure and documentation",
|
|
572
|
+
"Source code for patterns and conventions",
|
|
573
|
+
"Testing patterns and quality standards",
|
|
574
|
+
"Performance considerations",
|
|
575
|
+
"Domain-specific terminology",
|
|
576
|
+
],
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return {
|
|
580
|
+
"success": True,
|
|
581
|
+
"task_data": task_data,
|
|
582
|
+
"message": "Memory initialization task created",
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
except Exception as e:
|
|
586
|
+
self.logger.error(f"Error creating initialization task: {e}", exc_info=True)
|
|
587
|
+
return {"success": False, "error": str(e)}
|
|
588
|
+
|
|
589
|
+
# Helper methods
|
|
590
|
+
def _get_memory_files(self, memory_dir: Path) -> List[Path]:
|
|
591
|
+
"""Get all memory files supporting various formats."""
|
|
592
|
+
memory_files = []
|
|
593
|
+
|
|
594
|
+
# Support new format
|
|
595
|
+
memory_files.extend(memory_dir.glob("*_memories.md"))
|
|
596
|
+
|
|
597
|
+
# Support old formats for backward compatibility
|
|
598
|
+
memory_files.extend(memory_dir.glob("*_agent.md"))
|
|
599
|
+
memory_files.extend(
|
|
600
|
+
[
|
|
601
|
+
f
|
|
602
|
+
for f in memory_dir.glob("*.md")
|
|
603
|
+
if f.name != "README.md"
|
|
604
|
+
and not f.name.endswith("_memories.md")
|
|
605
|
+
and not f.name.endswith("_agent.md")
|
|
606
|
+
]
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
return sorted(set(memory_files))
|
|
610
|
+
|
|
611
|
+
def _extract_agent_id(self, file_path: Path) -> str:
|
|
612
|
+
"""Extract agent ID from various file name formats."""
|
|
613
|
+
if file_path.name.endswith("_memories.md"):
|
|
614
|
+
return file_path.stem[:-9] # Remove "_memories"
|
|
615
|
+
if file_path.name.endswith("_agent.md"):
|
|
616
|
+
return file_path.stem[:-6] # Remove "_agent"
|
|
617
|
+
return file_path.stem
|