claude-mpm 3.9.8__py3-none-any.whl → 3.9.9__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 (44) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/base_agent.json +1 -1
  3. claude_mpm/cli/__init__.py +3 -1
  4. claude_mpm/cli/commands/__init__.py +3 -1
  5. claude_mpm/cli/commands/cleanup.py +21 -1
  6. claude_mpm/cli/commands/mcp.py +821 -0
  7. claude_mpm/cli/parser.py +148 -1
  8. claude_mpm/config/memory_guardian_config.py +325 -0
  9. claude_mpm/constants.py +13 -0
  10. claude_mpm/hooks/claude_hooks/hook_handler.py +76 -19
  11. claude_mpm/models/state_models.py +433 -0
  12. claude_mpm/services/communication/__init__.py +2 -2
  13. claude_mpm/services/communication/socketio.py +18 -16
  14. claude_mpm/services/infrastructure/__init__.py +4 -1
  15. claude_mpm/services/infrastructure/logging.py +3 -3
  16. claude_mpm/services/infrastructure/memory_guardian.py +770 -0
  17. claude_mpm/services/mcp_gateway/__init__.py +28 -12
  18. claude_mpm/services/mcp_gateway/main.py +326 -0
  19. claude_mpm/services/mcp_gateway/registry/__init__.py +6 -3
  20. claude_mpm/services/mcp_gateway/registry/service_registry.py +397 -0
  21. claude_mpm/services/mcp_gateway/registry/tool_registry.py +477 -0
  22. claude_mpm/services/mcp_gateway/server/__init__.py +9 -3
  23. claude_mpm/services/mcp_gateway/server/mcp_server.py +430 -0
  24. claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +444 -0
  25. claude_mpm/services/mcp_gateway/server/stdio_handler.py +373 -0
  26. claude_mpm/services/mcp_gateway/tools/__init__.py +16 -3
  27. claude_mpm/services/mcp_gateway/tools/base_adapter.py +497 -0
  28. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +729 -0
  29. claude_mpm/services/mcp_gateway/tools/hello_world.py +551 -0
  30. claude_mpm/utils/file_utils.py +293 -0
  31. claude_mpm/utils/platform_memory.py +524 -0
  32. claude_mpm/utils/subprocess_utils.py +305 -0
  33. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/METADATA +3 -1
  34. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/RECORD +39 -28
  35. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
  36. claude_mpm/agents/templates/.claude-mpm/memories/engineer_agent.md +0 -39
  37. claude_mpm/agents/templates/.claude-mpm/memories/qa_agent.md +0 -38
  38. claude_mpm/agents/templates/.claude-mpm/memories/research_agent.md +0 -39
  39. claude_mpm/agents/templates/.claude-mpm/memories/version_control_agent.md +0 -38
  40. /claude_mpm/agents/templates/{research_memory_efficient.json → backup/research_memory_efficient.json} +0 -0
  41. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/WHEEL +0 -0
  42. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/entry_points.txt +0 -0
  43. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/licenses/LICENSE +0 -0
  44. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ File utilities for Claude MPM.
4
+
5
+ This module provides safe file operations with atomic writes,
6
+ error handling, and directory management.
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import shutil
12
+ import tempfile
13
+ from pathlib import Path
14
+ from typing import Any, Dict, Optional, Union
15
+
16
+
17
+ class FileOperationError(Exception):
18
+ """Exception raised for file operation errors."""
19
+ pass
20
+
21
+
22
+ def ensure_directory(path: Union[str, Path]) -> Path:
23
+ """
24
+ Ensure a directory exists, creating it if necessary.
25
+
26
+ Args:
27
+ path: Directory path to ensure exists
28
+
29
+ Returns:
30
+ Path object for the directory
31
+
32
+ Raises:
33
+ FileOperationError: If directory cannot be created
34
+ """
35
+ path = Path(path)
36
+ try:
37
+ path.mkdir(parents=True, exist_ok=True)
38
+ return path
39
+ except Exception as e:
40
+ raise FileOperationError(f"Failed to create directory {path}: {e}")
41
+
42
+
43
+ def safe_read_file(path: Union[str, Path], encoding: str = 'utf-8') -> str:
44
+ """
45
+ Safely read a file with error handling.
46
+
47
+ Args:
48
+ path: File path to read
49
+ encoding: Text encoding to use
50
+
51
+ Returns:
52
+ File contents as string
53
+
54
+ Raises:
55
+ FileOperationError: If file cannot be read
56
+ """
57
+ path = Path(path)
58
+ try:
59
+ return path.read_text(encoding=encoding)
60
+ except Exception as e:
61
+ raise FileOperationError(f"Failed to read file {path}: {e}")
62
+
63
+
64
+ def safe_write_file(
65
+ path: Union[str, Path],
66
+ content: str,
67
+ encoding: str = 'utf-8',
68
+ create_dirs: bool = True
69
+ ) -> None:
70
+ """
71
+ Safely write content to a file with directory creation.
72
+
73
+ Args:
74
+ path: File path to write
75
+ content: Content to write
76
+ encoding: Text encoding to use
77
+ create_dirs: Whether to create parent directories
78
+
79
+ Raises:
80
+ FileOperationError: If file cannot be written
81
+ """
82
+ path = Path(path)
83
+ try:
84
+ if create_dirs:
85
+ ensure_directory(path.parent)
86
+ path.write_text(content, encoding=encoding)
87
+ except Exception as e:
88
+ raise FileOperationError(f"Failed to write file {path}: {e}")
89
+
90
+
91
+ def atomic_write(
92
+ path: Union[str, Path],
93
+ content: str,
94
+ encoding: str = 'utf-8',
95
+ create_dirs: bool = True
96
+ ) -> None:
97
+ """
98
+ Atomically write content to a file using a temporary file.
99
+
100
+ This prevents corruption if the write operation is interrupted.
101
+
102
+ Args:
103
+ path: File path to write
104
+ content: Content to write
105
+ encoding: Text encoding to use
106
+ create_dirs: Whether to create parent directories
107
+
108
+ Raises:
109
+ FileOperationError: If file cannot be written atomically
110
+ """
111
+ path = Path(path)
112
+ try:
113
+ if create_dirs:
114
+ ensure_directory(path.parent)
115
+
116
+ # Write to temporary file first
117
+ with tempfile.NamedTemporaryFile(
118
+ mode='w',
119
+ encoding=encoding,
120
+ dir=path.parent,
121
+ delete=False,
122
+ suffix='.tmp'
123
+ ) as temp_file:
124
+ temp_file.write(content)
125
+ temp_path = temp_file.name
126
+
127
+ # Atomically move temporary file to target
128
+ shutil.move(temp_path, path)
129
+
130
+ except Exception as e:
131
+ # Clean up temporary file if it exists
132
+ try:
133
+ if 'temp_path' in locals():
134
+ os.unlink(temp_path)
135
+ except:
136
+ pass
137
+ raise FileOperationError(f"Failed to atomically write file {path}: {e}")
138
+
139
+
140
+ def get_file_info(path: Union[str, Path]) -> Optional[Dict[str, Any]]:
141
+ """
142
+ Get file metadata safely.
143
+
144
+ Args:
145
+ path: File path to examine
146
+
147
+ Returns:
148
+ Dictionary with file information or None if file doesn't exist
149
+ """
150
+ path = Path(path)
151
+ try:
152
+ if not path.exists():
153
+ return None
154
+
155
+ stat = path.stat()
156
+ return {
157
+ "path": str(path),
158
+ "size": stat.st_size,
159
+ "modified": stat.st_mtime,
160
+ "created": stat.st_ctime,
161
+ "is_file": path.is_file(),
162
+ "is_dir": path.is_dir(),
163
+ "permissions": oct(stat.st_mode)[-3:]
164
+ }
165
+ except Exception:
166
+ return None
167
+
168
+
169
+ def safe_copy_file(
170
+ src: Union[str, Path],
171
+ dst: Union[str, Path],
172
+ create_dirs: bool = True
173
+ ) -> None:
174
+ """
175
+ Safely copy a file with error handling.
176
+
177
+ Args:
178
+ src: Source file path
179
+ dst: Destination file path
180
+ create_dirs: Whether to create destination directories
181
+
182
+ Raises:
183
+ FileOperationError: If file cannot be copied
184
+ """
185
+ src = Path(src)
186
+ dst = Path(dst)
187
+
188
+ try:
189
+ if not src.exists():
190
+ raise FileOperationError(f"Source file does not exist: {src}")
191
+
192
+ if create_dirs:
193
+ ensure_directory(dst.parent)
194
+
195
+ shutil.copy2(src, dst)
196
+
197
+ except Exception as e:
198
+ raise FileOperationError(f"Failed to copy {src} to {dst}: {e}")
199
+
200
+
201
+ def safe_remove_file(path: Union[str, Path]) -> bool:
202
+ """
203
+ Safely remove a file.
204
+
205
+ Args:
206
+ path: File path to remove
207
+
208
+ Returns:
209
+ True if file was removed, False if it didn't exist
210
+
211
+ Raises:
212
+ FileOperationError: If file cannot be removed
213
+ """
214
+ path = Path(path)
215
+ try:
216
+ if not path.exists():
217
+ return False
218
+ path.unlink()
219
+ return True
220
+ except Exception as e:
221
+ raise FileOperationError(f"Failed to remove file {path}: {e}")
222
+
223
+
224
+ def read_json_file(path: Union[str, Path]) -> Any:
225
+ """
226
+ Read and parse a JSON file safely.
227
+
228
+ Args:
229
+ path: JSON file path
230
+
231
+ Returns:
232
+ Parsed JSON data
233
+
234
+ Raises:
235
+ FileOperationError: If file cannot be read or parsed
236
+ """
237
+ try:
238
+ content = safe_read_file(path)
239
+ return json.loads(content)
240
+ except json.JSONDecodeError as e:
241
+ raise FileOperationError(f"Invalid JSON in file {path}: {e}")
242
+
243
+
244
+ def write_json_file(
245
+ path: Union[str, Path],
246
+ data: Any,
247
+ indent: int = 2,
248
+ atomic: bool = True
249
+ ) -> None:
250
+ """
251
+ Write data to a JSON file safely.
252
+
253
+ Args:
254
+ path: JSON file path
255
+ data: Data to serialize to JSON
256
+ indent: JSON indentation level
257
+ atomic: Whether to use atomic write
258
+
259
+ Raises:
260
+ FileOperationError: If file cannot be written
261
+ """
262
+ try:
263
+ content = json.dumps(data, indent=indent, ensure_ascii=False)
264
+ if atomic:
265
+ atomic_write(path, content)
266
+ else:
267
+ safe_write_file(path, content)
268
+ except Exception as e:
269
+ raise FileOperationError(f"Failed to write JSON file {path}: {e}")
270
+
271
+
272
+ def backup_file(path: Union[str, Path], backup_suffix: str = '.backup') -> Path:
273
+ """
274
+ Create a backup copy of a file.
275
+
276
+ Args:
277
+ path: File path to backup
278
+ backup_suffix: Suffix to add to backup filename
279
+
280
+ Returns:
281
+ Path to the backup file
282
+
283
+ Raises:
284
+ FileOperationError: If backup cannot be created
285
+ """
286
+ path = Path(path)
287
+ backup_path = path.with_suffix(path.suffix + backup_suffix)
288
+
289
+ try:
290
+ safe_copy_file(path, backup_path)
291
+ return backup_path
292
+ except Exception as e:
293
+ raise FileOperationError(f"Failed to create backup of {path}: {e}")