claude-mpm 0.3.0__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/__init__.py +17 -0
- claude_mpm/__main__.py +14 -0
- claude_mpm/_version.py +32 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
- claude_mpm/agents/INSTRUCTIONS.md +375 -0
- claude_mpm/agents/__init__.py +118 -0
- claude_mpm/agents/agent_loader.py +621 -0
- claude_mpm/agents/agent_loader_integration.py +229 -0
- claude_mpm/agents/agents_metadata.py +204 -0
- claude_mpm/agents/base_agent.json +27 -0
- claude_mpm/agents/base_agent_loader.py +519 -0
- claude_mpm/agents/schema/agent_schema.json +160 -0
- claude_mpm/agents/system_agent_config.py +587 -0
- claude_mpm/agents/templates/__init__.py +101 -0
- claude_mpm/agents/templates/data_engineer_agent.json +46 -0
- claude_mpm/agents/templates/documentation_agent.json +45 -0
- claude_mpm/agents/templates/engineer_agent.json +49 -0
- claude_mpm/agents/templates/ops_agent.json +46 -0
- claude_mpm/agents/templates/qa_agent.json +45 -0
- claude_mpm/agents/templates/research_agent.json +49 -0
- claude_mpm/agents/templates/security_agent.json +46 -0
- claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
- claude_mpm/agents/templates/version_control_agent.json +46 -0
- claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
- claude_mpm/cli.py +655 -0
- claude_mpm/cli_main.py +13 -0
- claude_mpm/cli_module/__init__.py +15 -0
- claude_mpm/cli_module/args.py +222 -0
- claude_mpm/cli_module/commands.py +203 -0
- claude_mpm/cli_module/migration_example.py +183 -0
- claude_mpm/cli_module/refactoring_guide.md +253 -0
- claude_mpm/cli_old/__init__.py +1 -0
- claude_mpm/cli_old/ticket_cli.py +102 -0
- claude_mpm/config/__init__.py +5 -0
- claude_mpm/config/hook_config.py +42 -0
- claude_mpm/constants.py +150 -0
- claude_mpm/core/__init__.py +45 -0
- claude_mpm/core/agent_name_normalizer.py +248 -0
- claude_mpm/core/agent_registry.py +627 -0
- claude_mpm/core/agent_registry.py.bak +312 -0
- claude_mpm/core/agent_session_manager.py +273 -0
- claude_mpm/core/base_service.py +747 -0
- claude_mpm/core/base_service.py.bak +406 -0
- claude_mpm/core/config.py +334 -0
- claude_mpm/core/config_aliases.py +292 -0
- claude_mpm/core/container.py +347 -0
- claude_mpm/core/factories.py +281 -0
- claude_mpm/core/framework_loader.py +472 -0
- claude_mpm/core/injectable_service.py +206 -0
- claude_mpm/core/interfaces.py +539 -0
- claude_mpm/core/logger.py +468 -0
- claude_mpm/core/minimal_framework_loader.py +107 -0
- claude_mpm/core/mixins.py +150 -0
- claude_mpm/core/service_registry.py +299 -0
- claude_mpm/core/session_manager.py +190 -0
- claude_mpm/core/simple_runner.py +511 -0
- claude_mpm/core/tool_access_control.py +173 -0
- claude_mpm/hooks/README.md +243 -0
- claude_mpm/hooks/__init__.py +5 -0
- claude_mpm/hooks/base_hook.py +154 -0
- claude_mpm/hooks/builtin/__init__.py +1 -0
- claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
- claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
- claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
- claude_mpm/hooks/hook_client.py +264 -0
- claude_mpm/hooks/hook_runner.py +370 -0
- claude_mpm/hooks/json_rpc_executor.py +259 -0
- claude_mpm/hooks/json_rpc_hook_client.py +319 -0
- claude_mpm/hooks/tool_call_interceptor.py +204 -0
- claude_mpm/init.py +246 -0
- claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
- claude_mpm/orchestration/__init__.py +6 -0
- claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
- claude_mpm/orchestration/archive/factory.py +215 -0
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
- claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
- claude_mpm/orchestration/archive/orchestrator.py +501 -0
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
- claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
- claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
- claude_mpm/scripts/__init__.py +1 -0
- claude_mpm/scripts/ticket.py +269 -0
- claude_mpm/services/__init__.py +10 -0
- claude_mpm/services/agent_deployment.py +955 -0
- claude_mpm/services/agent_lifecycle_manager.py +948 -0
- claude_mpm/services/agent_management_service.py +596 -0
- claude_mpm/services/agent_modification_tracker.py +841 -0
- claude_mpm/services/agent_profile_loader.py +606 -0
- claude_mpm/services/agent_registry.py +677 -0
- claude_mpm/services/base_agent_manager.py +380 -0
- claude_mpm/services/framework_agent_loader.py +337 -0
- claude_mpm/services/framework_claude_md_generator/README.md +92 -0
- claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
- claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
- claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
- claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
- claude_mpm/services/framework_claude_md_generator.py +621 -0
- claude_mpm/services/hook_service.py +388 -0
- claude_mpm/services/hook_service_manager.py +223 -0
- claude_mpm/services/json_rpc_hook_manager.py +92 -0
- claude_mpm/services/parent_directory_manager/README.md +83 -0
- claude_mpm/services/parent_directory_manager/__init__.py +577 -0
- claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
- claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
- claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
- claude_mpm/services/parent_directory_manager/operations.py +186 -0
- claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
- claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
- claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
- claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
- claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
- claude_mpm/services/shared_prompt_cache.py +819 -0
- claude_mpm/services/ticket_manager.py +213 -0
- claude_mpm/services/ticket_manager_di.py +318 -0
- claude_mpm/services/ticketing_service_original.py +508 -0
- claude_mpm/services/version_control/VERSION +1 -0
- claude_mpm/services/version_control/__init__.py +70 -0
- claude_mpm/services/version_control/branch_strategy.py +670 -0
- claude_mpm/services/version_control/conflict_resolution.py +744 -0
- claude_mpm/services/version_control/git_operations.py +784 -0
- claude_mpm/services/version_control/semantic_versioning.py +703 -0
- claude_mpm/ui/__init__.py +1 -0
- claude_mpm/ui/rich_terminal_ui.py +295 -0
- claude_mpm/ui/terminal_ui.py +328 -0
- claude_mpm/utils/__init__.py +16 -0
- claude_mpm/utils/config_manager.py +468 -0
- claude_mpm/utils/import_migration_example.py +80 -0
- claude_mpm/utils/imports.py +182 -0
- claude_mpm/utils/path_operations.py +357 -0
- claude_mpm/utils/paths.py +289 -0
- claude_mpm-0.3.0.dist-info/METADATA +290 -0
- claude_mpm-0.3.0.dist-info/RECORD +159 -0
- claude_mpm-0.3.0.dist-info/WHEEL +5 -0
- claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
- claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
claude_mpm/cli.py
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
"""Command-line interface for Claude MPM."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
# Try relative imports first (when used as package)
|
|
10
|
+
from ._version import __version__
|
|
11
|
+
from .core.logger import get_logger, setup_logging
|
|
12
|
+
from .services.json_rpc_hook_manager import JSONRPCHookManager
|
|
13
|
+
from .constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
|
|
14
|
+
except ImportError:
|
|
15
|
+
# Fall back to absolute imports (when run directly)
|
|
16
|
+
from claude_mpm._version import __version__
|
|
17
|
+
from core.logger import get_logger, setup_logging
|
|
18
|
+
from services.json_rpc_hook_manager import JSONRPCHookManager
|
|
19
|
+
from constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _preprocess_args(argv: Optional[list] = None) -> list:
|
|
24
|
+
"""Preprocess arguments to handle --mpm: prefix commands."""
|
|
25
|
+
if argv is None:
|
|
26
|
+
argv = sys.argv[1:]
|
|
27
|
+
|
|
28
|
+
# Convert --mpm:command to command for argparse compatibility
|
|
29
|
+
processed_args = []
|
|
30
|
+
for i, arg in enumerate(argv):
|
|
31
|
+
if arg.startswith(CLIPrefix.MPM.value):
|
|
32
|
+
# Extract command after prefix
|
|
33
|
+
command = arg[len(CLIPrefix.MPM.value):]
|
|
34
|
+
processed_args.append(command)
|
|
35
|
+
else:
|
|
36
|
+
processed_args.append(arg)
|
|
37
|
+
|
|
38
|
+
return processed_args
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main(argv: Optional[list] = None):
|
|
42
|
+
"""Main CLI entry point."""
|
|
43
|
+
# Ensure directories are initialized on first run
|
|
44
|
+
try:
|
|
45
|
+
from .init import ensure_directories
|
|
46
|
+
ensure_directories()
|
|
47
|
+
except Exception:
|
|
48
|
+
# Continue even if initialization fails
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
parser = argparse.ArgumentParser(
|
|
52
|
+
prog="claude-mpm",
|
|
53
|
+
description=f"Claude Multi-Agent Project Manager v{__version__} - Orchestrate Claude with agent delegation and ticket tracking",
|
|
54
|
+
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"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Version
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--version",
|
|
60
|
+
action="version",
|
|
61
|
+
version=f"claude-mpm {__version__}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Global options
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"-d", "--debug",
|
|
67
|
+
action="store_true",
|
|
68
|
+
help="Enable debug logging (deprecated, use --logging DEBUG)"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--logging",
|
|
73
|
+
choices=[level.value for level in LogLevel],
|
|
74
|
+
default=LogLevel.INFO.value,
|
|
75
|
+
help="Logging level (default: INFO)"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
parser.add_argument(
|
|
79
|
+
"--log-dir",
|
|
80
|
+
type=Path,
|
|
81
|
+
help="Custom log directory (default: ~/.claude-mpm/logs)"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--framework-path",
|
|
86
|
+
type=Path,
|
|
87
|
+
help="Path to claude-mpm framework"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
parser.add_argument(
|
|
91
|
+
"--agents-dir",
|
|
92
|
+
type=Path,
|
|
93
|
+
help="Custom agents directory to use"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
parser.add_argument(
|
|
97
|
+
"--no-hooks",
|
|
98
|
+
action="store_true",
|
|
99
|
+
help="Disable hook service (runs without hooks)"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Add run-specific arguments at top level (for default behavior)
|
|
103
|
+
parser.add_argument(
|
|
104
|
+
"--no-tickets",
|
|
105
|
+
action="store_true",
|
|
106
|
+
help="Disable automatic ticket creation"
|
|
107
|
+
)
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
"-i", "--input",
|
|
110
|
+
type=str,
|
|
111
|
+
help="Input text or file path (for non-interactive mode)"
|
|
112
|
+
)
|
|
113
|
+
parser.add_argument(
|
|
114
|
+
"--non-interactive",
|
|
115
|
+
action="store_true",
|
|
116
|
+
help="Run in non-interactive mode (read from stdin or --input)"
|
|
117
|
+
)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"--no-native-agents",
|
|
120
|
+
action="store_true",
|
|
121
|
+
help="Disable deployment of Claude Code native agents"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Don't add claude_args at top level - it conflicts with subcommands
|
|
125
|
+
|
|
126
|
+
# Commands (only non-prefixed for argparse, but we preprocess to support both)
|
|
127
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
128
|
+
|
|
129
|
+
# Run command (default)
|
|
130
|
+
run_parser = subparsers.add_parser(CLICommands.RUN.value, help="Run orchestrated Claude session (default)")
|
|
131
|
+
|
|
132
|
+
run_parser.add_argument(
|
|
133
|
+
"--no-hooks",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="Disable hook service (runs without hooks)"
|
|
136
|
+
)
|
|
137
|
+
run_parser.add_argument(
|
|
138
|
+
"--no-tickets",
|
|
139
|
+
action="store_true",
|
|
140
|
+
help="Disable automatic ticket creation"
|
|
141
|
+
)
|
|
142
|
+
run_parser.add_argument(
|
|
143
|
+
"-i", "--input",
|
|
144
|
+
type=str,
|
|
145
|
+
help="Input text or file path (for non-interactive mode)"
|
|
146
|
+
)
|
|
147
|
+
run_parser.add_argument(
|
|
148
|
+
"--non-interactive",
|
|
149
|
+
action="store_true",
|
|
150
|
+
help="Run in non-interactive mode (read from stdin or --input)"
|
|
151
|
+
)
|
|
152
|
+
run_parser.add_argument(
|
|
153
|
+
"--no-native-agents",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help="Disable deployment of Claude Code native agents"
|
|
156
|
+
)
|
|
157
|
+
run_parser.add_argument(
|
|
158
|
+
"claude_args",
|
|
159
|
+
nargs=argparse.REMAINDER,
|
|
160
|
+
help="Additional arguments to pass to Claude CLI (use -- before Claude args)"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# List tickets command
|
|
164
|
+
list_parser = subparsers.add_parser(CLICommands.TICKETS.value, help="List recent tickets")
|
|
165
|
+
list_parser.add_argument(
|
|
166
|
+
"-n", "--limit",
|
|
167
|
+
type=int,
|
|
168
|
+
default=10,
|
|
169
|
+
help="Number of tickets to show"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Info command
|
|
173
|
+
info_parser = subparsers.add_parser(CLICommands.INFO.value, help="Show framework and configuration info")
|
|
174
|
+
|
|
175
|
+
# UI command
|
|
176
|
+
ui_parser = subparsers.add_parser(CLICommands.UI.value, help="Launch terminal UI with multiple panes")
|
|
177
|
+
ui_parser.add_argument(
|
|
178
|
+
"--mode",
|
|
179
|
+
choices=["terminal", "curses"],
|
|
180
|
+
default="terminal",
|
|
181
|
+
help="UI mode to launch (default: terminal)"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Agent management commands
|
|
185
|
+
agents_parser = subparsers.add_parser(CLICommands.AGENTS.value, help="Manage Claude Code native agents")
|
|
186
|
+
agents_subparsers = agents_parser.add_subparsers(dest="agents_command", help="Agent commands")
|
|
187
|
+
|
|
188
|
+
# List agents
|
|
189
|
+
list_agents_parser = agents_subparsers.add_parser(AgentCommands.LIST.value, help="List available agents")
|
|
190
|
+
list_agents_parser.add_argument(
|
|
191
|
+
"--system",
|
|
192
|
+
action="store_true",
|
|
193
|
+
help="List system agents"
|
|
194
|
+
)
|
|
195
|
+
list_agents_parser.add_argument(
|
|
196
|
+
"--deployed",
|
|
197
|
+
action="store_true",
|
|
198
|
+
help="List deployed agents"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Deploy agents
|
|
202
|
+
deploy_agents_parser = agents_subparsers.add_parser(AgentCommands.DEPLOY.value, help="Deploy system agents")
|
|
203
|
+
deploy_agents_parser.add_argument(
|
|
204
|
+
"--target",
|
|
205
|
+
type=Path,
|
|
206
|
+
help="Target directory (default: .claude/agents/)"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Force deploy agents
|
|
210
|
+
force_deploy_parser = agents_subparsers.add_parser(AgentCommands.FORCE_DEPLOY.value, help="Force deploy all system agents")
|
|
211
|
+
force_deploy_parser.add_argument(
|
|
212
|
+
"--target",
|
|
213
|
+
type=Path,
|
|
214
|
+
help="Target directory (default: .claude/agents/)"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Clean agents
|
|
218
|
+
clean_agents_parser = agents_subparsers.add_parser(AgentCommands.CLEAN.value, help="Remove deployed system agents")
|
|
219
|
+
clean_agents_parser.add_argument(
|
|
220
|
+
"--target",
|
|
221
|
+
type=Path,
|
|
222
|
+
help="Target directory (default: .claude/)"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Preprocess and parse arguments
|
|
226
|
+
processed_argv = _preprocess_args(argv)
|
|
227
|
+
args = parser.parse_args(processed_argv)
|
|
228
|
+
|
|
229
|
+
# Debug: Print parsed args
|
|
230
|
+
if hasattr(args, 'debug') and args.debug:
|
|
231
|
+
print(f"DEBUG: Parsed args: {args}")
|
|
232
|
+
|
|
233
|
+
# Set up logging first
|
|
234
|
+
# Handle deprecated --debug flag
|
|
235
|
+
if args.debug and args.logging == LogLevel.INFO.value:
|
|
236
|
+
args.logging = LogLevel.DEBUG.value
|
|
237
|
+
|
|
238
|
+
# Only setup logging if not OFF
|
|
239
|
+
if args.logging != LogLevel.OFF.value:
|
|
240
|
+
logger = setup_logging(level=args.logging, log_dir=args.log_dir)
|
|
241
|
+
else:
|
|
242
|
+
# Minimal logger for CLI feedback
|
|
243
|
+
import logging
|
|
244
|
+
logger = logging.getLogger("cli")
|
|
245
|
+
logger.setLevel(logging.WARNING)
|
|
246
|
+
|
|
247
|
+
# Initialize hook service manager (unless disabled)
|
|
248
|
+
hook_manager = None
|
|
249
|
+
if not getattr(args, 'no_hooks', False):
|
|
250
|
+
try:
|
|
251
|
+
# Check if hooks are enabled via config
|
|
252
|
+
try:
|
|
253
|
+
from .config.hook_config import HookConfig
|
|
254
|
+
except ImportError:
|
|
255
|
+
from config.hook_config import HookConfig
|
|
256
|
+
if HookConfig.is_hooks_enabled():
|
|
257
|
+
hook_manager = JSONRPCHookManager(log_dir=args.log_dir)
|
|
258
|
+
if hook_manager.start_service():
|
|
259
|
+
logger.info("JSON-RPC hook system initialized")
|
|
260
|
+
if args.logging != LogLevel.OFF.value:
|
|
261
|
+
print("✓ JSON-RPC hook system initialized")
|
|
262
|
+
else:
|
|
263
|
+
logger.warning("Failed to initialize JSON-RPC hooks, continuing without hooks")
|
|
264
|
+
if args.logging != LogLevel.OFF.value:
|
|
265
|
+
print("⚠️ Failed to initialize JSON-RPC hooks, continuing without hooks")
|
|
266
|
+
hook_manager = None
|
|
267
|
+
else:
|
|
268
|
+
logger.info("Hooks disabled via configuration")
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.warning(f"Hook service initialization failed: {e}, continuing without hooks")
|
|
271
|
+
hook_manager = None
|
|
272
|
+
|
|
273
|
+
# Default to run command
|
|
274
|
+
if not args.command:
|
|
275
|
+
args.command = CLICommands.RUN.value
|
|
276
|
+
# Also set default arguments for run command when no subcommand specified
|
|
277
|
+
args.no_tickets = getattr(args, 'no_tickets', False)
|
|
278
|
+
args.no_hooks = getattr(args, 'no_hooks', False)
|
|
279
|
+
args.input = getattr(args, 'input', None)
|
|
280
|
+
args.non_interactive = getattr(args, 'non_interactive', False)
|
|
281
|
+
args.claude_args = getattr(args, 'claude_args', [])
|
|
282
|
+
|
|
283
|
+
# Debug output
|
|
284
|
+
logger.debug(f"Command: {args.command}")
|
|
285
|
+
logger.debug(f"Arguments: {args}")
|
|
286
|
+
|
|
287
|
+
# Execute command (we've already preprocessed prefixes)
|
|
288
|
+
command = args.command
|
|
289
|
+
|
|
290
|
+
try:
|
|
291
|
+
if command in [CLICommands.RUN.value, None]:
|
|
292
|
+
run_session(args, hook_manager)
|
|
293
|
+
elif command == CLICommands.TICKETS.value:
|
|
294
|
+
list_tickets(args)
|
|
295
|
+
elif command == CLICommands.INFO.value:
|
|
296
|
+
show_info(args, hook_manager)
|
|
297
|
+
elif command == CLICommands.AGENTS.value:
|
|
298
|
+
manage_agents(args)
|
|
299
|
+
elif command == CLICommands.UI.value:
|
|
300
|
+
run_terminal_ui(args)
|
|
301
|
+
else:
|
|
302
|
+
parser.print_help()
|
|
303
|
+
return 1
|
|
304
|
+
except KeyboardInterrupt:
|
|
305
|
+
logger.info("Session interrupted by user")
|
|
306
|
+
return 0
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.error(f"Error: {e}")
|
|
309
|
+
if args.debug:
|
|
310
|
+
import traceback
|
|
311
|
+
traceback.print_exc()
|
|
312
|
+
return 1
|
|
313
|
+
finally:
|
|
314
|
+
# Clean up hook service
|
|
315
|
+
if hook_manager:
|
|
316
|
+
hook_manager.stop_service()
|
|
317
|
+
|
|
318
|
+
return 0
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _get_user_input(args, logger):
|
|
322
|
+
"""Get user input based on args."""
|
|
323
|
+
if args.input:
|
|
324
|
+
# Read from file or use as direct input
|
|
325
|
+
input_path = Path(args.input)
|
|
326
|
+
if input_path.exists():
|
|
327
|
+
logger.info(f"Reading input from file: {input_path}")
|
|
328
|
+
return input_path.read_text()
|
|
329
|
+
else:
|
|
330
|
+
logger.info("Using command line input")
|
|
331
|
+
return args.input
|
|
332
|
+
else:
|
|
333
|
+
# Read from stdin
|
|
334
|
+
logger.info("Reading input from stdin")
|
|
335
|
+
return sys.stdin.read()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def run_session(args, hook_manager=None):
|
|
341
|
+
"""Run a simplified Claude session."""
|
|
342
|
+
logger = get_logger("cli")
|
|
343
|
+
if args.logging != LogLevel.OFF.value:
|
|
344
|
+
logger.info("Starting Claude MPM session")
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
from .core.simple_runner import SimpleClaudeRunner, create_simple_context
|
|
348
|
+
except ImportError:
|
|
349
|
+
from core.simple_runner import SimpleClaudeRunner, create_simple_context
|
|
350
|
+
|
|
351
|
+
# Skip native agents if disabled
|
|
352
|
+
if getattr(args, 'no_native_agents', False):
|
|
353
|
+
print("Native agents disabled")
|
|
354
|
+
|
|
355
|
+
# Create simple runner
|
|
356
|
+
enable_tickets = not args.no_tickets
|
|
357
|
+
claude_args = getattr(args, 'claude_args', []) or []
|
|
358
|
+
runner = SimpleClaudeRunner(enable_tickets=enable_tickets, log_level=args.logging, claude_args=claude_args)
|
|
359
|
+
|
|
360
|
+
# Create basic context
|
|
361
|
+
context = create_simple_context()
|
|
362
|
+
|
|
363
|
+
# Run session based on mode
|
|
364
|
+
if args.non_interactive or args.input:
|
|
365
|
+
user_input = _get_user_input(args, logger)
|
|
366
|
+
success = runner.run_oneshot(user_input, context)
|
|
367
|
+
if not success:
|
|
368
|
+
logger.error("Session failed")
|
|
369
|
+
else:
|
|
370
|
+
# Run interactive session
|
|
371
|
+
runner.run_interactive(context)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def list_tickets(args):
|
|
375
|
+
"""List recent tickets."""
|
|
376
|
+
logger = get_logger("cli")
|
|
377
|
+
|
|
378
|
+
try:
|
|
379
|
+
try:
|
|
380
|
+
from .services.ticket_manager import TicketManager
|
|
381
|
+
except ImportError:
|
|
382
|
+
from services.ticket_manager import TicketManager
|
|
383
|
+
|
|
384
|
+
ticket_manager = TicketManager()
|
|
385
|
+
tickets = ticket_manager.list_recent_tickets(limit=args.limit)
|
|
386
|
+
|
|
387
|
+
if not tickets:
|
|
388
|
+
print("No tickets found")
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
print(f"Recent tickets (showing {len(tickets)}):")
|
|
392
|
+
print("-" * 80)
|
|
393
|
+
|
|
394
|
+
for ticket in tickets:
|
|
395
|
+
status_emoji = {
|
|
396
|
+
"open": "🔵",
|
|
397
|
+
"in_progress": "🟡",
|
|
398
|
+
"done": "🟢",
|
|
399
|
+
"closed": "⚫"
|
|
400
|
+
}.get(ticket['status'], "⚪")
|
|
401
|
+
|
|
402
|
+
print(f"{status_emoji} [{ticket['id']}] {ticket['title']}")
|
|
403
|
+
print(f" Priority: {ticket['priority']} | Tags: {', '.join(ticket['tags'])}")
|
|
404
|
+
print(f" Created: {ticket['created_at']}")
|
|
405
|
+
print()
|
|
406
|
+
|
|
407
|
+
except ImportError:
|
|
408
|
+
logger.error("ai-trackdown-pytools not installed")
|
|
409
|
+
print("Error: ai-trackdown-pytools not installed")
|
|
410
|
+
print("Install with: pip install ai-trackdown-pytools")
|
|
411
|
+
except Exception as e:
|
|
412
|
+
logger.error(f"Error listing tickets: {e}")
|
|
413
|
+
print(f"Error: {e}")
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def manage_agents(args):
|
|
417
|
+
"""Manage Claude Code native agents."""
|
|
418
|
+
logger = get_logger("cli")
|
|
419
|
+
|
|
420
|
+
try:
|
|
421
|
+
from .services.agent_deployment import AgentDeploymentService
|
|
422
|
+
deployment_service = AgentDeploymentService()
|
|
423
|
+
|
|
424
|
+
if not args.agents_command:
|
|
425
|
+
print("Error: No agent command specified")
|
|
426
|
+
print("\nUsage: claude-mpm --mpm:agents <command> [options]")
|
|
427
|
+
print("\nAvailable commands:")
|
|
428
|
+
print(" list - List available agents")
|
|
429
|
+
print(" deploy - Deploy system agents")
|
|
430
|
+
print(" force-deploy - Force deploy all system agents")
|
|
431
|
+
print(" clean - Remove deployed system agents")
|
|
432
|
+
print("\nExamples:")
|
|
433
|
+
print(" claude-mpm --mpm:agents list --system")
|
|
434
|
+
print(" claude-mpm --mpm:agents deploy")
|
|
435
|
+
print(" claude-mpm --mpm:agents force-deploy")
|
|
436
|
+
return
|
|
437
|
+
|
|
438
|
+
if args.agents_command == AgentCommands.LIST.value:
|
|
439
|
+
# Determine what to list
|
|
440
|
+
if args.system:
|
|
441
|
+
# List available agent templates
|
|
442
|
+
print("Available Agent Templates:")
|
|
443
|
+
print("-" * 80)
|
|
444
|
+
agents = deployment_service.list_available_agents()
|
|
445
|
+
if not agents:
|
|
446
|
+
print("No agent templates found")
|
|
447
|
+
else:
|
|
448
|
+
for agent in agents:
|
|
449
|
+
print(f"📄 {agent['file']}")
|
|
450
|
+
if 'name' in agent:
|
|
451
|
+
print(f" Name: {agent['name']}")
|
|
452
|
+
if 'description' in agent:
|
|
453
|
+
print(f" Description: {agent['description']}")
|
|
454
|
+
if 'version' in agent:
|
|
455
|
+
print(f" Version: {agent['version']}")
|
|
456
|
+
print()
|
|
457
|
+
|
|
458
|
+
elif args.deployed:
|
|
459
|
+
# List deployed agents
|
|
460
|
+
print("Deployed Agents:")
|
|
461
|
+
print("-" * 80)
|
|
462
|
+
verification = deployment_service.verify_deployment()
|
|
463
|
+
if not verification["agents_found"]:
|
|
464
|
+
print("No deployed agents found")
|
|
465
|
+
else:
|
|
466
|
+
for agent in verification["agents_found"]:
|
|
467
|
+
print(f"📄 {agent['file']}")
|
|
468
|
+
if 'name' in agent:
|
|
469
|
+
print(f" Name: {agent['name']}")
|
|
470
|
+
print(f" Path: {agent['path']}")
|
|
471
|
+
print()
|
|
472
|
+
|
|
473
|
+
if verification["warnings"]:
|
|
474
|
+
print("\nWarnings:")
|
|
475
|
+
for warning in verification["warnings"]:
|
|
476
|
+
print(f" ⚠️ {warning}")
|
|
477
|
+
|
|
478
|
+
else:
|
|
479
|
+
# Default: list both
|
|
480
|
+
print("Use --system to list system agents or --deployed to list deployed agents")
|
|
481
|
+
|
|
482
|
+
elif args.agents_command == AgentCommands.DEPLOY.value:
|
|
483
|
+
# Deploy agents
|
|
484
|
+
print("Deploying system agents...")
|
|
485
|
+
results = deployment_service.deploy_agents(args.target, force_rebuild=False)
|
|
486
|
+
|
|
487
|
+
if results["deployed"]:
|
|
488
|
+
print(f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}")
|
|
489
|
+
for agent in results["deployed"]:
|
|
490
|
+
print(f" - {agent['name']}")
|
|
491
|
+
|
|
492
|
+
elif args.agents_command == AgentCommands.FORCE_DEPLOY.value:
|
|
493
|
+
# Force deploy agents
|
|
494
|
+
print("Force deploying all system agents...")
|
|
495
|
+
results = deployment_service.deploy_agents(args.target, force_rebuild=True)
|
|
496
|
+
|
|
497
|
+
if results["deployed"]:
|
|
498
|
+
print(f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}")
|
|
499
|
+
for agent in results["deployed"]:
|
|
500
|
+
print(f" - {agent['name']}")
|
|
501
|
+
|
|
502
|
+
if results.get("updated", []):
|
|
503
|
+
print(f"\n✓ Updated {len(results['updated'])} agents")
|
|
504
|
+
for agent in results["updated"]:
|
|
505
|
+
print(f" - {agent['name']}")
|
|
506
|
+
|
|
507
|
+
if results.get("skipped", []):
|
|
508
|
+
print(f"\n✓ Skipped {len(results['skipped'])} up-to-date agents")
|
|
509
|
+
|
|
510
|
+
if results["errors"]:
|
|
511
|
+
print("\n❌ Errors during deployment:")
|
|
512
|
+
for error in results["errors"]:
|
|
513
|
+
print(f" - {error}")
|
|
514
|
+
|
|
515
|
+
# Set environment
|
|
516
|
+
env_vars = deployment_service.set_claude_environment(args.target.parent if args.target else None)
|
|
517
|
+
print(f"\n✓ Set Claude environment variables:")
|
|
518
|
+
for key, value in env_vars.items():
|
|
519
|
+
print(f" - {key}={value}")
|
|
520
|
+
|
|
521
|
+
elif args.agents_command == AgentCommands.CLEAN.value:
|
|
522
|
+
# Clean deployed agents
|
|
523
|
+
print("Cleaning deployed system agents...")
|
|
524
|
+
results = deployment_service.clean_deployment(args.target)
|
|
525
|
+
|
|
526
|
+
if results["removed"]:
|
|
527
|
+
print(f"\n✓ Removed {len(results['removed'])} agents")
|
|
528
|
+
for path in results["removed"]:
|
|
529
|
+
print(f" - {Path(path).name}")
|
|
530
|
+
else:
|
|
531
|
+
print("No system agents found to remove")
|
|
532
|
+
|
|
533
|
+
if results["errors"]:
|
|
534
|
+
print("\n❌ Errors during cleanup:")
|
|
535
|
+
for error in results["errors"]:
|
|
536
|
+
print(f" - {error}")
|
|
537
|
+
|
|
538
|
+
except ImportError:
|
|
539
|
+
logger.error("Agent deployment service not available")
|
|
540
|
+
print("Error: Agent deployment service not available")
|
|
541
|
+
except Exception as e:
|
|
542
|
+
logger.error(f"Error managing agents: {e}")
|
|
543
|
+
print(f"Error: {e}")
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def run_terminal_ui(args):
|
|
547
|
+
"""Run the terminal UI."""
|
|
548
|
+
logger = get_logger("cli")
|
|
549
|
+
|
|
550
|
+
ui_mode = getattr(args, 'mode', 'terminal')
|
|
551
|
+
|
|
552
|
+
try:
|
|
553
|
+
if ui_mode == 'terminal':
|
|
554
|
+
# Try rich UI first
|
|
555
|
+
try:
|
|
556
|
+
from .ui.rich_terminal_ui import main as run_rich_ui
|
|
557
|
+
logger.info("Starting rich terminal UI...")
|
|
558
|
+
run_rich_ui()
|
|
559
|
+
except ImportError:
|
|
560
|
+
# Fallback to curses UI
|
|
561
|
+
logger.info("Rich not available, falling back to curses UI...")
|
|
562
|
+
from .ui.terminal_ui import TerminalUI
|
|
563
|
+
ui = TerminalUI()
|
|
564
|
+
ui.run()
|
|
565
|
+
else:
|
|
566
|
+
# Use curses UI
|
|
567
|
+
from .ui.terminal_ui import TerminalUI
|
|
568
|
+
ui = TerminalUI()
|
|
569
|
+
ui.run()
|
|
570
|
+
except ImportError as e:
|
|
571
|
+
logger.error(f"UI module not found: {e}")
|
|
572
|
+
print(f"Error: Terminal UI requires 'curses' (built-in) or 'rich' (pip install rich)")
|
|
573
|
+
return 1
|
|
574
|
+
except Exception as e:
|
|
575
|
+
logger.error(f"Error running terminal UI: {e}")
|
|
576
|
+
print(f"Error: {e}")
|
|
577
|
+
return 1
|
|
578
|
+
|
|
579
|
+
return 0
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def show_info(args, hook_manager=None):
|
|
583
|
+
"""Show framework and configuration information."""
|
|
584
|
+
try:
|
|
585
|
+
from .core.framework_loader import FrameworkLoader
|
|
586
|
+
except ImportError:
|
|
587
|
+
from core.framework_loader import FrameworkLoader
|
|
588
|
+
|
|
589
|
+
print("Claude MPM - Multi-Agent Project Manager")
|
|
590
|
+
print("=" * 50)
|
|
591
|
+
|
|
592
|
+
# Framework info
|
|
593
|
+
loader = FrameworkLoader(args.framework_path)
|
|
594
|
+
if loader.framework_content["loaded"]:
|
|
595
|
+
print(f"Framework: claude-multiagent-pm")
|
|
596
|
+
print(f"Version: {loader.framework_content['version']}")
|
|
597
|
+
print(f"Path: {loader.framework_path}")
|
|
598
|
+
print(f"Agents: {', '.join(loader.get_agent_list())}")
|
|
599
|
+
else:
|
|
600
|
+
print("Framework: Not found (using minimal instructions)")
|
|
601
|
+
|
|
602
|
+
print()
|
|
603
|
+
|
|
604
|
+
# Configuration
|
|
605
|
+
print("Configuration:")
|
|
606
|
+
print(f" Log directory: {args.log_dir or '~/.claude-mpm/logs'}")
|
|
607
|
+
print(f" Debug mode: {args.debug}")
|
|
608
|
+
|
|
609
|
+
# Show agent hierarchy
|
|
610
|
+
if loader.agent_registry:
|
|
611
|
+
hierarchy = loader.agent_registry.get_agent_hierarchy()
|
|
612
|
+
print("\nAgent Hierarchy:")
|
|
613
|
+
print(f" Project agents: {len(hierarchy['project'])}")
|
|
614
|
+
print(f" User agents: {len(hierarchy['user'])}")
|
|
615
|
+
print(f" System agents: {len(hierarchy['system'])}")
|
|
616
|
+
|
|
617
|
+
# Show core agents
|
|
618
|
+
core_agents = loader.agent_registry.get_core_agents()
|
|
619
|
+
print(f"\nCore Agents: {', '.join(core_agents)}")
|
|
620
|
+
|
|
621
|
+
# Check dependencies
|
|
622
|
+
print("\nDependencies:")
|
|
623
|
+
|
|
624
|
+
# Check Claude
|
|
625
|
+
import shutil
|
|
626
|
+
claude_path = shutil.which("claude")
|
|
627
|
+
if claude_path:
|
|
628
|
+
print(f" ✓ Claude CLI: {claude_path}")
|
|
629
|
+
else:
|
|
630
|
+
print(" ✗ Claude CLI: Not found in PATH")
|
|
631
|
+
|
|
632
|
+
# Check ai-trackdown-pytools
|
|
633
|
+
try:
|
|
634
|
+
import ai_trackdown_pytools
|
|
635
|
+
print(" ✓ ai-trackdown-pytools: Installed")
|
|
636
|
+
except ImportError:
|
|
637
|
+
print(" ✗ ai-trackdown-pytools: Not installed")
|
|
638
|
+
|
|
639
|
+
# Check hook service
|
|
640
|
+
if hook_manager:
|
|
641
|
+
info = hook_manager.get_service_info()
|
|
642
|
+
if info['running']:
|
|
643
|
+
print(f" ✓ Hook System: JSON-RPC ({info.get('hook_count', 0)} hooks)")
|
|
644
|
+
if 'discovered_hooks' in info and info['discovered_hooks']:
|
|
645
|
+
print(f" Hooks: {', '.join(info['discovered_hooks'][:5])}")
|
|
646
|
+
if len(info['discovered_hooks']) > 5:
|
|
647
|
+
print(f" ... and {len(info['discovered_hooks']) - 5} more")
|
|
648
|
+
else:
|
|
649
|
+
print(" ✗ Hook System: Not running")
|
|
650
|
+
else:
|
|
651
|
+
print(" ✗ Hook System: Disabled (--no-hooks)")
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
if __name__ == "__main__":
|
|
655
|
+
sys.exit(main())
|
claude_mpm/cli_main.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Main entry point for CLI that can be run directly."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
# Add src directory to path so claude_mpm can be imported
|
|
7
|
+
src_dir = Path(__file__).parent.parent
|
|
8
|
+
sys.path.insert(0, str(src_dir))
|
|
9
|
+
|
|
10
|
+
from claude_mpm.cli import main
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
sys.exit(main())
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""CLI module for claude-mpm.
|
|
2
|
+
|
|
3
|
+
This module provides registry-based argument and command management
|
|
4
|
+
to reduce complexity in the main CLI function.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .args import ArgumentRegistry
|
|
8
|
+
from .commands import CommandRegistry, CommandDefinition, register_standard_commands
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
'ArgumentRegistry',
|
|
12
|
+
'CommandRegistry',
|
|
13
|
+
'CommandDefinition',
|
|
14
|
+
'register_standard_commands'
|
|
15
|
+
]
|