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.
Files changed (38) hide show
  1. claude_mpm/_version.py +3 -2
  2. claude_mpm/agents/__init__.py +2 -2
  3. claude_mpm/agents/agent-template.yaml +83 -0
  4. claude_mpm/agents/agent_loader.py +66 -90
  5. claude_mpm/agents/base_agent_loader.py +10 -15
  6. claude_mpm/cli.py +41 -47
  7. claude_mpm/cli_enhancements.py +297 -0
  8. claude_mpm/core/factories.py +1 -46
  9. claude_mpm/core/service_registry.py +0 -8
  10. claude_mpm/core/simple_runner.py +43 -0
  11. claude_mpm/generators/__init__.py +5 -0
  12. claude_mpm/generators/agent_profile_generator.py +137 -0
  13. claude_mpm/hooks/README.md +75 -221
  14. claude_mpm/hooks/builtin/mpm_command_hook.py +125 -0
  15. claude_mpm/hooks/claude_hooks/__init__.py +5 -0
  16. claude_mpm/hooks/claude_hooks/hook_handler.py +399 -0
  17. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +47 -0
  18. claude_mpm/hooks/validation_hooks.py +181 -0
  19. claude_mpm/services/agent_management_service.py +4 -4
  20. claude_mpm/services/agent_profile_loader.py +1 -1
  21. claude_mpm/services/agent_registry.py +0 -1
  22. claude_mpm/services/base_agent_manager.py +3 -3
  23. claude_mpm/utils/error_handler.py +247 -0
  24. claude_mpm/validation/__init__.py +5 -0
  25. claude_mpm/validation/agent_validator.py +175 -0
  26. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/METADATA +44 -7
  27. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/RECORD +30 -26
  28. claude_mpm/config/hook_config.py +0 -42
  29. claude_mpm/hooks/hook_client.py +0 -264
  30. claude_mpm/hooks/hook_runner.py +0 -370
  31. claude_mpm/hooks/json_rpc_executor.py +0 -259
  32. claude_mpm/hooks/json_rpc_hook_client.py +0 -319
  33. claude_mpm/services/hook_service.py +0 -388
  34. claude_mpm/services/hook_service_manager.py +0 -223
  35. claude_mpm/services/json_rpc_hook_manager.py +0 -92
  36. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/WHEEL +0 -0
  37. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/entry_points.txt +0 -0
  38. {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.0.0"
9
+ __version__ = "1.1.0"
10
10
  except ImportError:
11
11
  # Fallback for older Python versions
12
- __version__ = "1.0.0"
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
@@ -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 framework/agent-roles/*.md files
8
- with fallback to Python constants for backward compatibility.
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 framework markdown files.
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 framework/agent-roles/*.md files
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 MD file
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 _get_framework_agent_roles_dir() -> Path:
53
- """Get the framework agent-roles directory dynamically."""
54
- # Use PathResolver for consistent path discovery
55
- try:
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
- # Framework agent-roles directory (dynamically determined)
92
- FRAMEWORK_AGENT_ROLES_DIR = _get_framework_agent_roles_dir()
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 -> MD file name)
64
+ # Agent name mappings (agent name -> JSON file name)
98
65
  AGENT_MAPPINGS = {
99
- "documentation": "documentation-agent.md",
100
- "version_control": "version-control-agent.md",
101
- "qa": "qa-agent.md",
102
- "research": "research-agent.md",
103
- "ops": "ops-agent.md",
104
- "security": "security-agent.md",
105
- "engineer": "engineer-agent.md",
106
- "data_engineer": "data-agent.md", # Note: data-agent.md maps to data_engineer
107
- "pm": "pm-orchestrator-agent.md",
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 framework markdown file.
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 MD file, or None if not found
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}:md"
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 MD file path
167
- md_filename = AGENT_MAPPINGS.get(agent_name)
168
- if not md_filename:
169
- logger.warning(f"No MD file mapping found for agent: {agent_name}")
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
- # Always get fresh framework directory path to ensure we're using the right location
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 md_path.exists():
178
- logger.warning(f"Agent MD file not found: {md_path}")
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: {md_path}")
182
- content = md_path.read_text(encoding='utf-8')
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 MD for '{agent_name}': {e}")
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 MD file with optional dynamic model selection.
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 MD file
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 MD file found for: {agent_name}")
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 MD file path
437
+ dict: Agent information including JSON template path
456
438
  """
457
439
  agents = {}
458
440
 
459
- # Get fresh framework directory path
460
- framework_agent_roles_dir = _get_framework_agent_roles_dir()
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
- "md_file": md_filename if md_path.exists() else None,
467
- "md_path": str(md_path) if md_path.exists() else None,
468
- "has_md": md_path.exists(),
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}:md"
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}:md"
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
- # Get fresh framework directory path
510
- framework_agent_roles_dir = _get_framework_agent_roles_dir()
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
- "md_exists": md_path.exists(),
516
- "md_path": str(md_path)
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 dynamically."""
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" / "framework" / "agent-roles" / "base_agent.md"
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
- # For development, find framework directory
55
- current = Path.cwd()
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
- # Check current directory and parents
58
- for path in [current] + list(current.parents):
59
- framework_base_agent = path / "framework" / "agent-roles" / "base_agent.md"
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
- # 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
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, hook_manager)
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, hook_manager)
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
- # Clean up hook service
315
- if hook_manager:
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, hook_manager=None):
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
- runner.run_interactive(context)
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, hook_manager=None):
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 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")
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(" ✗ Hook System: Disabled (--no-hooks)")
644
+ print(" ✗ Claude Code Hooks: Not installed")
645
+ print(" Run: python scripts/install_hooks.py")
652
646
 
653
647
 
654
648
  if __name__ == "__main__":