claude-mpm 3.9.8__py3-none-any.whl → 3.9.11__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 (61) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/base_agent.json +1 -1
  3. claude_mpm/agents/templates/memory_manager.json +155 -0
  4. claude_mpm/cli/__init__.py +18 -3
  5. claude_mpm/cli/commands/__init__.py +6 -1
  6. claude_mpm/cli/commands/cleanup.py +21 -1
  7. claude_mpm/cli/commands/mcp.py +967 -0
  8. claude_mpm/cli/commands/run_guarded.py +511 -0
  9. claude_mpm/cli/parser.py +156 -3
  10. claude_mpm/config/experimental_features.py +219 -0
  11. claude_mpm/config/memory_guardian_config.py +325 -0
  12. claude_mpm/config/memory_guardian_yaml.py +335 -0
  13. claude_mpm/constants.py +14 -0
  14. claude_mpm/core/memory_aware_runner.py +353 -0
  15. claude_mpm/hooks/claude_hooks/hook_handler.py +76 -19
  16. claude_mpm/models/state_models.py +433 -0
  17. claude_mpm/services/communication/__init__.py +2 -2
  18. claude_mpm/services/communication/socketio.py +18 -16
  19. claude_mpm/services/infrastructure/__init__.py +4 -1
  20. claude_mpm/services/infrastructure/context_preservation.py +537 -0
  21. claude_mpm/services/infrastructure/graceful_degradation.py +616 -0
  22. claude_mpm/services/infrastructure/health_monitor.py +775 -0
  23. claude_mpm/services/infrastructure/logging.py +3 -3
  24. claude_mpm/services/infrastructure/memory_dashboard.py +479 -0
  25. claude_mpm/services/infrastructure/memory_guardian.py +944 -0
  26. claude_mpm/services/infrastructure/restart_protection.py +642 -0
  27. claude_mpm/services/infrastructure/state_manager.py +774 -0
  28. claude_mpm/services/mcp_gateway/__init__.py +39 -23
  29. claude_mpm/services/mcp_gateway/core/__init__.py +2 -2
  30. claude_mpm/services/mcp_gateway/core/interfaces.py +10 -9
  31. claude_mpm/services/mcp_gateway/main.py +356 -0
  32. claude_mpm/services/mcp_gateway/manager.py +334 -0
  33. claude_mpm/services/mcp_gateway/registry/__init__.py +6 -3
  34. claude_mpm/services/mcp_gateway/registry/service_registry.py +393 -0
  35. claude_mpm/services/mcp_gateway/registry/tool_registry.py +477 -0
  36. claude_mpm/services/mcp_gateway/server/__init__.py +9 -3
  37. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +431 -0
  38. claude_mpm/services/mcp_gateway/server/stdio_handler.py +373 -0
  39. claude_mpm/services/mcp_gateway/tools/__init__.py +16 -3
  40. claude_mpm/services/mcp_gateway/tools/base_adapter.py +496 -0
  41. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +729 -0
  42. claude_mpm/services/mcp_gateway/tools/hello_world.py +551 -0
  43. claude_mpm/services/ticket_manager.py +8 -8
  44. claude_mpm/services/ticket_manager_di.py +5 -5
  45. claude_mpm/storage/__init__.py +9 -0
  46. claude_mpm/storage/state_storage.py +556 -0
  47. claude_mpm/utils/file_utils.py +293 -0
  48. claude_mpm/utils/platform_memory.py +524 -0
  49. claude_mpm/utils/subprocess_utils.py +305 -0
  50. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/METADATA +27 -2
  51. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/RECORD +56 -32
  52. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
  53. claude_mpm/agents/templates/.claude-mpm/memories/engineer_agent.md +0 -39
  54. claude_mpm/agents/templates/.claude-mpm/memories/qa_agent.md +0 -38
  55. claude_mpm/agents/templates/.claude-mpm/memories/research_agent.md +0 -39
  56. claude_mpm/agents/templates/.claude-mpm/memories/version_control_agent.md +0 -38
  57. /claude_mpm/agents/templates/{research_memory_efficient.json → backup/research_memory_efficient.json} +0 -0
  58. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/WHEEL +0 -0
  59. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/entry_points.txt +0 -0
  60. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/licenses/LICENSE +0 -0
  61. {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,511 @@
1
+ """Run-guarded command implementation for memory-aware Claude execution.
2
+
3
+ WHY: This experimental command provides memory monitoring and automatic restart
4
+ capabilities for Claude Code sessions, preventing memory-related crashes.
5
+
6
+ DESIGN DECISION: This is kept completely separate from the main run command to
7
+ ensure stability. It extends ClaudeRunner through MemoryAwareClaudeRunner without
8
+ modifying the base implementation.
9
+
10
+ STATUS: EXPERIMENTAL - This feature is in beta and may change or have issues.
11
+ """
12
+
13
+ import argparse
14
+ import asyncio
15
+ import os
16
+ import sys
17
+ from pathlib import Path
18
+ from typing import Optional, Dict, Any
19
+
20
+ from claude_mpm.core.memory_aware_runner import MemoryAwareClaudeRunner
21
+ from claude_mpm.core.logging_config import get_logger
22
+ from claude_mpm.cli.utils import setup_logging
23
+ from claude_mpm.config.experimental_features import get_experimental_features
24
+ from claude_mpm.config.memory_guardian_config import (
25
+ MemoryGuardianConfig,
26
+ MemoryThresholds,
27
+ RestartPolicy,
28
+ MonitoringConfig,
29
+ get_default_config
30
+ )
31
+ from claude_mpm.constants import LogLevel
32
+
33
+
34
+ logger = get_logger(__name__)
35
+
36
+
37
+ def add_run_guarded_parser(subparsers) -> argparse.ArgumentParser:
38
+ """Add run-guarded command parser.
39
+
40
+ Args:
41
+ subparsers: Subparsers object from main parser
42
+
43
+ Returns:
44
+ The run-guarded parser for further configuration
45
+ """
46
+ parser = subparsers.add_parser(
47
+ 'run-guarded',
48
+ help='(EXPERIMENTAL) Run Claude with memory monitoring and automatic restart',
49
+ description=(
50
+ '⚠️ EXPERIMENTAL FEATURE\n\n'
51
+ 'Run Claude Code with memory monitoring and automatic restart capabilities. '
52
+ 'This command monitors memory usage and performs controlled restarts when '
53
+ 'thresholds are exceeded, preserving conversation state across restarts.\n\n'
54
+ 'NOTE: This is a beta feature. Use with caution in production environments.'
55
+ ),
56
+ formatter_class=argparse.RawDescriptionHelpFormatter,
57
+ epilog="""
58
+ Examples:
59
+ # Run with default settings (18GB threshold, 30s checks)
60
+ claude-mpm run-guarded
61
+
62
+ # Custom memory threshold (in MB)
63
+ claude-mpm run-guarded --memory-threshold 16000
64
+
65
+ # Faster monitoring for development
66
+ claude-mpm run-guarded --check-interval 10
67
+
68
+ # Limit restart attempts
69
+ claude-mpm run-guarded --max-restarts 5
70
+
71
+ # Disable state preservation (faster restarts)
72
+ claude-mpm run-guarded --no-state-preservation
73
+
74
+ # Use configuration file
75
+ claude-mpm run-guarded --config ~/.claude-mpm/memory-guardian.yaml
76
+
77
+ Memory thresholds:
78
+ - Warning: 80% of threshold (logs warning)
79
+ - Critical: 100% of threshold (triggers restart)
80
+ - Emergency: 120% of threshold (immediate restart)
81
+
82
+ State preservation:
83
+ When enabled, the runner captures and restores:
84
+ - Current conversation context
85
+ - Open files and working directory
86
+ - Environment variables
87
+ - Recent command history
88
+ """
89
+ )
90
+
91
+ # Memory monitoring options
92
+ memory_group = parser.add_argument_group('memory monitoring')
93
+ memory_group.add_argument(
94
+ '--memory-threshold',
95
+ type=float,
96
+ default=18000, # 18GB in MB
97
+ help='Memory threshold in MB before restart (default: 18000MB/18GB)'
98
+ )
99
+ memory_group.add_argument(
100
+ '--check-interval',
101
+ type=int,
102
+ default=30,
103
+ help='Memory check interval in seconds (default: 30)'
104
+ )
105
+ memory_group.add_argument(
106
+ '--warning-threshold',
107
+ type=float,
108
+ help='Warning threshold in MB (default: 80%% of memory threshold)'
109
+ )
110
+ memory_group.add_argument(
111
+ '--emergency-threshold',
112
+ type=float,
113
+ help='Emergency threshold in MB (default: 120%% of memory threshold)'
114
+ )
115
+
116
+ # Restart policy options
117
+ restart_group = parser.add_argument_group('restart policy')
118
+ restart_group.add_argument(
119
+ '--max-restarts',
120
+ type=int,
121
+ default=3,
122
+ help='Maximum number of automatic restarts (default: 3)'
123
+ )
124
+ restart_group.add_argument(
125
+ '--restart-cooldown',
126
+ type=int,
127
+ default=10,
128
+ help='Cooldown period between restarts in seconds (default: 10)'
129
+ )
130
+ restart_group.add_argument(
131
+ '--graceful-timeout',
132
+ type=int,
133
+ default=30,
134
+ help='Timeout for graceful shutdown in seconds (default: 30)'
135
+ )
136
+
137
+ # State preservation options
138
+ state_group = parser.add_argument_group('state preservation')
139
+ state_group.add_argument(
140
+ '--enable-state-preservation',
141
+ action='store_true',
142
+ default=True,
143
+ dest='state_preservation',
144
+ help='Enable state preservation across restarts (default: enabled)'
145
+ )
146
+ state_group.add_argument(
147
+ '--no-state-preservation',
148
+ action='store_false',
149
+ dest='state_preservation',
150
+ help='Disable state preservation for faster restarts'
151
+ )
152
+ state_group.add_argument(
153
+ '--state-dir',
154
+ type=Path,
155
+ help='Directory for state files (default: ~/.claude-mpm/state)'
156
+ )
157
+
158
+ # Configuration file
159
+ parser.add_argument(
160
+ '--config',
161
+ '--config-file',
162
+ type=Path,
163
+ dest='config_file',
164
+ help='Path to memory guardian configuration file (YAML)'
165
+ )
166
+
167
+ # Monitoring display options
168
+ display_group = parser.add_argument_group('display options')
169
+ display_group.add_argument(
170
+ '--quiet',
171
+ action='store_true',
172
+ help='Minimal output, only show critical events'
173
+ )
174
+ display_group.add_argument(
175
+ '--verbose',
176
+ action='store_true',
177
+ help='Verbose output with detailed monitoring information'
178
+ )
179
+ display_group.add_argument(
180
+ '--show-stats',
181
+ action='store_true',
182
+ help='Display memory statistics periodically'
183
+ )
184
+ display_group.add_argument(
185
+ '--stats-interval',
186
+ type=int,
187
+ default=60,
188
+ help='Statistics display interval in seconds (default: 60)'
189
+ )
190
+
191
+ # Claude runner options (inherited from run command)
192
+ run_group = parser.add_argument_group('claude options')
193
+ run_group.add_argument(
194
+ '--no-hooks',
195
+ action='store_true',
196
+ help='Disable hook service'
197
+ )
198
+ run_group.add_argument(
199
+ '--no-tickets',
200
+ action='store_true',
201
+ help='Disable automatic ticket creation'
202
+ )
203
+ run_group.add_argument(
204
+ '--no-native-agents',
205
+ action='store_true',
206
+ help='Disable deployment of Claude Code native agents'
207
+ )
208
+ run_group.add_argument(
209
+ '--websocket-port',
210
+ type=int,
211
+ default=8765,
212
+ help='WebSocket server port (default: 8765)'
213
+ )
214
+
215
+ # Input/output options
216
+ io_group = parser.add_argument_group('input/output')
217
+ io_group.add_argument(
218
+ '-i', '--input',
219
+ type=str,
220
+ help='Input text or file path for initial context'
221
+ )
222
+ io_group.add_argument(
223
+ '--non-interactive',
224
+ action='store_true',
225
+ help='Run in non-interactive mode'
226
+ )
227
+
228
+ # Logging options
229
+ logging_group = parser.add_argument_group('logging')
230
+ logging_group.add_argument(
231
+ '--logging',
232
+ choices=[level.value for level in LogLevel],
233
+ default=LogLevel.INFO.value,
234
+ help='Logging level (default: INFO)'
235
+ )
236
+ logging_group.add_argument(
237
+ '--log-dir',
238
+ type=Path,
239
+ help='Custom log directory'
240
+ )
241
+
242
+ # Experimental feature control
243
+ experimental_group = parser.add_argument_group('experimental control')
244
+ experimental_group.add_argument(
245
+ '--accept-experimental',
246
+ action='store_true',
247
+ help='Accept experimental status and suppress warning'
248
+ )
249
+ experimental_group.add_argument(
250
+ '--force-experimental',
251
+ action='store_true',
252
+ help='Force run even if experimental features are disabled'
253
+ )
254
+
255
+ # Claude CLI arguments
256
+ parser.add_argument(
257
+ 'claude_args',
258
+ nargs=argparse.REMAINDER,
259
+ help='Additional arguments to pass to Claude CLI'
260
+ )
261
+
262
+ return parser
263
+
264
+
265
+ def load_config_file(config_path: Path) -> Optional[MemoryGuardianConfig]:
266
+ """Load memory guardian configuration from YAML file.
267
+
268
+ Args:
269
+ config_path: Path to configuration file
270
+
271
+ Returns:
272
+ MemoryGuardianConfig or None if loading failed
273
+ """
274
+ try:
275
+ import yaml
276
+
277
+ if not config_path.exists():
278
+ logger.warning(f"Configuration file not found: {config_path}")
279
+ return None
280
+
281
+ with open(config_path, 'r') as f:
282
+ config_data = yaml.safe_load(f)
283
+
284
+ # Create configuration from YAML data
285
+ config = MemoryGuardianConfig()
286
+
287
+ # Update thresholds
288
+ if 'thresholds' in config_data:
289
+ t = config_data['thresholds']
290
+ config.thresholds = MemoryThresholds(
291
+ warning=t.get('warning', config.thresholds.warning),
292
+ critical=t.get('critical', config.thresholds.critical),
293
+ emergency=t.get('emergency', config.thresholds.emergency)
294
+ )
295
+
296
+ # Update monitoring settings
297
+ if 'monitoring' in config_data:
298
+ m = config_data['monitoring']
299
+ config.monitoring = MonitoringConfig(
300
+ normal_interval=m.get('normal_interval', config.monitoring.normal_interval),
301
+ warning_interval=m.get('warning_interval', config.monitoring.warning_interval),
302
+ critical_interval=m.get('critical_interval', config.monitoring.critical_interval),
303
+ log_memory_stats=m.get('log_memory_stats', config.monitoring.log_memory_stats),
304
+ log_interval=m.get('log_interval', config.monitoring.log_interval)
305
+ )
306
+
307
+ # Update restart policy
308
+ if 'restart_policy' in config_data:
309
+ r = config_data['restart_policy']
310
+ config.restart_policy = RestartPolicy(
311
+ max_attempts=r.get('max_attempts', config.restart_policy.max_attempts),
312
+ attempt_window=r.get('attempt_window', config.restart_policy.attempt_window),
313
+ initial_cooldown=r.get('initial_cooldown', config.restart_policy.initial_cooldown),
314
+ cooldown_multiplier=r.get('cooldown_multiplier', config.restart_policy.cooldown_multiplier),
315
+ max_cooldown=r.get('max_cooldown', config.restart_policy.max_cooldown),
316
+ graceful_timeout=r.get('graceful_timeout', config.restart_policy.graceful_timeout),
317
+ force_kill_timeout=r.get('force_kill_timeout', config.restart_policy.force_kill_timeout)
318
+ )
319
+
320
+ # Update general settings
321
+ config.enabled = config_data.get('enabled', True)
322
+ config.auto_start = config_data.get('auto_start', True)
323
+ config.persist_state = config_data.get('persist_state', True)
324
+
325
+ logger.info(f"Loaded configuration from {config_path}")
326
+ return config
327
+
328
+ except ImportError:
329
+ logger.error("PyYAML not installed. Install with: pip install pyyaml")
330
+ return None
331
+ except Exception as e:
332
+ logger.error(f"Failed to load configuration file: {e}")
333
+ return None
334
+
335
+
336
+ def execute_run_guarded(args: argparse.Namespace) -> int:
337
+ """Execute the run-guarded command.
338
+
339
+ WHY: This is the entry point for the experimental memory-guarded execution mode.
340
+ It checks experimental feature flags and shows appropriate warnings before
341
+ delegating to MemoryAwareClaudeRunner.
342
+
343
+ DESIGN DECISION: All experimental checks happen here, before any actual work,
344
+ to ensure users are aware they're using beta functionality.
345
+
346
+ Args:
347
+ args: Parsed command line arguments
348
+
349
+ Returns:
350
+ Exit code (0 for success, non-zero for failure)
351
+ """
352
+ try:
353
+ # Setup logging
354
+ log_level = getattr(args, 'logging', 'INFO')
355
+ log_dir = getattr(args, 'log_dir', None)
356
+ setup_logging(log_level, log_dir)
357
+
358
+ # Check experimental features
359
+ experimental = get_experimental_features()
360
+
361
+ # Check if Memory Guardian is enabled
362
+ if not experimental.is_enabled('memory_guardian') and not getattr(args, 'force_experimental', False):
363
+ logger.error("Memory Guardian is an experimental feature that is currently disabled.")
364
+ print("\n❌ Memory Guardian is disabled in experimental features configuration.")
365
+ print("\nTo enable it:")
366
+ print(" 1. Set environment variable: export CLAUDE_MPM_EXPERIMENTAL_ENABLE_MEMORY_GUARDIAN=true")
367
+ print(" 2. Or use --force-experimental flag to override")
368
+ print(" 3. Or enable in ~/.claude-mpm/experimental.json")
369
+ return 1
370
+
371
+ # Show experimental warning unless suppressed
372
+ if not getattr(args, 'accept_experimental', False):
373
+ if experimental.should_show_warning('memory_guardian'):
374
+ warning = experimental.get_warning('memory_guardian')
375
+ if warning:
376
+ print("\n" + warning)
377
+ print("\nThis feature is experimental and may:")
378
+ print(" • Have bugs or stability issues")
379
+ print(" • Change significantly in future versions")
380
+ print(" • Not work as expected in all environments")
381
+ print("\nContinue? [y/N]: ", end="")
382
+
383
+ try:
384
+ response = input().strip().lower()
385
+ if response != 'y':
386
+ print("\n✅ Cancelled. Use the stable 'run' command instead.")
387
+ return 0
388
+ # Mark as accepted for this session
389
+ experimental.mark_accepted('memory_guardian')
390
+ except (EOFError, KeyboardInterrupt):
391
+ print("\n✅ Cancelled.")
392
+ return 0
393
+
394
+ logger.info("Starting experimental run-guarded command")
395
+
396
+ # Load configuration
397
+ config = None
398
+ if hasattr(args, 'config_file') and args.config_file:
399
+ config = load_config_file(args.config_file)
400
+
401
+ if config is None:
402
+ # Create configuration from command line arguments
403
+ config = MemoryGuardianConfig()
404
+
405
+ # Set thresholds
406
+ config.thresholds.critical = args.memory_threshold
407
+
408
+ if hasattr(args, 'warning_threshold') and args.warning_threshold:
409
+ config.thresholds.warning = args.warning_threshold
410
+ else:
411
+ config.thresholds.warning = args.memory_threshold * 0.8
412
+
413
+ if hasattr(args, 'emergency_threshold') and args.emergency_threshold:
414
+ config.thresholds.emergency = args.emergency_threshold
415
+ else:
416
+ config.thresholds.emergency = args.memory_threshold * 1.2
417
+
418
+ # Set monitoring settings
419
+ config.monitoring.normal_interval = args.check_interval
420
+ config.monitoring.log_memory_stats = args.show_stats if hasattr(args, 'show_stats') else False
421
+
422
+ if hasattr(args, 'stats_interval'):
423
+ config.monitoring.log_interval = args.stats_interval
424
+
425
+ # Set restart policy
426
+ config.restart_policy.max_attempts = args.max_restarts
427
+
428
+ if hasattr(args, 'restart_cooldown'):
429
+ config.restart_policy.initial_cooldown = args.restart_cooldown
430
+
431
+ if hasattr(args, 'graceful_timeout'):
432
+ config.restart_policy.graceful_timeout = args.graceful_timeout
433
+
434
+ # Override config with CLI arguments
435
+ if hasattr(args, 'memory_threshold'):
436
+ config.thresholds.critical = args.memory_threshold
437
+
438
+ if hasattr(args, 'check_interval'):
439
+ config.monitoring.normal_interval = args.check_interval
440
+
441
+ if hasattr(args, 'max_restarts'):
442
+ config.restart_policy.max_attempts = args.max_restarts
443
+
444
+ # Check mode early to avoid unnecessary setup
445
+ if getattr(args, 'non_interactive', False):
446
+ logger.error("Non-interactive mode not yet supported for run-guarded")
447
+ return 1
448
+
449
+ # Determine verbosity
450
+ if hasattr(args, 'quiet') and args.quiet:
451
+ log_level = 'WARNING'
452
+ elif hasattr(args, 'verbose') and args.verbose:
453
+ log_level = 'DEBUG'
454
+
455
+ # Create runner
456
+ runner = MemoryAwareClaudeRunner(
457
+ enable_tickets=not getattr(args, 'no_tickets', False),
458
+ log_level=log_level,
459
+ claude_args=getattr(args, 'claude_args', []),
460
+ launch_method='subprocess', # Always subprocess for monitoring
461
+ enable_websocket=False, # Could be enabled in future
462
+ websocket_port=getattr(args, 'websocket_port', 8765),
463
+ memory_config=config,
464
+ enable_monitoring=True,
465
+ state_dir=getattr(args, 'state_dir', None)
466
+ )
467
+
468
+ # Deploy agents if not disabled
469
+ if not getattr(args, 'no_native_agents', False):
470
+ if not runner.setup_agents():
471
+ logger.warning("Failed to deploy some agents, continuing anyway")
472
+
473
+ # Get initial context if provided
474
+ initial_context = None
475
+ if hasattr(args, 'input') and args.input:
476
+ if Path(args.input).exists():
477
+ with open(args.input, 'r') as f:
478
+ initial_context = f.read()
479
+ else:
480
+ initial_context = args.input
481
+
482
+ # Run with monitoring
483
+ runner.run_interactive_with_monitoring(
484
+ initial_context=initial_context,
485
+ memory_threshold=config.thresholds.critical,
486
+ check_interval=config.monitoring.normal_interval,
487
+ max_restarts=config.restart_policy.max_attempts,
488
+ enable_state_preservation=getattr(args, 'state_preservation', True)
489
+ )
490
+
491
+ return 0
492
+
493
+ except KeyboardInterrupt:
494
+ logger.info("Run-guarded interrupted by user")
495
+ return 130 # Standard exit code for SIGINT
496
+ except Exception as e:
497
+ logger.error(f"Run-guarded failed: {e}", exc_info=True)
498
+ return 1
499
+
500
+
501
+ # Convenience function for direct module execution
502
+ def main():
503
+ """Main entry point for run-guarded command."""
504
+ parser = argparse.ArgumentParser(description='Run Claude with memory monitoring')
505
+ add_run_guarded_parser(parser._subparsers)
506
+ args = parser.parse_args()
507
+ sys.exit(execute_run_guarded(args))
508
+
509
+
510
+ if __name__ == '__main__':
511
+ main()
claude_mpm/cli/parser.py CHANGED
@@ -14,7 +14,7 @@ import argparse
14
14
  from pathlib import Path
15
15
  from typing import Optional, List
16
16
 
17
- from ..constants import CLICommands, CLIPrefix, AgentCommands, MemoryCommands, MonitorCommands, LogLevel, ConfigCommands, AggregateCommands, TicketCommands
17
+ from ..constants import CLICommands, CLIPrefix, AgentCommands, MemoryCommands, MonitorCommands, LogLevel, ConfigCommands, AggregateCommands, TicketCommands, MCPCommands
18
18
 
19
19
 
20
20
  def add_common_arguments(parser: argparse.ArgumentParser, version: str = None) -> None:
@@ -423,8 +423,8 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
423
423
  )
424
424
  update_ticket_parser.add_argument(
425
425
  "-s", "--status",
426
- choices=["open", "in_progress", "done", "closed", "blocked"],
427
- help="Update status"
426
+ choices=["waiting", "in_progress", "ready", "tested"],
427
+ help="Update workflow state (aitrackdown compatible)"
428
428
  )
429
429
  update_ticket_parser.add_argument(
430
430
  "-p", "--priority",
@@ -979,6 +979,159 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
979
979
  from .commands.cleanup import add_cleanup_parser
980
980
  add_cleanup_parser(subparsers)
981
981
 
982
+ # Import and add run-guarded command parser (EXPERIMENTAL)
983
+ # WHY: run-guarded is kept separate from run to maintain stability
984
+ # DESIGN DECISION: Experimental features are clearly marked and isolated
985
+ from .commands.run_guarded import add_run_guarded_parser
986
+ add_run_guarded_parser(subparsers)
987
+
988
+ # MCP command with subcommands
989
+ mcp_parser = subparsers.add_parser(
990
+ CLICommands.MCP.value,
991
+ help="Manage MCP Gateway server and tools"
992
+ )
993
+ add_common_arguments(mcp_parser)
994
+
995
+ mcp_subparsers = mcp_parser.add_subparsers(
996
+ dest="mcp_command",
997
+ help="MCP commands",
998
+ metavar="SUBCOMMAND"
999
+ )
1000
+
1001
+ # Install MCP Gateway
1002
+ install_mcp_parser = mcp_subparsers.add_parser(
1003
+ MCPCommands.INSTALL.value,
1004
+ help="Install and configure MCP Gateway"
1005
+ )
1006
+ install_mcp_parser.add_argument(
1007
+ "--force",
1008
+ action="store_true",
1009
+ help="Force overwrite existing configuration"
1010
+ )
1011
+
1012
+ # Start MCP server
1013
+ start_mcp_parser = mcp_subparsers.add_parser(
1014
+ MCPCommands.START.value,
1015
+ help="Start the MCP Gateway server"
1016
+ )
1017
+ start_mcp_parser.add_argument(
1018
+ "--mode",
1019
+ choices=["stdio", "standalone"],
1020
+ default="stdio",
1021
+ help="Server mode: stdio for Claude integration, standalone for testing (default: stdio)"
1022
+ )
1023
+ start_mcp_parser.add_argument(
1024
+ "--port",
1025
+ type=int,
1026
+ default=8766,
1027
+ help="Port for standalone mode (default: 8766)"
1028
+ )
1029
+ start_mcp_parser.add_argument(
1030
+ "--config-file",
1031
+ type=Path,
1032
+ help="Path to MCP configuration file"
1033
+ )
1034
+
1035
+ # Stop MCP server
1036
+ stop_mcp_parser = mcp_subparsers.add_parser(
1037
+ MCPCommands.STOP.value,
1038
+ help="Stop the MCP Gateway server"
1039
+ )
1040
+
1041
+ # MCP status
1042
+ status_mcp_parser = mcp_subparsers.add_parser(
1043
+ MCPCommands.STATUS.value,
1044
+ help="Check server and tool status"
1045
+ )
1046
+ status_mcp_parser.add_argument(
1047
+ "--verbose",
1048
+ action="store_true",
1049
+ help="Show detailed status information"
1050
+ )
1051
+
1052
+ # List/manage tools
1053
+ tools_mcp_parser = mcp_subparsers.add_parser(
1054
+ MCPCommands.TOOLS.value,
1055
+ help="List and manage registered tools"
1056
+ )
1057
+ tools_mcp_parser.add_argument(
1058
+ "tool_action",
1059
+ nargs="?",
1060
+ choices=["list", "enable", "disable"],
1061
+ default="list",
1062
+ help="Tool action (default: list)"
1063
+ )
1064
+ tools_mcp_parser.add_argument(
1065
+ "tool_name",
1066
+ nargs="?",
1067
+ help="Tool name for enable/disable actions"
1068
+ )
1069
+ tools_mcp_parser.add_argument(
1070
+ "--verbose",
1071
+ action="store_true",
1072
+ help="Show detailed tool information including schemas"
1073
+ )
1074
+
1075
+ # Register new tool
1076
+ register_mcp_parser = mcp_subparsers.add_parser(
1077
+ MCPCommands.REGISTER.value,
1078
+ help="Register a new MCP tool"
1079
+ )
1080
+ register_mcp_parser.add_argument(
1081
+ "name",
1082
+ help="Tool name"
1083
+ )
1084
+ register_mcp_parser.add_argument(
1085
+ "description",
1086
+ help="Tool description"
1087
+ )
1088
+ register_mcp_parser.add_argument(
1089
+ "--schema-file",
1090
+ type=Path,
1091
+ help="Path to JSON schema file for tool input"
1092
+ )
1093
+ register_mcp_parser.add_argument(
1094
+ "--adapter",
1095
+ help="Path to custom tool adapter module"
1096
+ )
1097
+ register_mcp_parser.add_argument(
1098
+ "--save",
1099
+ action="store_true",
1100
+ help="Save tool to configuration"
1101
+ )
1102
+
1103
+ # Test tool invocation
1104
+ test_mcp_parser = mcp_subparsers.add_parser(
1105
+ MCPCommands.TEST.value,
1106
+ help="Test MCP tool invocation"
1107
+ )
1108
+ test_mcp_parser.add_argument(
1109
+ "tool_name",
1110
+ help="Name of tool to test"
1111
+ )
1112
+ test_mcp_parser.add_argument(
1113
+ "--args",
1114
+ help="Tool arguments as JSON string"
1115
+ )
1116
+ test_mcp_parser.add_argument(
1117
+ "--args-file",
1118
+ type=Path,
1119
+ help="Path to JSON file containing tool arguments"
1120
+ )
1121
+
1122
+ # Manage configuration
1123
+ config_mcp_parser = mcp_subparsers.add_parser(
1124
+ MCPCommands.CONFIG.value,
1125
+ help="View and manage MCP configuration"
1126
+ )
1127
+ config_mcp_parser.add_argument(
1128
+ "config_action",
1129
+ nargs="?",
1130
+ choices=["view", "edit", "reset"],
1131
+ default="view",
1132
+ help="Configuration action (default: view)"
1133
+ )
1134
+
982
1135
  return parser
983
1136
 
984
1137