claude-mpm 1.0.0__py3-none-any.whl → 1.1.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.
- claude_mpm/_version.py +3 -2
- claude_mpm/agents/__init__.py +2 -2
- claude_mpm/agents/agent-template.yaml +83 -0
- claude_mpm/agents/agent_loader.py +66 -90
- claude_mpm/agents/base_agent_loader.py +10 -15
- claude_mpm/cli.py +41 -47
- claude_mpm/cli_enhancements.py +297 -0
- claude_mpm/core/factories.py +1 -46
- claude_mpm/core/service_registry.py +0 -8
- claude_mpm/core/simple_runner.py +43 -0
- claude_mpm/generators/__init__.py +5 -0
- claude_mpm/generators/agent_profile_generator.py +137 -0
- claude_mpm/hooks/README.md +75 -221
- claude_mpm/hooks/builtin/mpm_command_hook.py +125 -0
- claude_mpm/hooks/claude_hooks/__init__.py +5 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +399 -0
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +47 -0
- claude_mpm/hooks/validation_hooks.py +181 -0
- claude_mpm/services/agent_management_service.py +4 -4
- claude_mpm/services/agent_profile_loader.py +1 -1
- claude_mpm/services/agent_registry.py +0 -1
- claude_mpm/services/base_agent_manager.py +3 -3
- claude_mpm/utils/error_handler.py +247 -0
- claude_mpm/validation/__init__.py +5 -0
- claude_mpm/validation/agent_validator.py +175 -0
- {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/METADATA +44 -7
- {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/RECORD +30 -26
- claude_mpm/config/hook_config.py +0 -42
- claude_mpm/hooks/hook_client.py +0 -264
- claude_mpm/hooks/hook_runner.py +0 -370
- claude_mpm/hooks/json_rpc_executor.py +0 -259
- claude_mpm/hooks/json_rpc_hook_client.py +0 -319
- claude_mpm/services/hook_service.py +0 -388
- claude_mpm/services/hook_service_manager.py +0 -223
- claude_mpm/services/json_rpc_hook_manager.py +0 -92
- {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/WHEEL +0 -0
- {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/top_level.txt +0 -0
claude_mpm/_version.py
CHANGED
|
@@ -6,10 +6,10 @@ try:
|
|
|
6
6
|
try:
|
|
7
7
|
__version__ = version("claude-mpm")
|
|
8
8
|
except PackageNotFoundError:
|
|
9
|
-
__version__ = "1.
|
|
9
|
+
__version__ = "1.1.0"
|
|
10
10
|
except ImportError:
|
|
11
11
|
# Fallback for older Python versions
|
|
12
|
-
__version__ = "1.
|
|
12
|
+
__version__ = "1.1.0"
|
|
13
13
|
|
|
14
14
|
# This file may be overwritten by setuptools-scm during build
|
|
15
15
|
# The try/except ensures we always have a version available
|
|
@@ -25,6 +25,7 @@ def get_version_tuple():
|
|
|
25
25
|
__version_info__ = get_version_tuple()
|
|
26
26
|
|
|
27
27
|
# Version history
|
|
28
|
+
# 1.1.0 - BREAKING: Removed JSON-RPC hooks, enhanced Claude Code hooks with project-specific logging
|
|
28
29
|
# 1.0.0 - BREAKING: Architecture simplification, TodoWrite hooks, enhanced CLI, terminal UI
|
|
29
30
|
# 0.5.0 - Comprehensive deployment support for PyPI, npm, and local installation
|
|
30
31
|
# 0.3.0 - Added hook service architecture for context filtering and ticket automation
|
claude_mpm/agents/__init__.py
CHANGED
|
@@ -4,8 +4,8 @@ Claude PM Framework Agents Package
|
|
|
4
4
|
System-level agent implementations with Task Tool integration.
|
|
5
5
|
These agents provide specialized prompts and capabilities for PM orchestration.
|
|
6
6
|
|
|
7
|
-
Uses unified agent loader to load prompts from
|
|
8
|
-
|
|
7
|
+
Uses unified agent loader to load prompts from JSON templates in agents/templates/
|
|
8
|
+
for better structure and maintainability.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
# Import from unified agent loader
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Agent Profile Template Configuration
|
|
2
|
+
# Inspired by awesome-claude-code's template system
|
|
3
|
+
|
|
4
|
+
profile:
|
|
5
|
+
name: "{{AGENT_NAME}}"
|
|
6
|
+
version: "{{VERSION}}"
|
|
7
|
+
description: "{{DESCRIPTION}}"
|
|
8
|
+
author: "{{AUTHOR}}"
|
|
9
|
+
created: "{{CREATED_DATE}}"
|
|
10
|
+
|
|
11
|
+
# Categories for organizing agents
|
|
12
|
+
categories:
|
|
13
|
+
- id: analysis
|
|
14
|
+
title: "Code Analysis Agents"
|
|
15
|
+
description: |
|
|
16
|
+
> Agents specialized in analyzing codebases, identifying patterns, and providing insights
|
|
17
|
+
icon: "🔍"
|
|
18
|
+
|
|
19
|
+
- id: implementation
|
|
20
|
+
title: "Implementation Agents"
|
|
21
|
+
description: |
|
|
22
|
+
> Agents focused on writing and modifying code
|
|
23
|
+
icon: "🛠️"
|
|
24
|
+
|
|
25
|
+
- id: testing
|
|
26
|
+
title: "Testing & QA Agents"
|
|
27
|
+
description: |
|
|
28
|
+
> Agents dedicated to testing, quality assurance, and validation
|
|
29
|
+
icon: "🧪"
|
|
30
|
+
|
|
31
|
+
- id: security
|
|
32
|
+
title: "Security Agents"
|
|
33
|
+
description: |
|
|
34
|
+
> Agents specialized in security analysis and vulnerability detection
|
|
35
|
+
icon: "🔒"
|
|
36
|
+
|
|
37
|
+
agents:
|
|
38
|
+
- name: "{{AGENT_ID}}"
|
|
39
|
+
category: "{{CATEGORY}}"
|
|
40
|
+
role: "{{ROLE}}"
|
|
41
|
+
description: "{{AGENT_DESCRIPTION}}"
|
|
42
|
+
|
|
43
|
+
capabilities:
|
|
44
|
+
- "{{CAPABILITY_1}}"
|
|
45
|
+
- "{{CAPABILITY_2}}"
|
|
46
|
+
- "{{CAPABILITY_3}}"
|
|
47
|
+
|
|
48
|
+
constraints:
|
|
49
|
+
- "{{CONSTRAINT_1}}"
|
|
50
|
+
- "{{CONSTRAINT_2}}"
|
|
51
|
+
|
|
52
|
+
tools:
|
|
53
|
+
- name: "{{TOOL_NAME}}"
|
|
54
|
+
description: "{{TOOL_DESCRIPTION}}"
|
|
55
|
+
required: true
|
|
56
|
+
|
|
57
|
+
prompt_template: |
|
|
58
|
+
You are a {{ROLE}} agent specializing in {{SPECIALIZATION}}.
|
|
59
|
+
|
|
60
|
+
## Your Capabilities:
|
|
61
|
+
{{CAPABILITIES_LIST}}
|
|
62
|
+
|
|
63
|
+
## Your Constraints:
|
|
64
|
+
{{CONSTRAINTS_LIST}}
|
|
65
|
+
|
|
66
|
+
## Context:
|
|
67
|
+
{context}
|
|
68
|
+
|
|
69
|
+
## Task:
|
|
70
|
+
{task}
|
|
71
|
+
|
|
72
|
+
## Additional Instructions:
|
|
73
|
+
{{ADDITIONAL_INSTRUCTIONS}}
|
|
74
|
+
|
|
75
|
+
examples:
|
|
76
|
+
- scenario: "{{EXAMPLE_SCENARIO}}"
|
|
77
|
+
input: "{{EXAMPLE_INPUT}}"
|
|
78
|
+
expected_output: "{{EXAMPLE_OUTPUT}}"
|
|
79
|
+
|
|
80
|
+
best_practices:
|
|
81
|
+
- "{{BEST_PRACTICE_1}}"
|
|
82
|
+
- "{{BEST_PRACTICE_2}}"
|
|
83
|
+
- "{{BEST_PRACTICE_3}}"
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
Unified Agent Loader System
|
|
4
4
|
==========================
|
|
5
5
|
|
|
6
|
-
Provides unified loading of agent prompts from
|
|
6
|
+
Provides unified loading of agent prompts from JSON template files.
|
|
7
7
|
Integrates with SharedPromptCache for performance optimization.
|
|
8
8
|
|
|
9
9
|
Key Features:
|
|
10
|
-
- Loads agent prompts from
|
|
10
|
+
- Loads agent prompts from src/claude_mpm/agents/templates/*.json files
|
|
11
11
|
- Handles base_agent.md prepending
|
|
12
12
|
- Provides backward-compatible get_*_agent_prompt() functions
|
|
13
13
|
- Uses SharedPromptCache for performance
|
|
@@ -20,7 +20,7 @@ For advanced agent management features (CRUD, versioning, section updates), use:
|
|
|
20
20
|
Usage:
|
|
21
21
|
from claude_pm.agents.agent_loader import get_documentation_agent_prompt
|
|
22
22
|
|
|
23
|
-
# Get agent prompt from
|
|
23
|
+
# Get agent prompt from JSON template
|
|
24
24
|
prompt = get_documentation_agent_prompt()
|
|
25
25
|
"""
|
|
26
26
|
|
|
@@ -49,64 +49,29 @@ class ModelType:
|
|
|
49
49
|
logger = logging.getLogger(__name__)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def
|
|
53
|
-
"""Get the
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
framework_root = PathResolver.get_framework_root()
|
|
57
|
-
|
|
58
|
-
# Check if we're running from a wheel installation
|
|
59
|
-
try:
|
|
60
|
-
import claude_pm
|
|
61
|
-
package_path = Path(claude_pm.__file__).parent
|
|
62
|
-
path_str = str(package_path.resolve())
|
|
63
|
-
if 'site-packages' in path_str or 'dist-packages' in path_str:
|
|
64
|
-
# For wheel installations, check data directory
|
|
65
|
-
data_agent_roles = package_path / "data" / "framework" / "agent-roles"
|
|
66
|
-
if data_agent_roles.exists():
|
|
67
|
-
logger.debug(f"Using wheel installation agent-roles: {data_agent_roles}")
|
|
68
|
-
return data_agent_roles
|
|
69
|
-
except Exception:
|
|
70
|
-
pass
|
|
71
|
-
|
|
72
|
-
# Check framework structure
|
|
73
|
-
agent_roles_dir = framework_root / "framework" / "agent-roles"
|
|
74
|
-
if agent_roles_dir.exists():
|
|
75
|
-
logger.debug(f"Using framework agent-roles: {agent_roles_dir}")
|
|
76
|
-
return agent_roles_dir
|
|
77
|
-
|
|
78
|
-
# Try agents directory as fallback
|
|
79
|
-
agents_dir = PathResolver.get_agents_dir()
|
|
80
|
-
logger.debug(f"Using agents directory: {agents_dir}")
|
|
81
|
-
return agents_dir
|
|
82
|
-
|
|
83
|
-
except FileNotFoundError as e:
|
|
84
|
-
# Ultimate fallback
|
|
85
|
-
logger.warning(f"PathResolver could not find framework root: {e}")
|
|
86
|
-
fallback = Path(__file__).parent.parent.parent / "framework" / "agent-roles"
|
|
87
|
-
logger.warning(f"Using fallback agent-roles path: {fallback}")
|
|
88
|
-
return fallback
|
|
52
|
+
def _get_agent_templates_dir() -> Path:
|
|
53
|
+
"""Get the agent templates directory."""
|
|
54
|
+
# Agent templates are now in the agents/templates directory
|
|
55
|
+
return Path(__file__).parent / "templates"
|
|
89
56
|
|
|
90
57
|
|
|
91
|
-
#
|
|
92
|
-
|
|
58
|
+
# Agent templates directory
|
|
59
|
+
AGENT_TEMPLATES_DIR = _get_agent_templates_dir()
|
|
93
60
|
|
|
94
61
|
# Cache prefix for agent prompts
|
|
95
62
|
AGENT_CACHE_PREFIX = "agent_prompt:"
|
|
96
63
|
|
|
97
|
-
# Agent name mappings (agent name ->
|
|
64
|
+
# Agent name mappings (agent name -> JSON file name)
|
|
98
65
|
AGENT_MAPPINGS = {
|
|
99
|
-
"documentation": "
|
|
100
|
-
"version_control": "
|
|
101
|
-
"qa": "
|
|
102
|
-
"research": "
|
|
103
|
-
"ops": "
|
|
104
|
-
"security": "
|
|
105
|
-
"engineer": "
|
|
106
|
-
"data_engineer": "
|
|
107
|
-
|
|
108
|
-
"orchestrator": "pm-orchestrator-agent.md",
|
|
109
|
-
"pm_orchestrator": "pm-orchestrator-agent.md"
|
|
66
|
+
"documentation": "documentation_agent.json",
|
|
67
|
+
"version_control": "version_control_agent.json",
|
|
68
|
+
"qa": "qa_agent.json",
|
|
69
|
+
"research": "research_agent.json",
|
|
70
|
+
"ops": "ops_agent.json",
|
|
71
|
+
"security": "security_agent.json",
|
|
72
|
+
"engineer": "engineer_agent.json",
|
|
73
|
+
"data_engineer": "data_engineer_agent.json"
|
|
74
|
+
# Note: pm, orchestrator, and pm_orchestrator agents are handled separately
|
|
110
75
|
}
|
|
111
76
|
|
|
112
77
|
# Model configuration thresholds
|
|
@@ -142,19 +107,19 @@ MODEL_NAME_MAPPINGS = {
|
|
|
142
107
|
|
|
143
108
|
def load_agent_prompt_from_md(agent_name: str, force_reload: bool = False) -> Optional[str]:
|
|
144
109
|
"""
|
|
145
|
-
Load agent prompt from
|
|
110
|
+
Load agent prompt from JSON template file.
|
|
146
111
|
|
|
147
112
|
Args:
|
|
148
113
|
agent_name: Agent name (e.g., 'documentation', 'ticketing')
|
|
149
114
|
force_reload: Force reload from file, bypassing cache
|
|
150
115
|
|
|
151
116
|
Returns:
|
|
152
|
-
str: Agent prompt content from
|
|
117
|
+
str: Agent prompt content from JSON template, or None if not found
|
|
153
118
|
"""
|
|
154
119
|
try:
|
|
155
120
|
# Get cache instance
|
|
156
121
|
cache = SharedPromptCache.get_instance()
|
|
157
|
-
cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}:
|
|
122
|
+
cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}:json"
|
|
158
123
|
|
|
159
124
|
# Check cache first (unless force reload)
|
|
160
125
|
if not force_reload:
|
|
@@ -163,23 +128,40 @@ def load_agent_prompt_from_md(agent_name: str, force_reload: bool = False) -> Op
|
|
|
163
128
|
logger.debug(f"Agent prompt for '{agent_name}' loaded from cache")
|
|
164
129
|
return str(cached_content)
|
|
165
130
|
|
|
166
|
-
# Get
|
|
167
|
-
|
|
168
|
-
if not
|
|
169
|
-
logger.warning(f"No
|
|
131
|
+
# Get JSON file path
|
|
132
|
+
json_filename = AGENT_MAPPINGS.get(agent_name)
|
|
133
|
+
if not json_filename:
|
|
134
|
+
logger.warning(f"No JSON file mapping found for agent: {agent_name}")
|
|
170
135
|
return None
|
|
171
136
|
|
|
172
|
-
|
|
173
|
-
framework_agent_roles_dir = _get_framework_agent_roles_dir()
|
|
174
|
-
md_path = framework_agent_roles_dir / md_filename
|
|
137
|
+
json_path = AGENT_TEMPLATES_DIR / json_filename
|
|
175
138
|
|
|
176
139
|
# Check if file exists
|
|
177
|
-
if not
|
|
178
|
-
logger.warning(f"Agent
|
|
140
|
+
if not json_path.exists():
|
|
141
|
+
logger.warning(f"Agent JSON file not found: {json_path}")
|
|
179
142
|
return None
|
|
180
143
|
|
|
181
|
-
logger.debug(f"Loading agent prompt from: {
|
|
182
|
-
|
|
144
|
+
logger.debug(f"Loading agent prompt from: {json_path}")
|
|
145
|
+
|
|
146
|
+
# Load and parse JSON
|
|
147
|
+
import json
|
|
148
|
+
with open(json_path, 'r', encoding='utf-8') as f:
|
|
149
|
+
data = json.load(f)
|
|
150
|
+
|
|
151
|
+
# Extract prompt content from JSON
|
|
152
|
+
# Check multiple possible locations for instructions/content
|
|
153
|
+
# Following the same pattern as AgentDeploymentService
|
|
154
|
+
content = (
|
|
155
|
+
data.get('instructions') or
|
|
156
|
+
data.get('narrative_fields', {}).get('instructions') or
|
|
157
|
+
data.get('content') or
|
|
158
|
+
''
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if not content:
|
|
162
|
+
logger.warning(f"No content/instructions found in JSON template: {json_path}")
|
|
163
|
+
logger.debug(f"Checked fields: instructions, narrative_fields.instructions, content")
|
|
164
|
+
return None
|
|
183
165
|
|
|
184
166
|
# Cache the content with 1 hour TTL
|
|
185
167
|
cache.set(cache_key, content, ttl=3600)
|
|
@@ -188,7 +170,7 @@ def load_agent_prompt_from_md(agent_name: str, force_reload: bool = False) -> Op
|
|
|
188
170
|
return content
|
|
189
171
|
|
|
190
172
|
except Exception as e:
|
|
191
|
-
logger.error(f"Error loading agent prompt from
|
|
173
|
+
logger.error(f"Error loading agent prompt from JSON for '{agent_name}': {e}")
|
|
192
174
|
return None
|
|
193
175
|
|
|
194
176
|
|
|
@@ -289,7 +271,7 @@ def _get_model_config(agent_name: str, complexity_analysis: Optional[Dict[str, A
|
|
|
289
271
|
|
|
290
272
|
def get_agent_prompt(agent_name: str, force_reload: bool = False, return_model_info: bool = False, **kwargs: Any) -> Union[str, Tuple[str, str, Dict[str, Any]]]:
|
|
291
273
|
"""
|
|
292
|
-
Get agent prompt from
|
|
274
|
+
Get agent prompt from JSON template with optional dynamic model selection.
|
|
293
275
|
|
|
294
276
|
Args:
|
|
295
277
|
agent_name: Agent name (e.g., 'documentation', 'ticketing')
|
|
@@ -305,11 +287,11 @@ def get_agent_prompt(agent_name: str, force_reload: bool = False, return_model_i
|
|
|
305
287
|
str or tuple: Complete agent prompt with base instructions prepended,
|
|
306
288
|
or tuple of (prompt, selected_model, model_config) if return_model_info=True
|
|
307
289
|
"""
|
|
308
|
-
# Load from
|
|
290
|
+
# Load from JSON template
|
|
309
291
|
prompt = load_agent_prompt_from_md(agent_name, force_reload)
|
|
310
292
|
|
|
311
293
|
if prompt is None:
|
|
312
|
-
raise ValueError(f"No agent prompt
|
|
294
|
+
raise ValueError(f"No agent prompt JSON template found for: {agent_name}")
|
|
313
295
|
|
|
314
296
|
# Analyze task complexity if task description is provided
|
|
315
297
|
complexity_analysis = None
|
|
@@ -452,20 +434,17 @@ def list_available_agents() -> Dict[str, Dict[str, Any]]:
|
|
|
452
434
|
List all available agents with their sources.
|
|
453
435
|
|
|
454
436
|
Returns:
|
|
455
|
-
dict: Agent information including
|
|
437
|
+
dict: Agent information including JSON template path
|
|
456
438
|
"""
|
|
457
439
|
agents = {}
|
|
458
440
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
for agent_name, md_filename in AGENT_MAPPINGS.items():
|
|
463
|
-
md_path = framework_agent_roles_dir / md_filename
|
|
441
|
+
for agent_name, json_filename in AGENT_MAPPINGS.items():
|
|
442
|
+
json_path = AGENT_TEMPLATES_DIR / json_filename
|
|
464
443
|
|
|
465
444
|
agents[agent_name] = {
|
|
466
|
-
"
|
|
467
|
-
"
|
|
468
|
-
"
|
|
445
|
+
"json_file": json_filename if json_path.exists() else None,
|
|
446
|
+
"json_path": str(json_path) if json_path.exists() else None,
|
|
447
|
+
"has_template": json_path.exists(),
|
|
469
448
|
"default_model": DEFAULT_AGENT_MODELS.get(agent_name, 'claude-sonnet-4-20250514')
|
|
470
449
|
}
|
|
471
450
|
|
|
@@ -483,13 +462,13 @@ def clear_agent_cache(agent_name: Optional[str] = None) -> None:
|
|
|
483
462
|
cache = SharedPromptCache.get_instance()
|
|
484
463
|
|
|
485
464
|
if agent_name:
|
|
486
|
-
cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}:
|
|
465
|
+
cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}:json"
|
|
487
466
|
cache.invalidate(cache_key)
|
|
488
467
|
logger.debug(f"Cache cleared for agent: {agent_name}")
|
|
489
468
|
else:
|
|
490
469
|
# Clear all agent caches
|
|
491
470
|
for name in AGENT_MAPPINGS:
|
|
492
|
-
cache_key = f"{AGENT_CACHE_PREFIX}{name}:
|
|
471
|
+
cache_key = f"{AGENT_CACHE_PREFIX}{name}:json"
|
|
493
472
|
cache.invalidate(cache_key)
|
|
494
473
|
logger.debug("All agent caches cleared")
|
|
495
474
|
|
|
@@ -506,14 +485,11 @@ def validate_agent_files() -> Dict[str, Dict[str, Any]]:
|
|
|
506
485
|
"""
|
|
507
486
|
results = {}
|
|
508
487
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
for agent_name, md_filename in AGENT_MAPPINGS.items():
|
|
513
|
-
md_path = framework_agent_roles_dir / md_filename
|
|
488
|
+
for agent_name, json_filename in AGENT_MAPPINGS.items():
|
|
489
|
+
json_path = AGENT_TEMPLATES_DIR / json_filename
|
|
514
490
|
results[agent_name] = {
|
|
515
|
-
"
|
|
516
|
-
"
|
|
491
|
+
"template_exists": json_path.exists(),
|
|
492
|
+
"template_path": str(json_path)
|
|
517
493
|
}
|
|
518
494
|
|
|
519
495
|
return results
|
|
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
|
|
|
36
36
|
BASE_AGENT_CACHE_KEY = "base_agent:instructions"
|
|
37
37
|
|
|
38
38
|
def _get_base_agent_file() -> Path:
|
|
39
|
-
"""Get the base agent file path
|
|
39
|
+
"""Get the base agent file path."""
|
|
40
40
|
# Check if we're running from a wheel installation
|
|
41
41
|
try:
|
|
42
42
|
import claude_pm
|
|
@@ -44,27 +44,22 @@ def _get_base_agent_file() -> Path:
|
|
|
44
44
|
path_str = str(package_path.resolve())
|
|
45
45
|
if 'site-packages' in path_str or 'dist-packages' in path_str:
|
|
46
46
|
# For wheel installations, check data directory
|
|
47
|
-
data_base_agent = package_path / "data" / "
|
|
47
|
+
data_base_agent = package_path / "data" / "agents" / "BASE_AGENT_TEMPLATE.md"
|
|
48
48
|
if data_base_agent.exists():
|
|
49
49
|
logger.debug(f"Using wheel installation base_agent: {data_base_agent}")
|
|
50
50
|
return data_base_agent
|
|
51
51
|
except Exception:
|
|
52
52
|
pass
|
|
53
53
|
|
|
54
|
-
#
|
|
55
|
-
|
|
54
|
+
# Use the BASE_AGENT_TEMPLATE.md in the agents directory
|
|
55
|
+
base_agent_path = Path(__file__).parent / "BASE_AGENT_TEMPLATE.md"
|
|
56
|
+
if base_agent_path.exists():
|
|
57
|
+
logger.debug(f"Using base agent template: {base_agent_path}")
|
|
58
|
+
return base_agent_path
|
|
56
59
|
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if framework_base_agent.exists():
|
|
61
|
-
logger.debug(f"Using development base_agent: {framework_base_agent}")
|
|
62
|
-
return framework_base_agent
|
|
63
|
-
|
|
64
|
-
# Fallback to old behavior (though this shouldn't happen)
|
|
65
|
-
fallback = Path(__file__).parent.parent.parent / "framework" / "agent-roles" / "base_agent.md"
|
|
66
|
-
logger.warning(f"Using fallback base_agent path: {fallback}")
|
|
67
|
-
return fallback
|
|
60
|
+
# Fallback error
|
|
61
|
+
logger.error("Base agent template file not found")
|
|
62
|
+
raise FileNotFoundError("BASE_AGENT_TEMPLATE.md not found in agents directory")
|
|
68
63
|
|
|
69
64
|
|
|
70
65
|
# Base agent file path (dynamically determined)
|
claude_mpm/cli.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Command-line interface for Claude MPM."""
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
+
import subprocess
|
|
4
5
|
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Optional
|
|
@@ -9,13 +10,11 @@ try:
|
|
|
9
10
|
# Try relative imports first (when used as package)
|
|
10
11
|
from ._version import __version__
|
|
11
12
|
from .core.logger import get_logger, setup_logging
|
|
12
|
-
from .services.json_rpc_hook_manager import JSONRPCHookManager
|
|
13
13
|
from .constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
|
|
14
14
|
except ImportError:
|
|
15
15
|
# Fall back to absolute imports (when run directly)
|
|
16
16
|
from claude_mpm._version import __version__
|
|
17
17
|
from core.logger import get_logger, setup_logging
|
|
18
|
-
from services.json_rpc_hook_manager import JSONRPCHookManager
|
|
19
18
|
from constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
|
|
20
19
|
|
|
21
20
|
|
|
@@ -99,6 +98,12 @@ def main(argv: Optional[list] = None):
|
|
|
99
98
|
help="Disable hook service (runs without hooks)"
|
|
100
99
|
)
|
|
101
100
|
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
"--intercept-commands",
|
|
103
|
+
action="store_true",
|
|
104
|
+
help="Enable command interception in interactive mode (intercepts /mpm: commands)"
|
|
105
|
+
)
|
|
106
|
+
|
|
102
107
|
# Add run-specific arguments at top level (for default behavior)
|
|
103
108
|
parser.add_argument(
|
|
104
109
|
"--no-tickets",
|
|
@@ -139,6 +144,11 @@ def main(argv: Optional[list] = None):
|
|
|
139
144
|
action="store_true",
|
|
140
145
|
help="Disable automatic ticket creation"
|
|
141
146
|
)
|
|
147
|
+
run_parser.add_argument(
|
|
148
|
+
"--intercept-commands",
|
|
149
|
+
action="store_true",
|
|
150
|
+
help="Enable command interception in interactive mode (intercepts /mpm: commands)"
|
|
151
|
+
)
|
|
142
152
|
run_parser.add_argument(
|
|
143
153
|
"-i", "--input",
|
|
144
154
|
type=str,
|
|
@@ -244,31 +254,10 @@ def main(argv: Optional[list] = None):
|
|
|
244
254
|
logger = logging.getLogger("cli")
|
|
245
255
|
logger.setLevel(logging.WARNING)
|
|
246
256
|
|
|
247
|
-
#
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
257
|
+
# Hook system note: Claude Code hooks are handled externally via the
|
|
258
|
+
# hook_handler.py script installed in ~/.claude/settings.json
|
|
259
|
+
# The --no-hooks flag is kept for backward compatibility but doesn't affect
|
|
260
|
+
# Claude Code hooks which are configured separately.
|
|
272
261
|
|
|
273
262
|
# Default to run command
|
|
274
263
|
if not args.command:
|
|
@@ -289,11 +278,11 @@ def main(argv: Optional[list] = None):
|
|
|
289
278
|
|
|
290
279
|
try:
|
|
291
280
|
if command in [CLICommands.RUN.value, None]:
|
|
292
|
-
run_session(args
|
|
281
|
+
run_session(args)
|
|
293
282
|
elif command == CLICommands.TICKETS.value:
|
|
294
283
|
list_tickets(args)
|
|
295
284
|
elif command == CLICommands.INFO.value:
|
|
296
|
-
show_info(args
|
|
285
|
+
show_info(args)
|
|
297
286
|
elif command == CLICommands.AGENTS.value:
|
|
298
287
|
manage_agents(args)
|
|
299
288
|
elif command == CLICommands.UI.value:
|
|
@@ -311,9 +300,8 @@ def main(argv: Optional[list] = None):
|
|
|
311
300
|
traceback.print_exc()
|
|
312
301
|
return 1
|
|
313
302
|
finally:
|
|
314
|
-
#
|
|
315
|
-
|
|
316
|
-
hook_manager.stop_service()
|
|
303
|
+
# Cleanup handled by individual components
|
|
304
|
+
pass
|
|
317
305
|
|
|
318
306
|
return 0
|
|
319
307
|
|
|
@@ -337,7 +325,7 @@ def _get_user_input(args, logger):
|
|
|
337
325
|
|
|
338
326
|
|
|
339
327
|
|
|
340
|
-
def run_session(args
|
|
328
|
+
def run_session(args):
|
|
341
329
|
"""Run a simplified Claude session."""
|
|
342
330
|
logger = get_logger("cli")
|
|
343
331
|
if args.logging != LogLevel.OFF.value:
|
|
@@ -368,7 +356,17 @@ def run_session(args, hook_manager=None):
|
|
|
368
356
|
logger.error("Session failed")
|
|
369
357
|
else:
|
|
370
358
|
# Run interactive session
|
|
371
|
-
|
|
359
|
+
if getattr(args, 'intercept_commands', False):
|
|
360
|
+
# Use the interactive wrapper for command interception
|
|
361
|
+
wrapper_path = Path(__file__).parent.parent.parent / "scripts" / "interactive_wrapper.py"
|
|
362
|
+
if wrapper_path.exists():
|
|
363
|
+
print("Starting interactive session with command interception...")
|
|
364
|
+
subprocess.run([sys.executable, str(wrapper_path)])
|
|
365
|
+
else:
|
|
366
|
+
logger.warning("Interactive wrapper not found, falling back to normal mode")
|
|
367
|
+
runner.run_interactive(context)
|
|
368
|
+
else:
|
|
369
|
+
runner.run_interactive(context)
|
|
372
370
|
|
|
373
371
|
|
|
374
372
|
def list_tickets(args):
|
|
@@ -579,7 +577,7 @@ def run_terminal_ui(args):
|
|
|
579
577
|
return 0
|
|
580
578
|
|
|
581
579
|
|
|
582
|
-
def show_info(args
|
|
580
|
+
def show_info(args):
|
|
583
581
|
"""Show framework and configuration information."""
|
|
584
582
|
try:
|
|
585
583
|
from .core.framework_loader import FrameworkLoader
|
|
@@ -636,19 +634,15 @@ def show_info(args, hook_manager=None):
|
|
|
636
634
|
except ImportError:
|
|
637
635
|
print(" ✗ ai-trackdown-pytools: Not installed")
|
|
638
636
|
|
|
639
|
-
# Check
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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")
|
|
637
|
+
# Check Claude Code hooks
|
|
638
|
+
from pathlib import Path
|
|
639
|
+
claude_settings = Path.home() / ".claude" / "settings.json"
|
|
640
|
+
if claude_settings.exists():
|
|
641
|
+
print(" ✓ Claude Code Hooks: Installed")
|
|
642
|
+
print(" Use /mpm commands in Claude Code")
|
|
650
643
|
else:
|
|
651
|
-
print(" ✗
|
|
644
|
+
print(" ✗ Claude Code Hooks: Not installed")
|
|
645
|
+
print(" Run: python scripts/install_hooks.py")
|
|
652
646
|
|
|
653
647
|
|
|
654
648
|
if __name__ == "__main__":
|