claude-mpm 3.1.3__py3-none-any.whl → 3.2.1__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 (79) hide show
  1. claude_mpm/__init__.py +3 -3
  2. claude_mpm/__main__.py +0 -17
  3. claude_mpm/agents/INSTRUCTIONS.md +81 -18
  4. claude_mpm/agents/backups/INSTRUCTIONS.md +238 -0
  5. claude_mpm/agents/base_agent.json +1 -1
  6. claude_mpm/agents/templates/pm.json +25 -0
  7. claude_mpm/agents/templates/research.json +2 -1
  8. claude_mpm/cli/__init__.py +19 -23
  9. claude_mpm/cli/commands/__init__.py +3 -1
  10. claude_mpm/cli/commands/agents.py +7 -18
  11. claude_mpm/cli/commands/info.py +5 -10
  12. claude_mpm/cli/commands/memory.py +232 -0
  13. claude_mpm/cli/commands/run.py +501 -28
  14. claude_mpm/cli/commands/tickets.py +10 -17
  15. claude_mpm/cli/commands/ui.py +15 -37
  16. claude_mpm/cli/parser.py +91 -1
  17. claude_mpm/cli/utils.py +9 -28
  18. claude_mpm/config/socketio_config.py +256 -0
  19. claude_mpm/constants.py +9 -0
  20. claude_mpm/core/__init__.py +2 -2
  21. claude_mpm/core/agent_registry.py +4 -4
  22. claude_mpm/core/claude_runner.py +919 -0
  23. claude_mpm/core/config.py +21 -1
  24. claude_mpm/core/factories.py +1 -1
  25. claude_mpm/core/hook_manager.py +196 -0
  26. claude_mpm/core/pm_hook_interceptor.py +205 -0
  27. claude_mpm/core/service_registry.py +1 -1
  28. claude_mpm/core/simple_runner.py +323 -33
  29. claude_mpm/core/socketio_pool.py +582 -0
  30. claude_mpm/core/websocket_handler.py +233 -0
  31. claude_mpm/deployment_paths.py +261 -0
  32. claude_mpm/hooks/builtin/memory_hooks_example.py +67 -0
  33. claude_mpm/hooks/claude_hooks/hook_handler.py +667 -679
  34. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +9 -4
  35. claude_mpm/hooks/memory_integration_hook.py +312 -0
  36. claude_mpm/models/__init__.py +9 -91
  37. claude_mpm/orchestration/__init__.py +1 -1
  38. claude_mpm/scripts/claude-mpm-socketio +32 -0
  39. claude_mpm/scripts/claude_mpm_monitor.html +567 -0
  40. claude_mpm/scripts/install_socketio_server.py +407 -0
  41. claude_mpm/scripts/launch_monitor.py +132 -0
  42. claude_mpm/scripts/manage_version.py +479 -0
  43. claude_mpm/scripts/socketio_daemon.py +181 -0
  44. claude_mpm/scripts/socketio_server_manager.py +428 -0
  45. claude_mpm/services/__init__.py +5 -0
  46. claude_mpm/services/agent_lifecycle_manager.py +76 -25
  47. claude_mpm/services/agent_memory_manager.py +684 -0
  48. claude_mpm/services/agent_modification_tracker.py +98 -17
  49. claude_mpm/services/agent_persistence_service.py +33 -13
  50. claude_mpm/services/agent_registry.py +82 -43
  51. claude_mpm/services/hook_service.py +362 -0
  52. claude_mpm/services/socketio_client_manager.py +474 -0
  53. claude_mpm/services/socketio_server.py +698 -0
  54. claude_mpm/services/standalone_socketio_server.py +631 -0
  55. claude_mpm/services/ticket_manager.py +4 -5
  56. claude_mpm/services/{ticket_manager_dependency_injection.py → ticket_manager_di.py} +12 -39
  57. claude_mpm/services/{legacy_ticketing_service.py → ticketing_service_original.py} +9 -16
  58. claude_mpm/services/version_control/semantic_versioning.py +9 -10
  59. claude_mpm/services/websocket_server.py +376 -0
  60. claude_mpm/utils/dependency_manager.py +211 -0
  61. claude_mpm/utils/import_migration_example.py +80 -0
  62. claude_mpm/utils/path_operations.py +0 -20
  63. claude_mpm/web/open_dashboard.py +34 -0
  64. {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/METADATA +20 -9
  65. {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/RECORD +70 -50
  66. claude_mpm-3.2.1.dist-info/entry_points.txt +7 -0
  67. claude_mpm/cli_old.py +0 -728
  68. claude_mpm/models/common.py +0 -41
  69. claude_mpm/models/lifecycle.py +0 -97
  70. claude_mpm/models/modification.py +0 -126
  71. claude_mpm/models/persistence.py +0 -57
  72. claude_mpm/models/registry.py +0 -91
  73. claude_mpm/security/__init__.py +0 -8
  74. claude_mpm/security/bash_validator.py +0 -393
  75. claude_mpm-3.1.3.dist-info/entry_points.txt +0 -4
  76. /claude_mpm/{cli_enhancements.py → experimental/cli_enhancements.py} +0 -0
  77. {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/WHEEL +0 -0
  78. {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/licenses/LICENSE +0 -0
  79. {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/top_level.txt +0 -0
claude_mpm/cli_old.py DELETED
@@ -1,728 +0,0 @@
1
- """Command-line interface for Claude MPM."""
2
-
3
- import argparse
4
- import subprocess
5
- import sys
6
- from pathlib import Path
7
- from typing import Optional
8
-
9
- try:
10
- # Try relative imports first (when used as package)
11
- from .core.logger import get_logger, setup_logging
12
- from .constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
13
- except ImportError:
14
- # Fall back to absolute imports (when run directly)
15
- from core.logger import get_logger, setup_logging
16
- from constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
17
-
18
- # Get version from VERSION file - single source of truth
19
- version_file = Path(__file__).parent.parent.parent / "VERSION"
20
- if version_file.exists():
21
- __version__ = version_file.read_text().strip()
22
- else:
23
- # Try to import from package as fallback
24
- try:
25
- from . import __version__
26
- except ImportError:
27
- # Default version if all else fails
28
- __version__ = "0.0.0"
29
-
30
-
31
-
32
- def _preprocess_args(argv: Optional[list] = None) -> list:
33
- """Preprocess arguments to handle --mpm: prefix commands."""
34
- if argv is None:
35
- argv = sys.argv[1:]
36
-
37
- # Convert --mpm:command to command for argparse compatibility
38
- processed_args = []
39
- for i, arg in enumerate(argv):
40
- if arg.startswith(CLIPrefix.MPM.value):
41
- # Extract command after prefix
42
- command = arg[len(CLIPrefix.MPM.value):]
43
- processed_args.append(command)
44
- else:
45
- processed_args.append(arg)
46
-
47
- return processed_args
48
-
49
-
50
- def main(argv: Optional[list] = None):
51
- """Main CLI entry point."""
52
- # Ensure directories are initialized on first run
53
- try:
54
- from .init import ensure_directories
55
- ensure_directories()
56
- except Exception:
57
- # Continue even if initialization fails
58
- pass
59
-
60
- parser = argparse.ArgumentParser(
61
- prog="claude-mpm",
62
- description=f"Claude Multi-Agent Project Manager v{__version__} - Orchestrate Claude with agent delegation and ticket tracking",
63
- epilog="By default, runs an orchestrated Claude session. Use 'claude-mpm' for interactive mode or 'claude-mpm -i \"prompt\"' for non-interactive mode.\n\nTo pass arguments to Claude CLI, use -- separator: claude-mpm run -- --model sonnet --temperature 0.1"
64
- )
65
-
66
- # Version
67
- parser.add_argument(
68
- "--version",
69
- action="version",
70
- version=f"claude-mpm {__version__}"
71
- )
72
-
73
- # Global options
74
- parser.add_argument(
75
- "-d", "--debug",
76
- action="store_true",
77
- help="Enable debug logging (deprecated, use --logging DEBUG)"
78
- )
79
-
80
- parser.add_argument(
81
- "--logging",
82
- choices=[level.value for level in LogLevel],
83
- default=LogLevel.INFO.value,
84
- help="Logging level (default: INFO)"
85
- )
86
-
87
- parser.add_argument(
88
- "--log-dir",
89
- type=Path,
90
- help="Custom log directory (default: ~/.claude-mpm/logs)"
91
- )
92
-
93
- parser.add_argument(
94
- "--framework-path",
95
- type=Path,
96
- help="Path to claude-mpm framework"
97
- )
98
-
99
- parser.add_argument(
100
- "--agents-dir",
101
- type=Path,
102
- help="Custom agents directory to use"
103
- )
104
-
105
- parser.add_argument(
106
- "--no-hooks",
107
- action="store_true",
108
- help="Disable hook service (runs without hooks)"
109
- )
110
-
111
- parser.add_argument(
112
- "--intercept-commands",
113
- action="store_true",
114
- help="Enable command interception in interactive mode (intercepts /mpm: commands)"
115
- )
116
-
117
- # Add run-specific arguments at top level (for default behavior)
118
- parser.add_argument(
119
- "--no-tickets",
120
- action="store_true",
121
- help="Disable automatic ticket creation"
122
- )
123
- parser.add_argument(
124
- "-i", "--input",
125
- type=str,
126
- help="Input text or file path (for non-interactive mode)"
127
- )
128
- parser.add_argument(
129
- "--non-interactive",
130
- action="store_true",
131
- help="Run in non-interactive mode (read from stdin or --input)"
132
- )
133
- parser.add_argument(
134
- "--no-native-agents",
135
- action="store_true",
136
- help="Disable deployment of Claude Code native agents"
137
- )
138
-
139
- # Don't add claude_args at top level - it conflicts with subcommands
140
-
141
- # Commands (only non-prefixed for argparse, but we preprocess to support both)
142
- subparsers = parser.add_subparsers(dest="command", help="Available commands")
143
-
144
- # Run command (default)
145
- run_parser = subparsers.add_parser(CLICommands.RUN.value, help="Run orchestrated Claude session (default)")
146
-
147
- run_parser.add_argument(
148
- "--no-hooks",
149
- action="store_true",
150
- help="Disable hook service (runs without hooks)"
151
- )
152
- run_parser.add_argument(
153
- "--no-tickets",
154
- action="store_true",
155
- help="Disable automatic ticket creation"
156
- )
157
- run_parser.add_argument(
158
- "--intercept-commands",
159
- action="store_true",
160
- help="Enable command interception in interactive mode (intercepts /mpm: commands)"
161
- )
162
- run_parser.add_argument(
163
- "-i", "--input",
164
- type=str,
165
- help="Input text or file path (for non-interactive mode)"
166
- )
167
- run_parser.add_argument(
168
- "--non-interactive",
169
- action="store_true",
170
- help="Run in non-interactive mode (read from stdin or --input)"
171
- )
172
- run_parser.add_argument(
173
- "--no-native-agents",
174
- action="store_true",
175
- help="Disable deployment of Claude Code native agents"
176
- )
177
- run_parser.add_argument(
178
- "claude_args",
179
- nargs=argparse.REMAINDER,
180
- help="Additional arguments to pass to Claude CLI (use -- before Claude args)"
181
- )
182
-
183
- # List tickets command
184
- list_parser = subparsers.add_parser(CLICommands.TICKETS.value, help="List recent tickets")
185
- list_parser.add_argument(
186
- "-n", "--limit",
187
- type=int,
188
- default=10,
189
- help="Number of tickets to show"
190
- )
191
-
192
- # Info command
193
- info_parser = subparsers.add_parser(CLICommands.INFO.value, help="Show framework and configuration info")
194
-
195
- # UI command
196
- ui_parser = subparsers.add_parser(CLICommands.UI.value, help="Launch terminal UI with multiple panes")
197
- ui_parser.add_argument(
198
- "--mode",
199
- choices=["terminal", "curses"],
200
- default="terminal",
201
- help="UI mode to launch (default: terminal)"
202
- )
203
-
204
- # Agent management commands
205
- agents_parser = subparsers.add_parser(CLICommands.AGENTS.value, help="Manage Claude Code native agents")
206
- agents_subparsers = agents_parser.add_subparsers(dest="agents_command", help="Agent commands")
207
-
208
- # List agents
209
- list_agents_parser = agents_subparsers.add_parser(AgentCommands.LIST.value, help="List available agents")
210
- list_agents_parser.add_argument(
211
- "--system",
212
- action="store_true",
213
- help="List system agents"
214
- )
215
- list_agents_parser.add_argument(
216
- "--deployed",
217
- action="store_true",
218
- help="List deployed agents"
219
- )
220
-
221
- # Deploy agents
222
- deploy_agents_parser = agents_subparsers.add_parser(AgentCommands.DEPLOY.value, help="Deploy system agents")
223
- deploy_agents_parser.add_argument(
224
- "--target",
225
- type=Path,
226
- help="Target directory (default: .claude/agents/)"
227
- )
228
-
229
- # Force deploy agents
230
- force_deploy_parser = agents_subparsers.add_parser(AgentCommands.FORCE_DEPLOY.value, help="Force deploy all system agents")
231
- force_deploy_parser.add_argument(
232
- "--target",
233
- type=Path,
234
- help="Target directory (default: .claude/agents/)"
235
- )
236
-
237
- # Clean agents
238
- clean_agents_parser = agents_subparsers.add_parser(AgentCommands.CLEAN.value, help="Remove deployed system agents")
239
- clean_agents_parser.add_argument(
240
- "--target",
241
- type=Path,
242
- help="Target directory (default: .claude/)"
243
- )
244
-
245
- # Preprocess and parse arguments
246
- processed_argv = _preprocess_args(argv)
247
- args = parser.parse_args(processed_argv)
248
-
249
- # Debug: Print parsed args
250
- if hasattr(args, 'debug') and args.debug:
251
- print(f"DEBUG: Parsed args: {args}")
252
-
253
- # Set up logging first
254
- # Handle deprecated --debug flag
255
- if args.debug and args.logging == LogLevel.INFO.value:
256
- args.logging = LogLevel.DEBUG.value
257
-
258
- # Only setup logging if not OFF
259
- if args.logging != LogLevel.OFF.value:
260
- logger = setup_logging(level=args.logging, log_dir=args.log_dir)
261
- else:
262
- # Minimal logger for CLI feedback
263
- import logging
264
- logger = logging.getLogger("cli")
265
- logger.setLevel(logging.WARNING)
266
-
267
- # Hook system note: Claude Code hooks are handled externally via the
268
- # hook_handler.py script installed in ~/.claude/settings.json
269
- # The --no-hooks flag is kept for backward compatibility but doesn't affect
270
- # Claude Code hooks which are configured separately.
271
-
272
- # Default to run command
273
- if not args.command:
274
- args.command = CLICommands.RUN.value
275
- # Also set default arguments for run command when no subcommand specified
276
- args.no_tickets = getattr(args, 'no_tickets', False)
277
- args.no_hooks = getattr(args, 'no_hooks', False)
278
- args.input = getattr(args, 'input', None)
279
- args.non_interactive = getattr(args, 'non_interactive', False)
280
- args.claude_args = getattr(args, 'claude_args', [])
281
-
282
- # Debug output
283
- logger.debug(f"Command: {args.command}")
284
- logger.debug(f"Arguments: {args}")
285
-
286
- # Execute command (we've already preprocessed prefixes)
287
- command = args.command
288
-
289
- try:
290
- if command in [CLICommands.RUN.value, None]:
291
- run_session(args)
292
- elif command == CLICommands.TICKETS.value:
293
- list_tickets(args)
294
- elif command == CLICommands.INFO.value:
295
- show_info(args)
296
- elif command == CLICommands.AGENTS.value:
297
- manage_agents(args)
298
- elif command == CLICommands.UI.value:
299
- run_terminal_ui(args)
300
- else:
301
- parser.print_help()
302
- return 1
303
- except KeyboardInterrupt:
304
- logger.info("Session interrupted by user")
305
- return 0
306
- except Exception as e:
307
- logger.error(f"Error: {e}")
308
- if args.debug:
309
- import traceback
310
- traceback.print_exc()
311
- return 1
312
- finally:
313
- # Cleanup handled by individual components
314
- pass
315
-
316
- return 0
317
-
318
-
319
- def _get_user_input(args, logger):
320
- """Get user input based on args."""
321
- if args.input:
322
- # Read from file or use as direct input
323
- input_path = Path(args.input)
324
- if input_path.exists():
325
- logger.info(f"Reading input from file: {input_path}")
326
- return input_path.read_text()
327
- else:
328
- logger.info("Using command line input")
329
- return args.input
330
- else:
331
- # Read from stdin
332
- logger.info("Reading input from stdin")
333
- return sys.stdin.read()
334
-
335
-
336
- def _get_agent_versions_display():
337
- """Get formatted agent versions display as a string.
338
-
339
- WHY: This function was created to provide a single source of truth for agent version
340
- information that can be displayed both at startup and on-demand via the /mpm agents command.
341
- This ensures consistency in how agent versions are presented to users.
342
-
343
- Returns:
344
- str: Formatted string containing agent version information, or None if failed
345
- """
346
- try:
347
- from .services.agent_deployment import AgentDeploymentService
348
- deployment_service = AgentDeploymentService()
349
-
350
- # Get deployed agents
351
- verification = deployment_service.verify_deployment()
352
- if not verification.get("agents_found"):
353
- return None
354
-
355
- output_lines = []
356
- output_lines.append("\nDeployed Agent Versions:")
357
- output_lines.append("-" * 40)
358
-
359
- # Sort agents by name for consistent display
360
- agents = sorted(verification["agents_found"], key=lambda x: x.get('name', x.get('file', '')))
361
-
362
- for agent in agents:
363
- name = agent.get('name', 'unknown')
364
- version = agent.get('version', 'unknown')
365
- # Format: name (version)
366
- output_lines.append(f" {name:<20} {version}")
367
-
368
- # Add base agent version info
369
- try:
370
- import json
371
- base_agent_path = deployment_service.base_agent_path
372
- if base_agent_path.exists():
373
- base_data = json.loads(base_agent_path.read_text())
374
- # Parse version the same way as AgentDeploymentService
375
- raw_version = base_data.get('base_version') or base_data.get('version', 0)
376
- base_version_tuple = deployment_service._parse_version(raw_version)
377
- base_version_str = deployment_service._format_version_display(base_version_tuple)
378
- output_lines.append(f"\n Base Agent Version: {base_version_str}")
379
- except:
380
- pass
381
-
382
- # Check for agents needing migration
383
- if verification.get("agents_needing_migration"):
384
- output_lines.append(f"\n ⚠️ {len(verification['agents_needing_migration'])} agent(s) need migration to semantic versioning")
385
- output_lines.append(f" Run 'claude-mpm agents deploy' to update")
386
-
387
- output_lines.append("-" * 40)
388
- return "\n".join(output_lines)
389
- except Exception as e:
390
- # Log error but don't fail
391
- logger = get_logger("cli")
392
- logger.debug(f"Failed to get agent versions: {e}")
393
- return None
394
-
395
-
396
- def _list_agent_versions_at_startup():
397
- """List deployed agent versions at startup."""
398
- agent_versions = _get_agent_versions_display()
399
- if agent_versions:
400
- print(agent_versions)
401
- print() # Extra newline after the display
402
-
403
-
404
-
405
-
406
- def run_session(args):
407
- """Run a simplified Claude session."""
408
- logger = get_logger("cli")
409
- if args.logging != LogLevel.OFF.value:
410
- logger.info("Starting Claude MPM session")
411
-
412
- try:
413
- from .core.simple_runner import SimpleClaudeRunner, create_simple_context
414
- except ImportError:
415
- from core.simple_runner import SimpleClaudeRunner, create_simple_context
416
-
417
- # Skip native agents if disabled
418
- if getattr(args, 'no_native_agents', False):
419
- print("Native agents disabled")
420
- else:
421
- # List deployed agent versions at startup
422
- _list_agent_versions_at_startup()
423
-
424
- # Create simple runner
425
- enable_tickets = not args.no_tickets
426
- claude_args = getattr(args, 'claude_args', []) or []
427
- runner = SimpleClaudeRunner(enable_tickets=enable_tickets, log_level=args.logging, claude_args=claude_args)
428
-
429
- # Create basic context
430
- context = create_simple_context()
431
-
432
- # Run session based on mode
433
- if args.non_interactive or args.input:
434
- user_input = _get_user_input(args, logger)
435
- success = runner.run_oneshot(user_input, context)
436
- if not success:
437
- logger.error("Session failed")
438
- else:
439
- # Run interactive session
440
- if getattr(args, 'intercept_commands', False):
441
- # Use the interactive wrapper for command interception
442
- wrapper_path = Path(__file__).parent.parent.parent / "scripts" / "interactive_wrapper.py"
443
- if wrapper_path.exists():
444
- print("Starting interactive session with command interception...")
445
- subprocess.run([sys.executable, str(wrapper_path)])
446
- else:
447
- logger.warning("Interactive wrapper not found, falling back to normal mode")
448
- runner.run_interactive(context)
449
- else:
450
- runner.run_interactive(context)
451
-
452
-
453
- def list_tickets(args):
454
- """List recent tickets."""
455
- logger = get_logger("cli")
456
-
457
- try:
458
- try:
459
- from .services.ticket_manager import TicketManager
460
- except ImportError:
461
- from services.ticket_manager import TicketManager
462
-
463
- ticket_manager = TicketManager()
464
- tickets = ticket_manager.list_recent_tickets(limit=args.limit)
465
-
466
- if not tickets:
467
- print("No tickets found")
468
- return
469
-
470
- print(f"Recent tickets (showing {len(tickets)}):")
471
- print("-" * 80)
472
-
473
- for ticket in tickets:
474
- status_emoji = {
475
- "open": "🔵",
476
- "in_progress": "🟡",
477
- "done": "🟢",
478
- "closed": "⚫"
479
- }.get(ticket['status'], "⚪")
480
-
481
- print(f"{status_emoji} [{ticket['id']}] {ticket['title']}")
482
- print(f" Priority: {ticket['priority']} | Tags: {', '.join(ticket['tags'])}")
483
- print(f" Created: {ticket['created_at']}")
484
- print()
485
-
486
- except ImportError:
487
- logger.error("ai-trackdown-pytools not installed")
488
- print("Error: ai-trackdown-pytools not installed")
489
- print("Install with: pip install ai-trackdown-pytools")
490
- except Exception as e:
491
- logger.error(f"Error listing tickets: {e}")
492
- print(f"Error: {e}")
493
-
494
-
495
- def manage_agents(args):
496
- """Manage Claude Code native agents."""
497
- logger = get_logger("cli")
498
-
499
- try:
500
- from .services.agent_deployment import AgentDeploymentService
501
- deployment_service = AgentDeploymentService()
502
-
503
- if not args.agents_command:
504
- # When no subcommand is provided, display agent versions
505
- # WHY: This provides a quick way for users to check deployed agent versions
506
- # without needing to specify additional subcommands, matching the startup display
507
- agent_versions = _get_agent_versions_display()
508
- if agent_versions:
509
- print(agent_versions)
510
- else:
511
- print("No deployed agents found")
512
- print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
513
- return
514
-
515
- if args.agents_command == AgentCommands.LIST.value:
516
- # Determine what to list
517
- if args.system:
518
- # List available agent templates
519
- print("Available Agent Templates:")
520
- print("-" * 80)
521
- agents = deployment_service.list_available_agents()
522
- if not agents:
523
- print("No agent templates found")
524
- else:
525
- for agent in agents:
526
- print(f"📄 {agent['file']}")
527
- if 'name' in agent:
528
- print(f" Name: {agent['name']}")
529
- if 'description' in agent:
530
- print(f" Description: {agent['description']}")
531
- if 'version' in agent:
532
- print(f" Version: {agent['version']}")
533
- print()
534
-
535
- elif args.deployed:
536
- # List deployed agents
537
- print("Deployed Agents:")
538
- print("-" * 80)
539
- verification = deployment_service.verify_deployment()
540
- if not verification["agents_found"]:
541
- print("No deployed agents found")
542
- else:
543
- for agent in verification["agents_found"]:
544
- print(f"📄 {agent['file']}")
545
- if 'name' in agent:
546
- print(f" Name: {agent['name']}")
547
- print(f" Path: {agent['path']}")
548
- print()
549
-
550
- if verification["warnings"]:
551
- print("\nWarnings:")
552
- for warning in verification["warnings"]:
553
- print(f" ⚠️ {warning}")
554
-
555
- else:
556
- # Default: list both
557
- print("Use --system to list system agents or --deployed to list deployed agents")
558
-
559
- elif args.agents_command == AgentCommands.DEPLOY.value:
560
- # Deploy agents
561
- print("Deploying system agents...")
562
- results = deployment_service.deploy_agents(args.target, force_rebuild=False)
563
-
564
- if results["deployed"]:
565
- print(f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}")
566
- for agent in results["deployed"]:
567
- print(f" - {agent['name']}")
568
-
569
- elif args.agents_command == AgentCommands.FORCE_DEPLOY.value:
570
- # Force deploy agents
571
- print("Force deploying all system agents...")
572
- results = deployment_service.deploy_agents(args.target, force_rebuild=True)
573
-
574
- if results["deployed"]:
575
- print(f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}")
576
- for agent in results["deployed"]:
577
- print(f" - {agent['name']}")
578
-
579
- if results.get("updated", []):
580
- print(f"\n✓ Updated {len(results['updated'])} agents")
581
- for agent in results["updated"]:
582
- print(f" - {agent['name']}")
583
-
584
- if results.get("skipped", []):
585
- print(f"\n✓ Skipped {len(results['skipped'])} up-to-date agents")
586
-
587
- if results["errors"]:
588
- print("\n❌ Errors during deployment:")
589
- for error in results["errors"]:
590
- print(f" - {error}")
591
-
592
- # Set environment
593
- env_vars = deployment_service.set_claude_environment(args.target.parent if args.target else None)
594
- print(f"\n✓ Set Claude environment variables:")
595
- for key, value in env_vars.items():
596
- print(f" - {key}={value}")
597
-
598
- elif args.agents_command == AgentCommands.CLEAN.value:
599
- # Clean deployed agents
600
- print("Cleaning deployed system agents...")
601
- results = deployment_service.clean_deployment(args.target)
602
-
603
- if results["removed"]:
604
- print(f"\n✓ Removed {len(results['removed'])} agents")
605
- for path in results["removed"]:
606
- print(f" - {Path(path).name}")
607
- else:
608
- print("No system agents found to remove")
609
-
610
- if results["errors"]:
611
- print("\n❌ Errors during cleanup:")
612
- for error in results["errors"]:
613
- print(f" - {error}")
614
-
615
- except ImportError:
616
- logger.error("Agent deployment service not available")
617
- print("Error: Agent deployment service not available")
618
- except Exception as e:
619
- logger.error(f"Error managing agents: {e}")
620
- print(f"Error: {e}")
621
-
622
-
623
- def run_terminal_ui(args):
624
- """Run the terminal UI."""
625
- logger = get_logger("cli")
626
-
627
- ui_mode = getattr(args, 'mode', 'terminal')
628
-
629
- try:
630
- if ui_mode == 'terminal':
631
- # Try rich UI first
632
- try:
633
- from .ui.rich_terminal_ui import main as run_rich_ui
634
- logger.info("Starting rich terminal UI...")
635
- run_rich_ui()
636
- except ImportError:
637
- # Fallback to curses UI
638
- logger.info("Rich not available, falling back to curses UI...")
639
- from .ui.terminal_ui import TerminalUI
640
- ui = TerminalUI()
641
- ui.run()
642
- else:
643
- # Use curses UI
644
- from .ui.terminal_ui import TerminalUI
645
- ui = TerminalUI()
646
- ui.run()
647
- except ImportError as e:
648
- logger.error(f"UI module not found: {e}")
649
- print(f"Error: Terminal UI requires 'curses' (built-in) or 'rich' (pip install rich)")
650
- return 1
651
- except Exception as e:
652
- logger.error(f"Error running terminal UI: {e}")
653
- print(f"Error: {e}")
654
- return 1
655
-
656
- return 0
657
-
658
-
659
- def show_info(args):
660
- """Show framework and configuration information."""
661
- try:
662
- from .core.framework_loader import FrameworkLoader
663
- except ImportError:
664
- from core.framework_loader import FrameworkLoader
665
-
666
- print("Claude MPM - Multi-Agent Project Manager")
667
- print("=" * 50)
668
-
669
- # Framework info
670
- loader = FrameworkLoader(args.framework_path)
671
- if loader.framework_content["loaded"]:
672
- print(f"Framework: claude-multiagent-pm")
673
- print(f"Version: {loader.framework_content['version']}")
674
- print(f"Path: {loader.framework_path}")
675
- print(f"Agents: {', '.join(loader.get_agent_list())}")
676
- else:
677
- print("Framework: Not found (using minimal instructions)")
678
-
679
- print()
680
-
681
- # Configuration
682
- print("Configuration:")
683
- print(f" Log directory: {args.log_dir or '~/.claude-mpm/logs'}")
684
- print(f" Debug mode: {args.debug}")
685
-
686
- # Show agent hierarchy
687
- if loader.agent_registry:
688
- hierarchy = loader.agent_registry.get_agent_hierarchy()
689
- print("\nAgent Hierarchy:")
690
- print(f" Project agents: {len(hierarchy['project'])}")
691
- print(f" User agents: {len(hierarchy['user'])}")
692
- print(f" System agents: {len(hierarchy['system'])}")
693
-
694
- # Show core agents
695
- core_agents = loader.agent_registry.get_core_agents()
696
- print(f"\nCore Agents: {', '.join(core_agents)}")
697
-
698
- # Check dependencies
699
- print("\nDependencies:")
700
-
701
- # Check Claude
702
- import shutil
703
- claude_path = shutil.which("claude")
704
- if claude_path:
705
- print(f" ✓ Claude CLI: {claude_path}")
706
- else:
707
- print(" ✗ Claude CLI: Not found in PATH")
708
-
709
- # Check ai-trackdown-pytools
710
- try:
711
- import ai_trackdown_pytools
712
- print(" ✓ ai-trackdown-pytools: Installed")
713
- except ImportError:
714
- print(" ✗ ai-trackdown-pytools: Not installed")
715
-
716
- # Check Claude Code hooks
717
- from pathlib import Path
718
- claude_settings = Path.home() / ".claude" / "settings.json"
719
- if claude_settings.exists():
720
- print(" ✓ Claude Code Hooks: Installed")
721
- print(" Use /mpm commands in Claude Code")
722
- else:
723
- print(" ✗ Claude Code Hooks: Not installed")
724
- print(" Run: python scripts/install_hooks.py")
725
-
726
-
727
- if __name__ == "__main__":
728
- sys.exit(main())