claude-mpm 4.0.22__py3-none-any.whl → 4.0.25__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/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +4 -1
- claude_mpm/agents/BASE_PM.md +3 -0
- claude_mpm/agents/templates/code_analyzer.json +3 -3
- claude_mpm/agents/templates/data_engineer.json +2 -2
- claude_mpm/agents/templates/documentation.json +36 -9
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/qa.json +2 -2
- claude_mpm/agents/templates/refactoring_engineer.json +65 -43
- claude_mpm/agents/templates/security.json +2 -2
- claude_mpm/agents/templates/version_control.json +2 -2
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/commands/agents.py +453 -113
- claude_mpm/cli/commands/aggregate.py +107 -15
- claude_mpm/cli/commands/cleanup.py +142 -10
- claude_mpm/cli/commands/config.py +358 -224
- claude_mpm/cli/commands/info.py +184 -75
- claude_mpm/cli/commands/mcp_command_router.py +5 -76
- claude_mpm/cli/commands/mcp_install_commands.py +68 -36
- claude_mpm/cli/commands/mcp_server_commands.py +30 -37
- claude_mpm/cli/commands/memory.py +331 -61
- claude_mpm/cli/commands/monitor.py +101 -7
- claude_mpm/cli/commands/run.py +368 -8
- claude_mpm/cli/commands/tickets.py +206 -24
- claude_mpm/cli/parsers/mcp_parser.py +3 -0
- claude_mpm/cli/shared/__init__.py +40 -0
- claude_mpm/cli/shared/argument_patterns.py +212 -0
- claude_mpm/cli/shared/command_base.py +234 -0
- claude_mpm/cli/shared/error_handling.py +238 -0
- claude_mpm/cli/shared/output_formatters.py +231 -0
- claude_mpm/config/agent_config.py +29 -8
- claude_mpm/core/container.py +6 -4
- claude_mpm/core/framework_loader.py +32 -9
- claude_mpm/core/service_registry.py +4 -2
- claude_mpm/core/shared/__init__.py +17 -0
- claude_mpm/core/shared/config_loader.py +320 -0
- claude_mpm/core/shared/path_resolver.py +277 -0
- claude_mpm/core/shared/singleton_manager.py +208 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +4 -2
- claude_mpm/hooks/claude_hooks/response_tracking.py +14 -3
- claude_mpm/hooks/memory_integration_hook.py +11 -2
- claude_mpm/services/agents/deployment/agent_deployment.py +43 -23
- claude_mpm/services/agents/deployment/deployment_wrapper.py +71 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +1 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +43 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +4 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +11 -3
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +14 -5
- claude_mpm/services/event_aggregator.py +4 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +89 -28
- claude_mpm/services/mcp_gateway/config/configuration.py +29 -0
- claude_mpm/services/mcp_gateway/registry/service_registry.py +22 -5
- claude_mpm/services/memory/builder.py +6 -1
- claude_mpm/services/response_tracker.py +3 -1
- claude_mpm/services/runner_configuration_service.py +15 -6
- claude_mpm/services/shared/__init__.py +20 -0
- claude_mpm/services/shared/async_service_base.py +219 -0
- claude_mpm/services/shared/config_service_base.py +292 -0
- claude_mpm/services/shared/lifecycle_service_base.py +317 -0
- claude_mpm/services/shared/manager_base.py +303 -0
- claude_mpm/services/shared/service_factory.py +308 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/METADATA +19 -13
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/RECORD +70 -54
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.22.dist-info → claude_mpm-4.0.25.dist-info}/top_level.txt +0 -0
|
@@ -4,10 +4,17 @@ from pathlib import Path
|
|
|
4
4
|
|
|
5
5
|
WHY: Provides command-line interface for managing the event aggregator service
|
|
6
6
|
that captures Socket.IO events and saves them as structured session documents.
|
|
7
|
+
|
|
8
|
+
DESIGN DECISIONS:
|
|
9
|
+
- Use BaseCommand for consistent CLI patterns
|
|
10
|
+
- Leverage shared utilities for argument parsing and output formatting
|
|
11
|
+
- Maintain backward compatibility with existing event aggregator integration
|
|
12
|
+
- Support multiple output formats (json, yaml, table, text)
|
|
7
13
|
"""
|
|
8
14
|
|
|
9
15
|
import json
|
|
10
16
|
import sys
|
|
17
|
+
from typing import Optional
|
|
11
18
|
|
|
12
19
|
from ...core.logger import get_logger
|
|
13
20
|
from ...models.agent_session import AgentSession
|
|
@@ -17,36 +24,121 @@ from ...services.event_aggregator import (
|
|
|
17
24
|
start_aggregator,
|
|
18
25
|
stop_aggregator,
|
|
19
26
|
)
|
|
27
|
+
from ..shared import BaseCommand, CommandResult
|
|
20
28
|
|
|
21
29
|
logger = get_logger("cli.aggregate")
|
|
22
30
|
|
|
23
31
|
|
|
32
|
+
class AggregateCommand(BaseCommand):
|
|
33
|
+
"""Aggregate command using shared utilities."""
|
|
34
|
+
|
|
35
|
+
def __init__(self):
|
|
36
|
+
super().__init__("aggregate")
|
|
37
|
+
|
|
38
|
+
def validate_args(self, args) -> Optional[str]:
|
|
39
|
+
"""Validate command arguments."""
|
|
40
|
+
if not hasattr(args, 'aggregate_subcommand') or not args.aggregate_subcommand:
|
|
41
|
+
return "No aggregate subcommand specified"
|
|
42
|
+
|
|
43
|
+
valid_commands = ["start", "stop", "status", "sessions", "view", "export"]
|
|
44
|
+
if args.aggregate_subcommand not in valid_commands:
|
|
45
|
+
return f"Unknown aggregate command: {args.aggregate_subcommand}. Valid commands: {', '.join(valid_commands)}"
|
|
46
|
+
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
def run(self, args) -> CommandResult:
|
|
50
|
+
"""Execute the aggregate command."""
|
|
51
|
+
try:
|
|
52
|
+
# Route to specific subcommand handlers
|
|
53
|
+
command_map = {
|
|
54
|
+
"start": self._start_command,
|
|
55
|
+
"stop": self._stop_command,
|
|
56
|
+
"status": self._status_command,
|
|
57
|
+
"sessions": self._sessions_command,
|
|
58
|
+
"view": self._view_command,
|
|
59
|
+
"export": self._export_command,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if args.aggregate_subcommand in command_map:
|
|
63
|
+
exit_code = command_map[args.aggregate_subcommand](args)
|
|
64
|
+
if exit_code == 0:
|
|
65
|
+
return CommandResult.success_result(f"Aggregate {args.aggregate_subcommand} completed successfully")
|
|
66
|
+
else:
|
|
67
|
+
return CommandResult.error_result(f"Aggregate {args.aggregate_subcommand} failed", exit_code=exit_code)
|
|
68
|
+
else:
|
|
69
|
+
return CommandResult.error_result(f"Unknown aggregate command: {args.aggregate_subcommand}")
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
self.logger.error(f"Error executing aggregate command: {e}", exc_info=True)
|
|
73
|
+
return CommandResult.error_result(f"Error executing aggregate command: {e}")
|
|
74
|
+
|
|
75
|
+
def _start_command(self, args) -> int:
|
|
76
|
+
"""Start the event aggregator service."""
|
|
77
|
+
return start_command_legacy(args)
|
|
78
|
+
|
|
79
|
+
def _stop_command(self, args) -> int:
|
|
80
|
+
"""Stop the event aggregator service."""
|
|
81
|
+
return stop_command_legacy(args)
|
|
82
|
+
|
|
83
|
+
def _status_command(self, args) -> int:
|
|
84
|
+
"""Show status of the event aggregator service."""
|
|
85
|
+
return status_command_legacy(args)
|
|
86
|
+
|
|
87
|
+
def _sessions_command(self, args) -> int:
|
|
88
|
+
"""List captured sessions."""
|
|
89
|
+
return sessions_command_legacy(args)
|
|
90
|
+
|
|
91
|
+
def _view_command(self, args) -> int:
|
|
92
|
+
"""View details of a specific session."""
|
|
93
|
+
return view_command_legacy(args)
|
|
94
|
+
|
|
95
|
+
def _export_command(self, args) -> int:
|
|
96
|
+
"""Export a session to a file."""
|
|
97
|
+
return export_command_legacy(args)
|
|
98
|
+
|
|
99
|
+
|
|
24
100
|
def aggregate_command(args):
|
|
25
|
-
"""
|
|
101
|
+
"""
|
|
102
|
+
Main entry point for aggregate command.
|
|
103
|
+
|
|
104
|
+
This function maintains backward compatibility while using the new BaseCommand pattern.
|
|
105
|
+
"""
|
|
106
|
+
command = AggregateCommand()
|
|
107
|
+
result = command.execute(args)
|
|
108
|
+
|
|
109
|
+
# Print result if structured output format is requested
|
|
110
|
+
if hasattr(args, 'format') and args.format in ['json', 'yaml']:
|
|
111
|
+
command.print_result(result, args)
|
|
112
|
+
|
|
113
|
+
return result.exit_code
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def aggregate_command_legacy(args):
|
|
117
|
+
"""Legacy aggregate command dispatcher.
|
|
26
118
|
|
|
27
|
-
WHY:
|
|
28
|
-
|
|
119
|
+
WHY: This contains the original aggregate_command logic, preserved during migration
|
|
120
|
+
to BaseCommand pattern. Will be gradually refactored into the AggregateCommand class.
|
|
29
121
|
"""
|
|
30
122
|
subcommand = args.aggregate_subcommand
|
|
31
123
|
|
|
32
124
|
if subcommand == "start":
|
|
33
|
-
return
|
|
125
|
+
return start_command_legacy(args)
|
|
34
126
|
elif subcommand == "stop":
|
|
35
|
-
return
|
|
127
|
+
return stop_command_legacy(args)
|
|
36
128
|
elif subcommand == "status":
|
|
37
|
-
return
|
|
129
|
+
return status_command_legacy(args)
|
|
38
130
|
elif subcommand == "sessions":
|
|
39
|
-
return
|
|
131
|
+
return sessions_command_legacy(args)
|
|
40
132
|
elif subcommand == "view":
|
|
41
|
-
return
|
|
133
|
+
return view_command_legacy(args)
|
|
42
134
|
elif subcommand == "export":
|
|
43
|
-
return
|
|
135
|
+
return export_command_legacy(args)
|
|
44
136
|
else:
|
|
45
137
|
print(f"Unknown subcommand: {subcommand}", file=sys.stderr)
|
|
46
138
|
return 1
|
|
47
139
|
|
|
48
140
|
|
|
49
|
-
def
|
|
141
|
+
def start_command_legacy(args):
|
|
50
142
|
"""Start the event aggregator service.
|
|
51
143
|
|
|
52
144
|
WHY: Starts capturing events from the Socket.IO dashboard server
|
|
@@ -105,7 +197,7 @@ def start_command(args):
|
|
|
105
197
|
return 1
|
|
106
198
|
|
|
107
199
|
|
|
108
|
-
def
|
|
200
|
+
def stop_command_legacy(args):
|
|
109
201
|
"""Stop the event aggregator service.
|
|
110
202
|
|
|
111
203
|
WHY: Gracefully stops the aggregator and saves any active sessions.
|
|
@@ -136,7 +228,7 @@ def stop_command(args):
|
|
|
136
228
|
return 0
|
|
137
229
|
|
|
138
230
|
|
|
139
|
-
def
|
|
231
|
+
def status_command_legacy(args):
|
|
140
232
|
"""Show status of the event aggregator service.
|
|
141
233
|
|
|
142
234
|
WHY: Provides visibility into what the aggregator is doing and
|
|
@@ -169,7 +261,7 @@ def status_command(args):
|
|
|
169
261
|
return 0
|
|
170
262
|
|
|
171
263
|
|
|
172
|
-
def
|
|
264
|
+
def sessions_command_legacy(args):
|
|
173
265
|
"""List captured sessions.
|
|
174
266
|
|
|
175
267
|
WHY: Shows what sessions have been captured for analysis.
|
|
@@ -198,7 +290,7 @@ def sessions_command(args):
|
|
|
198
290
|
return 0
|
|
199
291
|
|
|
200
292
|
|
|
201
|
-
def
|
|
293
|
+
def view_command_legacy(args):
|
|
202
294
|
"""View details of a specific session.
|
|
203
295
|
|
|
204
296
|
WHY: Allows detailed inspection of what happened during a session.
|
|
@@ -293,7 +385,7 @@ def view_command(args):
|
|
|
293
385
|
return 0
|
|
294
386
|
|
|
295
387
|
|
|
296
|
-
def
|
|
388
|
+
def export_command_legacy(args):
|
|
297
389
|
"""Export a session to a file.
|
|
298
390
|
|
|
299
391
|
WHY: Allows sessions to be exported for external analysis or sharing.
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
1
|
"""
|
|
4
2
|
Memory cleanup command implementation for claude-mpm.
|
|
5
3
|
|
|
@@ -8,19 +6,22 @@ Claude Desktop loads the entire conversation history into memory, leading to 2GB
|
|
|
8
6
|
consumption. This command helps users manage and clean up their conversation history.
|
|
9
7
|
|
|
10
8
|
DESIGN DECISIONS:
|
|
9
|
+
- Use BaseCommand for consistent CLI patterns
|
|
11
10
|
- Archive old conversations instead of deleting them
|
|
12
11
|
- Provide clear feedback about space savings
|
|
13
12
|
- Default to safe operations with confirmation prompts
|
|
14
13
|
- Keep recent conversations (30 days by default) in active memory
|
|
14
|
+
- Support multiple output formats (json, yaml, table, text)
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
18
|
import shutil
|
|
19
19
|
import sys
|
|
20
20
|
from datetime import datetime, timedelta
|
|
21
|
+
from pathlib import Path
|
|
21
22
|
from typing import Any, Dict, List, Tuple
|
|
22
23
|
|
|
23
|
-
from
|
|
24
|
+
from ..shared import BaseCommand, CommandResult
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def add_cleanup_parser(subparsers):
|
|
@@ -269,16 +270,147 @@ def clean_claude_json(
|
|
|
269
270
|
return original_size, original_size
|
|
270
271
|
|
|
271
272
|
|
|
272
|
-
|
|
273
|
-
"""
|
|
273
|
+
class CleanupCommand(BaseCommand):
|
|
274
|
+
"""Memory cleanup command using shared utilities."""
|
|
274
275
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
their conversation history and prevent memory issues.
|
|
276
|
+
def __init__(self):
|
|
277
|
+
super().__init__("cleanup")
|
|
278
278
|
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
def validate_args(self, args) -> str:
|
|
280
|
+
"""Validate command arguments."""
|
|
281
|
+
# Validate max_size format
|
|
282
|
+
max_size = getattr(args, 'max_size', '500KB')
|
|
283
|
+
try:
|
|
284
|
+
parse_size(max_size)
|
|
285
|
+
except ValueError as e:
|
|
286
|
+
return str(e)
|
|
287
|
+
|
|
288
|
+
# Validate days
|
|
289
|
+
days = getattr(args, 'days', 30)
|
|
290
|
+
if days < 0:
|
|
291
|
+
return "Days must be a positive number"
|
|
292
|
+
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
def run(self, args) -> CommandResult:
|
|
296
|
+
"""Execute the cleanup command."""
|
|
297
|
+
try:
|
|
298
|
+
# Gather cleanup information
|
|
299
|
+
cleanup_data = self._analyze_cleanup_needs(args)
|
|
300
|
+
|
|
301
|
+
output_format = getattr(args, 'format', 'text')
|
|
302
|
+
|
|
303
|
+
if output_format in ['json', 'yaml']:
|
|
304
|
+
# Structured output
|
|
305
|
+
if getattr(args, 'dry_run', False):
|
|
306
|
+
return CommandResult.success_result("Cleanup analysis completed (dry run)", data=cleanup_data)
|
|
307
|
+
else:
|
|
308
|
+
# Perform actual cleanup
|
|
309
|
+
result_data = self._perform_cleanup(args, cleanup_data)
|
|
310
|
+
return CommandResult.success_result("Cleanup completed", data=result_data)
|
|
311
|
+
else:
|
|
312
|
+
# Text output using existing function
|
|
313
|
+
cleanup_memory(args)
|
|
314
|
+
return CommandResult.success_result("Cleanup completed")
|
|
315
|
+
|
|
316
|
+
except Exception as e:
|
|
317
|
+
self.logger.error(f"Error during cleanup: {e}", exc_info=True)
|
|
318
|
+
return CommandResult.error_result(f"Error during cleanup: {e}")
|
|
319
|
+
|
|
320
|
+
def _analyze_cleanup_needs(self, args) -> Dict[str, Any]:
|
|
321
|
+
"""Analyze what needs to be cleaned up."""
|
|
322
|
+
claude_json = Path.home() / ".claude.json"
|
|
323
|
+
archive_dir = Path.home() / ".claude-mpm" / "archives"
|
|
324
|
+
|
|
325
|
+
if not claude_json.exists():
|
|
326
|
+
return {
|
|
327
|
+
"file_exists": False,
|
|
328
|
+
"file_path": str(claude_json),
|
|
329
|
+
"needs_cleanup": False,
|
|
330
|
+
"message": "No .claude.json file found - nothing to clean up"
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# Analyze current state
|
|
334
|
+
stats, issues = analyze_claude_json(claude_json)
|
|
335
|
+
|
|
336
|
+
# Check if cleanup is needed
|
|
337
|
+
max_size = parse_size(getattr(args, 'max_size', '500KB'))
|
|
338
|
+
needs_cleanup = stats["file_size"] > max_size
|
|
339
|
+
|
|
340
|
+
return {
|
|
341
|
+
"file_exists": True,
|
|
342
|
+
"file_path": str(claude_json),
|
|
343
|
+
"archive_dir": str(archive_dir),
|
|
344
|
+
"stats": stats,
|
|
345
|
+
"issues": issues,
|
|
346
|
+
"needs_cleanup": needs_cleanup,
|
|
347
|
+
"max_size_bytes": max_size,
|
|
348
|
+
"max_size_formatted": format_size(max_size),
|
|
349
|
+
"current_size_formatted": format_size(stats["file_size"]),
|
|
350
|
+
"settings": {
|
|
351
|
+
"days": getattr(args, 'days', 30),
|
|
352
|
+
"archive": getattr(args, 'archive', True),
|
|
353
|
+
"force": getattr(args, 'force', False),
|
|
354
|
+
"dry_run": getattr(args, 'dry_run', False)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
def _perform_cleanup(self, args, cleanup_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
359
|
+
"""Perform the actual cleanup operation."""
|
|
360
|
+
claude_json = Path(cleanup_data["file_path"])
|
|
361
|
+
archive_dir = Path(cleanup_data["archive_dir"])
|
|
362
|
+
|
|
363
|
+
result = {
|
|
364
|
+
"archive_created": False,
|
|
365
|
+
"archive_path": None,
|
|
366
|
+
"original_size": cleanup_data["stats"]["file_size"],
|
|
367
|
+
"new_size": cleanup_data["stats"]["file_size"],
|
|
368
|
+
"savings": 0,
|
|
369
|
+
"old_archives_removed": 0
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
# Create archive if requested
|
|
373
|
+
if cleanup_data["settings"]["archive"] and not cleanup_data["settings"]["dry_run"]:
|
|
374
|
+
try:
|
|
375
|
+
archive_path = create_archive(claude_json, archive_dir)
|
|
376
|
+
result["archive_created"] = True
|
|
377
|
+
result["archive_path"] = str(archive_path)
|
|
378
|
+
except Exception as e:
|
|
379
|
+
raise Exception(f"Failed to create archive: {e}")
|
|
380
|
+
|
|
381
|
+
# Perform cleanup
|
|
382
|
+
original_size, new_size = clean_claude_json(
|
|
383
|
+
claude_json,
|
|
384
|
+
keep_days=cleanup_data["settings"]["days"],
|
|
385
|
+
dry_run=cleanup_data["settings"]["dry_run"]
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
result["original_size"] = original_size
|
|
389
|
+
result["new_size"] = new_size
|
|
390
|
+
result["savings"] = original_size - new_size
|
|
391
|
+
|
|
392
|
+
# Clean up old archives
|
|
393
|
+
if cleanup_data["settings"]["archive"] and not cleanup_data["settings"]["dry_run"]:
|
|
394
|
+
old_archives = clean_old_archives(archive_dir, keep_days=90)
|
|
395
|
+
result["old_archives_removed"] = len(old_archives)
|
|
396
|
+
|
|
397
|
+
return result
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def cleanup_memory(args):
|
|
401
|
+
"""
|
|
402
|
+
Main entry point for cleanup command.
|
|
403
|
+
|
|
404
|
+
This function maintains backward compatibility while using the new BaseCommand pattern.
|
|
281
405
|
"""
|
|
406
|
+
# For complex interactive commands like this, we'll delegate to the original implementation
|
|
407
|
+
# but could be refactored to use the new pattern in the future
|
|
408
|
+
_cleanup_memory_original(args)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _cleanup_memory_original(args):
|
|
412
|
+
"""Original cleanup implementation for backward compatibility."""
|
|
413
|
+
from ...core.logger import get_logger
|
|
282
414
|
logger = get_logger("cleanup")
|
|
283
415
|
|
|
284
416
|
# File paths
|