claude-mpm 3.1.1__py3-none-any.whl → 3.1.3__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 (47) hide show
  1. claude_mpm/__main__.py +27 -3
  2. claude_mpm/agents/INSTRUCTIONS.md +17 -2
  3. claude_mpm/agents/templates/test-integration-agent.md +34 -0
  4. claude_mpm/cli/README.md +109 -0
  5. claude_mpm/cli/__init__.py +172 -0
  6. claude_mpm/cli/commands/__init__.py +20 -0
  7. claude_mpm/cli/commands/agents.py +202 -0
  8. claude_mpm/cli/commands/info.py +94 -0
  9. claude_mpm/cli/commands/run.py +95 -0
  10. claude_mpm/cli/commands/tickets.py +70 -0
  11. claude_mpm/cli/commands/ui.py +79 -0
  12. claude_mpm/cli/parser.py +337 -0
  13. claude_mpm/cli/utils.py +190 -0
  14. claude_mpm/cli_enhancements.py +19 -0
  15. claude_mpm/core/agent_registry.py +4 -4
  16. claude_mpm/core/factories.py +1 -1
  17. claude_mpm/core/service_registry.py +1 -1
  18. claude_mpm/core/simple_runner.py +17 -27
  19. claude_mpm/hooks/claude_hooks/hook_handler.py +53 -4
  20. claude_mpm/models/__init__.py +106 -0
  21. claude_mpm/models/agent_definition.py +196 -0
  22. claude_mpm/models/common.py +41 -0
  23. claude_mpm/models/lifecycle.py +97 -0
  24. claude_mpm/models/modification.py +126 -0
  25. claude_mpm/models/persistence.py +57 -0
  26. claude_mpm/models/registry.py +91 -0
  27. claude_mpm/security/__init__.py +8 -0
  28. claude_mpm/security/bash_validator.py +393 -0
  29. claude_mpm/services/agent_lifecycle_manager.py +206 -94
  30. claude_mpm/services/agent_modification_tracker.py +27 -100
  31. claude_mpm/services/agent_persistence_service.py +74 -0
  32. claude_mpm/services/agent_registry.py +43 -82
  33. claude_mpm/services/agent_versioning.py +37 -0
  34. claude_mpm/services/{ticketing_service_original.py → legacy_ticketing_service.py} +16 -9
  35. claude_mpm/services/ticket_manager.py +5 -4
  36. claude_mpm/services/{ticket_manager_di.py → ticket_manager_dependency_injection.py} +39 -12
  37. claude_mpm/services/version_control/semantic_versioning.py +10 -9
  38. claude_mpm/utils/path_operations.py +20 -0
  39. {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/METADATA +9 -1
  40. {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/RECORD +45 -25
  41. claude_mpm/cli_main.py +0 -13
  42. claude_mpm/utils/import_migration_example.py +0 -80
  43. /claude_mpm/{cli.py → cli_old.py} +0 -0
  44. {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/WHEEL +0 -0
  45. {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/entry_points.txt +0 -0
  46. {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/licenses/LICENSE +0 -0
  47. {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/top_level.txt +0 -0
claude_mpm/__main__.py CHANGED
@@ -1,14 +1,38 @@
1
- """Main entry point for claude-mpm."""
1
+ """
2
+ Main entry point for claude-mpm package.
3
+
4
+ WHY: This module enables running claude-mpm as a Python module via `python -m claude_mpm`.
5
+ It's the standard Python pattern for making packages executable.
6
+
7
+ DESIGN DECISION: We only import and call the main function from the CLI module,
8
+ keeping this file minimal and focused on its single responsibility.
9
+ """
2
10
 
3
- import sys
4
11
  import os
12
+ import sys
5
13
  from pathlib import Path
6
14
 
7
15
  # Add parent directory to path to ensure proper imports
8
16
  sys.path.insert(0, str(Path(__file__).parent.parent))
9
17
 
10
- # Import main function from cli
18
+ # Import main function from the new CLI module structure
11
19
  from claude_mpm.cli import main
12
20
 
21
+ # Restore user's working directory if preserved by bash wrapper
22
+ # WHY: The bash wrapper preserves the user's launch directory in CLAUDE_MPM_USER_PWD
23
+ # to maintain proper file access permissions and security boundaries.
24
+ # Python imports work via PYTHONPATH, so we can safely restore the original directory.
13
25
  if __name__ == "__main__":
26
+ user_pwd = os.environ.get('CLAUDE_MPM_USER_PWD')
27
+ if user_pwd and os.path.exists(user_pwd):
28
+ try:
29
+ os.chdir(user_pwd)
30
+ # Only log if debug is enabled
31
+ if os.environ.get('CLAUDE_MPM_DEBUG') == '1':
32
+ print(f"[INFO] Restored working directory to: {user_pwd}")
33
+ except Exception as e:
34
+ # If we can't change to user directory, continue but log warning
35
+ if os.environ.get('CLAUDE_MPM_DEBUG') == '1':
36
+ print(f"[WARNING] Could not restore working directory to {user_pwd}: {e}")
37
+
14
38
  sys.exit(main())
@@ -4,15 +4,30 @@
4
4
  # Claude Multi-Agent Project Manager Instructions
5
5
 
6
6
  ## Core Identity & Authority
7
- You are **Claude Multi-Agent Project Manager (claude-mpm)** - your **SOLE function** is **orchestration and delegation**. You are **FORBIDDEN** from direct work except:
7
+ You are **Claude Multi-Agent Project Manager (claude-mpm)** - your **SOLE function** is **orchestration and delegation**.
8
+
9
+ ### Permitted Actions (Exhaustive List)
8
10
  - **Task Tool** for delegation (primary function)
9
11
  - **TodoWrite** for tracking (with [Agent] prefixes)
10
12
  - **WebSearch/WebFetch** only for delegation requirements
11
13
  - **Direct answers** for PM role/capability questions only
12
- - **Direct work** only when explicitly authorized: "do this yourself", "don't delegate", "implement directly"
14
+ - **Direct work** only when explicitly authorized with phrases like: "do this yourself", "don't delegate", "implement directly"
13
15
 
14
16
  **ABSOLUTE RULE**: ALL other work must be delegated to specialized agents via Task Tool.
15
17
 
18
+ ### Professional Standards
19
+ - **Clarity Threshold**: Achieve 90% certainty of success before delegating. Ask clarifying questions when needed.
20
+ - **Agent Standards**: Require the same 90% clarity threshold from your agents before they proceed.
21
+ - **Project Validation**: Verify that requested tasks align with project context and question any incongruities.
22
+ - **Communication Style**:
23
+ - Avoid unnecessary affirmation or praise
24
+ - Never say "You're right!" (or similar) unless correctness was in question and validated by agent authority
25
+ - Focus on substance over pleasantries
26
+ - Ask direct questions when clarity is needed
27
+
28
+ ### Your Responsibility
29
+ It is your **JOB** to ensure sufficient clarity before delegation. Set high standards for both yourself and your agents to ensure successful outcomes.
30
+
16
31
  ## Context-Aware Agent Selection
17
32
  - **PM role/capabilities questions**: Answer directly (only exception)
18
33
  - **Explanations/How-to questions**: Delegate to Documentation Agent
@@ -0,0 +1,34 @@
1
+ ---
2
+ author: test-script
3
+ model_preference: claude-3-sonnet
4
+ specializations: []
5
+ tags:
6
+ - test
7
+ - integration
8
+ type: custom
9
+ version: 1.0.0
10
+ ---
11
+
12
+ # Test Integration Agent
13
+
14
+ ## 🎯 Primary Role
15
+ test-integration-agent agent
16
+
17
+ ## 🎯 When to Use This Agent
18
+
19
+ **Select this agent when:**
20
+
21
+ **Do NOT select for:**
22
+
23
+ ## 🔧 Core Capabilities
24
+
25
+ ## 🔑 Authority & Permissions
26
+
27
+ ### ✅ Exclusive Write Access
28
+
29
+ ### ❌ Forbidden Operations
30
+
31
+ ---
32
+ **Agent Type**: custom
33
+ **Model Preference**: claude-3-sonnet
34
+ **Version**: 1.0.0
@@ -0,0 +1,109 @@
1
+ # Claude MPM CLI Architecture
2
+
3
+ This document describes the refactored CLI architecture for claude-mpm.
4
+
5
+ ## Overview
6
+
7
+ The CLI has been refactored from a single monolithic `cli.py` file into a modular structure that improves maintainability and code organization.
8
+
9
+ ## Directory Structure
10
+
11
+ ```
12
+ cli/
13
+ ├── __init__.py # Main entry point - orchestrates the CLI flow
14
+ ├── parser.py # Argument parsing logic - single source of truth for CLI arguments
15
+ ├── utils.py # Shared utility functions
16
+ ├── commands/ # Individual command implementations
17
+ │ ├── __init__.py
18
+ │ ├── run.py # Default command - runs Claude sessions
19
+ │ ├── tickets.py # Lists tickets
20
+ │ ├── info.py # Shows system information
21
+ │ ├── agents.py # Manages agent deployments
22
+ │ └── ui.py # Terminal UI launcher
23
+ └── README.md # This file
24
+ ```
25
+
26
+ ## Key Design Decisions
27
+
28
+ ### 1. Modular Command Structure
29
+ Each command is implemented in its own module under `commands/`. This makes it easy to:
30
+ - Add new commands without touching existing code
31
+ - Test commands in isolation
32
+ - Understand what each command does
33
+
34
+ ### 2. Centralized Argument Parsing
35
+ All argument definitions are in `parser.py`. This provides:
36
+ - Single source of truth for CLI arguments
37
+ - Reusable argument groups (common arguments, run arguments)
38
+ - Clear separation of parsing from execution
39
+
40
+ ### 3. Shared Utilities
41
+ Common functions are in `utils.py`:
42
+ - `get_user_input()` - Handles input from files, stdin, or command line
43
+ - `get_agent_versions_display()` - Formats agent version information
44
+ - `setup_logging()` - Configures logging based on arguments
45
+ - `ensure_directories()` - Creates required directories on first run
46
+
47
+ ### 4. Backward Compatibility
48
+ The refactoring maintains full backward compatibility:
49
+ - `__main__.py` still imports from `claude_mpm.cli`
50
+ - The main `cli/__init__.py` exports the same `main()` function
51
+ - All existing commands and arguments work exactly as before
52
+
53
+ ## Entry Points
54
+
55
+ 1. **Package execution**: `python -m claude_mpm`
56
+ - Uses `__main__.py` which imports from `cli/__init__.py`
57
+
58
+ 2. **Direct import**: `from claude_mpm.cli import main`
59
+ - Imports the main function from `cli/__init__.py`
60
+
61
+ 3. **Shell script**: `claude-mpm` command
62
+ - Calls `python -m claude_mpm` with proper environment setup
63
+
64
+ ## Adding New Commands
65
+
66
+ To add a new command:
67
+
68
+ 1. Create a new module in `commands/`:
69
+ ```python
70
+ # commands/mycommand.py
71
+ def my_command(args):
72
+ """Execute my command."""
73
+ # Implementation here
74
+ ```
75
+
76
+ 2. Add the command to `commands/__init__.py`:
77
+ ```python
78
+ from .mycommand import my_command
79
+ ```
80
+
81
+ 3. Add parser configuration in `parser.py`:
82
+ ```python
83
+ # In create_parser()
84
+ mycommand_parser = subparsers.add_parser(
85
+ "mycommand",
86
+ help="Description of my command"
87
+ )
88
+ # Add command-specific arguments
89
+ ```
90
+
91
+ 4. Add the command mapping in `cli/__init__.py`:
92
+ ```python
93
+ # In _execute_command()
94
+ command_map = {
95
+ # ... existing commands ...
96
+ "mycommand": my_command,
97
+ }
98
+ ```
99
+
100
+ ## Removed Files
101
+
102
+ - `cli_main.py` - Redundant entry point, functionality moved to `__main__.py`
103
+ - Original `cli.py` - Split into the modular structure described above
104
+
105
+ ## Preserved Files
106
+
107
+ - `cli_enhancements.py` - Experimental Click-based CLI with enhanced features
108
+ - Kept for reference and future enhancement ideas
109
+ - Not currently used in production
@@ -0,0 +1,172 @@
1
+ """
2
+ Claude MPM Command-Line Interface.
3
+
4
+ WHY: This module serves as the main entry point for the CLI, coordinating
5
+ argument parsing and command execution. It replaces the monolithic cli.py
6
+ with a more modular structure.
7
+
8
+ DESIGN DECISION: We maintain backward compatibility by keeping the same
9
+ interface while organizing code into logical modules. The main() function
10
+ remains the primary entry point for both direct execution and package imports.
11
+ """
12
+
13
+ import os
14
+ import sys
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ from claude_mpm.utils.imports import safe_import
19
+
20
+ # Import constants and utilities using safe_import pattern
21
+ CLICommands, LogLevel = safe_import('claude_mpm.constants', None, ['CLICommands', 'LogLevel'])
22
+ create_parser, preprocess_args = safe_import('claude_mpm.cli.parser', None, ['create_parser', 'preprocess_args'])
23
+ ensure_directories, setup_logging = safe_import('claude_mpm.cli.utils', None, ['ensure_directories', 'setup_logging'])
24
+
25
+ # Import commands
26
+ run_session = safe_import('claude_mpm.cli.commands', None, ['run_session'])
27
+ list_tickets = safe_import('claude_mpm.cli.commands', None, ['list_tickets'])
28
+ show_info = safe_import('claude_mpm.cli.commands', None, ['show_info'])
29
+ manage_agents = safe_import('claude_mpm.cli.commands', None, ['manage_agents'])
30
+ run_terminal_ui = safe_import('claude_mpm.cli.commands', None, ['run_terminal_ui'])
31
+
32
+ # Get version from VERSION file - single source of truth
33
+ version_file = Path(__file__).parent.parent.parent / "VERSION"
34
+ if version_file.exists():
35
+ __version__ = version_file.read_text().strip()
36
+ else:
37
+ # Try to import from package as fallback using safe_import
38
+ version_module = safe_import('claude_mpm', None)
39
+ if version_module and hasattr(version_module, '__version__'):
40
+ __version__ = version_module.__version__
41
+ else:
42
+ # Default version if all else fails
43
+ __version__ = "0.0.0"
44
+
45
+
46
+ def main(argv: Optional[list] = None):
47
+ """
48
+ Main CLI entry point.
49
+
50
+ WHY: This function orchestrates the entire CLI flow:
51
+ 1. Ensures directories exist
52
+ 2. Preprocesses arguments (handling --mpm: prefix)
53
+ 3. Parses arguments
54
+ 4. Sets up logging
55
+ 5. Executes the appropriate command
56
+
57
+ DESIGN DECISION: We keep error handling at this level to provide consistent
58
+ error messages and exit codes across all commands.
59
+
60
+ Args:
61
+ argv: Optional list of command line arguments for testing
62
+
63
+ Returns:
64
+ Exit code (0 for success, non-zero for errors)
65
+ """
66
+ # Ensure directories are initialized on first run
67
+ ensure_directories()
68
+
69
+ # Create parser with version
70
+ parser = create_parser(version=__version__)
71
+
72
+ # Preprocess and parse arguments
73
+ processed_argv = preprocess_args(argv)
74
+ args = parser.parse_args(processed_argv)
75
+
76
+ # Set up logging
77
+ logger = setup_logging(args)
78
+
79
+ # Debug output if requested
80
+ if hasattr(args, 'debug') and args.debug:
81
+ logger.debug(f"Command: {args.command}")
82
+ logger.debug(f"Arguments: {args}")
83
+ # Log working directory context
84
+ logger.debug(f"Working directory: {os.getcwd()}")
85
+ logger.debug(f"User PWD (from env): {os.environ.get('CLAUDE_MPM_USER_PWD', 'Not set')}")
86
+ logger.debug(f"Framework path: {os.environ.get('CLAUDE_MPM_FRAMEWORK_PATH', 'Not set')}")
87
+
88
+ # Hook system note: Claude Code hooks are handled externally via the
89
+ # hook_handler.py script installed in ~/.claude/settings.json
90
+ # The --no-hooks flag is kept for backward compatibility but doesn't affect
91
+ # Claude Code hooks which are configured separately.
92
+
93
+ # Default to run command if no command specified
94
+ if not args.command:
95
+ args.command = CLICommands.RUN.value
96
+ # Ensure run-specific attributes exist when defaulting to run
97
+ _ensure_run_attributes(args)
98
+
99
+ # Execute command
100
+ try:
101
+ exit_code = _execute_command(args.command, args)
102
+ return exit_code
103
+ except KeyboardInterrupt:
104
+ logger.info("Session interrupted by user")
105
+ return 0
106
+ except Exception as e:
107
+ logger.error(f"Error: {e}")
108
+ if args.debug:
109
+ import traceback
110
+ traceback.print_exc()
111
+ return 1
112
+
113
+
114
+ def _ensure_run_attributes(args):
115
+ """
116
+ Ensure run command attributes exist when defaulting to run.
117
+
118
+ WHY: When no command is specified, we default to 'run' but the args object
119
+ won't have run-specific attributes from the subparser. This function ensures
120
+ they exist with sensible defaults.
121
+
122
+ Args:
123
+ args: Parsed arguments object to update
124
+ """
125
+ # Set defaults for run command attributes
126
+ args.no_tickets = getattr(args, 'no_tickets', False)
127
+ args.no_hooks = getattr(args, 'no_hooks', False)
128
+ args.intercept_commands = getattr(args, 'intercept_commands', False)
129
+ args.input = getattr(args, 'input', None)
130
+ args.non_interactive = getattr(args, 'non_interactive', False)
131
+ args.no_native_agents = getattr(args, 'no_native_agents', False)
132
+ args.claude_args = getattr(args, 'claude_args', [])
133
+
134
+
135
+ def _execute_command(command: str, args) -> int:
136
+ """
137
+ Execute the specified command.
138
+
139
+ WHY: This function maps command names to their implementations, providing
140
+ a single place to manage command routing.
141
+
142
+ Args:
143
+ command: The command name to execute
144
+ args: Parsed command line arguments
145
+
146
+ Returns:
147
+ Exit code from the command
148
+ """
149
+ # Map commands to their implementations
150
+ command_map = {
151
+ CLICommands.RUN.value: run_session,
152
+ CLICommands.TICKETS.value: list_tickets,
153
+ CLICommands.INFO.value: show_info,
154
+ CLICommands.AGENTS.value: manage_agents,
155
+ CLICommands.UI.value: run_terminal_ui,
156
+ }
157
+
158
+ # Execute command if found
159
+ if command in command_map:
160
+ result = command_map[command](args)
161
+ # Commands may return None (success) or an exit code
162
+ return result if result is not None else 0
163
+ else:
164
+ # Unknown command - this shouldn't happen with argparse
165
+ # but we handle it for completeness
166
+ print(f"Unknown command: {command}")
167
+ return 1
168
+
169
+
170
+ # For backward compatibility - export main
171
+ if __name__ == "__main__":
172
+ sys.exit(main())
@@ -0,0 +1,20 @@
1
+ """
2
+ CLI commands for claude-mpm.
3
+
4
+ WHY: This package contains individual command implementations, organized into
5
+ separate modules for better maintainability and code organization.
6
+ """
7
+
8
+ from .run import run_session
9
+ from .tickets import list_tickets
10
+ from .info import show_info
11
+ from .agents import manage_agents
12
+ from .ui import run_terminal_ui
13
+
14
+ __all__ = [
15
+ 'run_session',
16
+ 'list_tickets',
17
+ 'show_info',
18
+ 'manage_agents',
19
+ 'run_terminal_ui'
20
+ ]
@@ -0,0 +1,202 @@
1
+ """
2
+ Agents command implementation for claude-mpm.
3
+
4
+ WHY: This module manages Claude Code native agents, including listing, deploying,
5
+ and cleaning agent deployments.
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+ from claude_mpm.utils.imports import safe_import
11
+
12
+ # Import dependencies using safe_import pattern
13
+ get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
14
+ AgentCommands = safe_import('claude_mpm.constants', None, ['AgentCommands'])
15
+ get_agent_versions_display = safe_import('claude_mpm.cli.utils', None, ['get_agent_versions_display'])
16
+
17
+
18
+ def manage_agents(args):
19
+ """
20
+ Manage Claude Code native agents.
21
+
22
+ WHY: Claude Code agents need to be deployed and managed. This command provides
23
+ a unified interface for all agent-related operations.
24
+
25
+ DESIGN DECISION: When no subcommand is provided, we show the current agent
26
+ versions as a quick status check. This matches the behavior users see at startup.
27
+
28
+ Args:
29
+ args: Parsed command line arguments with agents_command attribute
30
+ """
31
+ logger = get_logger("cli")
32
+
33
+ # Import AgentDeploymentService using safe_import pattern
34
+ AgentDeploymentService = safe_import(
35
+ 'claude_mpm.services.agent_deployment',
36
+ None,
37
+ ['AgentDeploymentService']
38
+ )
39
+
40
+ if not AgentDeploymentService:
41
+ logger.error("Agent deployment service not available")
42
+ print("Error: Agent deployment service not available")
43
+ return
44
+
45
+ try:
46
+ deployment_service = AgentDeploymentService()
47
+
48
+ if not args.agents_command:
49
+ # No subcommand - show agent versions
50
+ # WHY: This provides a quick way for users to check deployed agent versions
51
+ # without needing to specify additional subcommands
52
+ agent_versions = get_agent_versions_display()
53
+ if agent_versions:
54
+ print(agent_versions)
55
+ else:
56
+ print("No deployed agents found")
57
+ print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
58
+ return
59
+
60
+ if args.agents_command == AgentCommands.LIST.value:
61
+ _list_agents(args, deployment_service)
62
+
63
+ elif args.agents_command == AgentCommands.DEPLOY.value:
64
+ _deploy_agents(args, deployment_service, force=False)
65
+
66
+ elif args.agents_command == AgentCommands.FORCE_DEPLOY.value:
67
+ _deploy_agents(args, deployment_service, force=True)
68
+
69
+ elif args.agents_command == AgentCommands.CLEAN.value:
70
+ _clean_agents(args, deployment_service)
71
+
72
+ except Exception as e:
73
+ logger.error(f"Error managing agents: {e}")
74
+ print(f"Error: {e}")
75
+
76
+
77
+ def _list_agents(args, deployment_service):
78
+ """
79
+ List available or deployed agents.
80
+
81
+ WHY: Users need to see what agents are available in the system and what's
82
+ currently deployed. This helps them understand the agent ecosystem.
83
+
84
+ Args:
85
+ args: Command arguments with 'system' and 'deployed' flags
86
+ deployment_service: Agent deployment service instance
87
+ """
88
+ if args.system:
89
+ # List available agent templates
90
+ print("Available Agent Templates:")
91
+ print("-" * 80)
92
+ agents = deployment_service.list_available_agents()
93
+ if not agents:
94
+ print("No agent templates found")
95
+ else:
96
+ for agent in agents:
97
+ print(f"📄 {agent['file']}")
98
+ if 'name' in agent:
99
+ print(f" Name: {agent['name']}")
100
+ if 'description' in agent:
101
+ print(f" Description: {agent['description']}")
102
+ if 'version' in agent:
103
+ print(f" Version: {agent['version']}")
104
+ print()
105
+
106
+ elif args.deployed:
107
+ # List deployed agents
108
+ print("Deployed Agents:")
109
+ print("-" * 80)
110
+ verification = deployment_service.verify_deployment()
111
+ if not verification["agents_found"]:
112
+ print("No deployed agents found")
113
+ else:
114
+ for agent in verification["agents_found"]:
115
+ print(f"📄 {agent['file']}")
116
+ if 'name' in agent:
117
+ print(f" Name: {agent['name']}")
118
+ print(f" Path: {agent['path']}")
119
+ print()
120
+
121
+ if verification["warnings"]:
122
+ print("\nWarnings:")
123
+ for warning in verification["warnings"]:
124
+ print(f" ⚠️ {warning}")
125
+
126
+ else:
127
+ # Default: show usage
128
+ print("Use --system to list system agents or --deployed to list deployed agents")
129
+
130
+
131
+ def _deploy_agents(args, deployment_service, force=False):
132
+ """
133
+ Deploy system agents.
134
+
135
+ WHY: Agents need to be deployed to the working directory for Claude Code to use them.
136
+ This function handles both regular and forced deployment.
137
+
138
+ Args:
139
+ args: Command arguments with optional 'target' path
140
+ deployment_service: Agent deployment service instance
141
+ force: Whether to force rebuild all agents
142
+ """
143
+ if force:
144
+ print("Force deploying all system agents...")
145
+ else:
146
+ print("Deploying system agents...")
147
+
148
+ results = deployment_service.deploy_agents(args.target, force_rebuild=force)
149
+
150
+ if results["deployed"]:
151
+ print(f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}")
152
+ for agent in results["deployed"]:
153
+ print(f" - {agent['name']}")
154
+
155
+ if force and results.get("updated", []):
156
+ print(f"\n✓ Updated {len(results['updated'])} agents")
157
+ for agent in results["updated"]:
158
+ print(f" - {agent['name']}")
159
+
160
+ if force and results.get("skipped", []):
161
+ print(f"\n✓ Skipped {len(results['skipped'])} up-to-date agents")
162
+
163
+ if results["errors"]:
164
+ print("\n❌ Errors during deployment:")
165
+ for error in results["errors"]:
166
+ print(f" - {error}")
167
+
168
+ if force:
169
+ # Set environment for force deploy
170
+ env_vars = deployment_service.set_claude_environment(
171
+ args.target.parent if args.target else None
172
+ )
173
+ print(f"\n✓ Set Claude environment variables:")
174
+ for key, value in env_vars.items():
175
+ print(f" - {key}={value}")
176
+
177
+
178
+ def _clean_agents(args, deployment_service):
179
+ """
180
+ Clean deployed system agents.
181
+
182
+ WHY: Users may want to remove deployed agents to start fresh or clean up
183
+ their working directory.
184
+
185
+ Args:
186
+ args: Command arguments with optional 'target' path
187
+ deployment_service: Agent deployment service instance
188
+ """
189
+ print("Cleaning deployed system agents...")
190
+ results = deployment_service.clean_deployment(args.target)
191
+
192
+ if results["removed"]:
193
+ print(f"\n✓ Removed {len(results['removed'])} agents")
194
+ for path in results["removed"]:
195
+ print(f" - {Path(path).name}")
196
+ else:
197
+ print("No system agents found to remove")
198
+
199
+ if results["errors"]:
200
+ print("\n❌ Errors during cleanup:")
201
+ for error in results["errors"]:
202
+ print(f" - {error}")