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.

Files changed (159) hide show
  1. claude_mpm/__init__.py +17 -0
  2. claude_mpm/__main__.py +14 -0
  3. claude_mpm/_version.py +32 -0
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +375 -0
  6. claude_mpm/agents/__init__.py +118 -0
  7. claude_mpm/agents/agent_loader.py +621 -0
  8. claude_mpm/agents/agent_loader_integration.py +229 -0
  9. claude_mpm/agents/agents_metadata.py +204 -0
  10. claude_mpm/agents/base_agent.json +27 -0
  11. claude_mpm/agents/base_agent_loader.py +519 -0
  12. claude_mpm/agents/schema/agent_schema.json +160 -0
  13. claude_mpm/agents/system_agent_config.py +587 -0
  14. claude_mpm/agents/templates/__init__.py +101 -0
  15. claude_mpm/agents/templates/data_engineer_agent.json +46 -0
  16. claude_mpm/agents/templates/documentation_agent.json +45 -0
  17. claude_mpm/agents/templates/engineer_agent.json +49 -0
  18. claude_mpm/agents/templates/ops_agent.json +46 -0
  19. claude_mpm/agents/templates/qa_agent.json +45 -0
  20. claude_mpm/agents/templates/research_agent.json +49 -0
  21. claude_mpm/agents/templates/security_agent.json +46 -0
  22. claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
  23. claude_mpm/agents/templates/version_control_agent.json +46 -0
  24. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
  25. claude_mpm/cli.py +655 -0
  26. claude_mpm/cli_main.py +13 -0
  27. claude_mpm/cli_module/__init__.py +15 -0
  28. claude_mpm/cli_module/args.py +222 -0
  29. claude_mpm/cli_module/commands.py +203 -0
  30. claude_mpm/cli_module/migration_example.py +183 -0
  31. claude_mpm/cli_module/refactoring_guide.md +253 -0
  32. claude_mpm/cli_old/__init__.py +1 -0
  33. claude_mpm/cli_old/ticket_cli.py +102 -0
  34. claude_mpm/config/__init__.py +5 -0
  35. claude_mpm/config/hook_config.py +42 -0
  36. claude_mpm/constants.py +150 -0
  37. claude_mpm/core/__init__.py +45 -0
  38. claude_mpm/core/agent_name_normalizer.py +248 -0
  39. claude_mpm/core/agent_registry.py +627 -0
  40. claude_mpm/core/agent_registry.py.bak +312 -0
  41. claude_mpm/core/agent_session_manager.py +273 -0
  42. claude_mpm/core/base_service.py +747 -0
  43. claude_mpm/core/base_service.py.bak +406 -0
  44. claude_mpm/core/config.py +334 -0
  45. claude_mpm/core/config_aliases.py +292 -0
  46. claude_mpm/core/container.py +347 -0
  47. claude_mpm/core/factories.py +281 -0
  48. claude_mpm/core/framework_loader.py +472 -0
  49. claude_mpm/core/injectable_service.py +206 -0
  50. claude_mpm/core/interfaces.py +539 -0
  51. claude_mpm/core/logger.py +468 -0
  52. claude_mpm/core/minimal_framework_loader.py +107 -0
  53. claude_mpm/core/mixins.py +150 -0
  54. claude_mpm/core/service_registry.py +299 -0
  55. claude_mpm/core/session_manager.py +190 -0
  56. claude_mpm/core/simple_runner.py +511 -0
  57. claude_mpm/core/tool_access_control.py +173 -0
  58. claude_mpm/hooks/README.md +243 -0
  59. claude_mpm/hooks/__init__.py +5 -0
  60. claude_mpm/hooks/base_hook.py +154 -0
  61. claude_mpm/hooks/builtin/__init__.py +1 -0
  62. claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
  63. claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
  64. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
  65. claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
  66. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
  67. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
  68. claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
  69. claude_mpm/hooks/hook_client.py +264 -0
  70. claude_mpm/hooks/hook_runner.py +370 -0
  71. claude_mpm/hooks/json_rpc_executor.py +259 -0
  72. claude_mpm/hooks/json_rpc_hook_client.py +319 -0
  73. claude_mpm/hooks/tool_call_interceptor.py +204 -0
  74. claude_mpm/init.py +246 -0
  75. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
  76. claude_mpm/orchestration/__init__.py +6 -0
  77. claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
  78. claude_mpm/orchestration/archive/factory.py +215 -0
  79. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
  80. claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
  81. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
  82. claude_mpm/orchestration/archive/orchestrator.py +501 -0
  83. claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
  84. claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
  85. claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
  86. claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
  87. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
  88. claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
  89. claude_mpm/scripts/__init__.py +1 -0
  90. claude_mpm/scripts/ticket.py +269 -0
  91. claude_mpm/services/__init__.py +10 -0
  92. claude_mpm/services/agent_deployment.py +955 -0
  93. claude_mpm/services/agent_lifecycle_manager.py +948 -0
  94. claude_mpm/services/agent_management_service.py +596 -0
  95. claude_mpm/services/agent_modification_tracker.py +841 -0
  96. claude_mpm/services/agent_profile_loader.py +606 -0
  97. claude_mpm/services/agent_registry.py +677 -0
  98. claude_mpm/services/base_agent_manager.py +380 -0
  99. claude_mpm/services/framework_agent_loader.py +337 -0
  100. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  101. claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
  102. claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
  103. claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
  105. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
  106. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
  107. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
  108. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
  109. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
  110. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
  111. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
  112. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
  113. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
  114. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
  115. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
  116. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
  117. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
  118. claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
  119. claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
  120. claude_mpm/services/framework_claude_md_generator.py +621 -0
  121. claude_mpm/services/hook_service.py +388 -0
  122. claude_mpm/services/hook_service_manager.py +223 -0
  123. claude_mpm/services/json_rpc_hook_manager.py +92 -0
  124. claude_mpm/services/parent_directory_manager/README.md +83 -0
  125. claude_mpm/services/parent_directory_manager/__init__.py +577 -0
  126. claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
  127. claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
  128. claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
  129. claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
  130. claude_mpm/services/parent_directory_manager/operations.py +186 -0
  131. claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
  132. claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
  133. claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
  134. claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
  135. claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
  136. claude_mpm/services/shared_prompt_cache.py +819 -0
  137. claude_mpm/services/ticket_manager.py +213 -0
  138. claude_mpm/services/ticket_manager_di.py +318 -0
  139. claude_mpm/services/ticketing_service_original.py +508 -0
  140. claude_mpm/services/version_control/VERSION +1 -0
  141. claude_mpm/services/version_control/__init__.py +70 -0
  142. claude_mpm/services/version_control/branch_strategy.py +670 -0
  143. claude_mpm/services/version_control/conflict_resolution.py +744 -0
  144. claude_mpm/services/version_control/git_operations.py +784 -0
  145. claude_mpm/services/version_control/semantic_versioning.py +703 -0
  146. claude_mpm/ui/__init__.py +1 -0
  147. claude_mpm/ui/rich_terminal_ui.py +295 -0
  148. claude_mpm/ui/terminal_ui.py +328 -0
  149. claude_mpm/utils/__init__.py +16 -0
  150. claude_mpm/utils/config_manager.py +468 -0
  151. claude_mpm/utils/import_migration_example.py +80 -0
  152. claude_mpm/utils/imports.py +182 -0
  153. claude_mpm/utils/path_operations.py +357 -0
  154. claude_mpm/utils/paths.py +289 -0
  155. claude_mpm-0.3.0.dist-info/METADATA +290 -0
  156. claude_mpm-0.3.0.dist-info/RECORD +159 -0
  157. claude_mpm-0.3.0.dist-info/WHEEL +5 -0
  158. claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
  159. claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,213 @@
1
+ """Ticket management using ai-trackdown-pytools."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional, Dict, Any, List
5
+ from datetime import datetime
6
+
7
+ try:
8
+ from ..core.logger import get_logger
9
+ except ImportError:
10
+ from core.logger import get_logger
11
+
12
+
13
+ class TicketManager:
14
+ """
15
+ Manage ticket creation using ai-trackdown-pytools.
16
+
17
+ This wraps the ai-trackdown-pytools API for creating tickets
18
+ in the standard tickets/ directory structure.
19
+ """
20
+
21
+ def __init__(self, project_path: Optional[Path] = None):
22
+ """
23
+ Initialize ticket manager.
24
+
25
+ Args:
26
+ project_path: Project root (defaults to current directory)
27
+ """
28
+ self.logger = get_logger("ticket_manager")
29
+ self.project_path = project_path or Path.cwd()
30
+ self.task_manager = self._init_task_manager()
31
+
32
+ def _init_task_manager(self):
33
+ """Initialize ai-trackdown-pytools TaskManager."""
34
+ try:
35
+ from ai_trackdown_pytools.core.task import TaskManager
36
+ from ai_trackdown_pytools import Config, Project
37
+
38
+ # First, ensure tickets directory exists
39
+ tickets_dir = self.project_path / "tickets"
40
+ if not tickets_dir.exists():
41
+ tickets_dir.mkdir(exist_ok=True)
42
+ (tickets_dir / "epics").mkdir(exist_ok=True)
43
+ (tickets_dir / "issues").mkdir(exist_ok=True)
44
+ (tickets_dir / "tasks").mkdir(exist_ok=True)
45
+ self.logger.info(f"Created tickets directory structure at: {tickets_dir}")
46
+
47
+ # Check if we need to configure ai-trackdown
48
+ config_file = self.project_path / ".trackdown.yaml"
49
+ if not config_file.exists():
50
+ # Create default config that uses tickets/ directory
51
+ config = Config.create_default(config_file)
52
+ config.set("paths.tickets_dir", "tickets")
53
+ config.set("paths.epics_dir", "tickets/epics")
54
+ config.set("paths.issues_dir", "tickets/issues")
55
+ config.set("paths.tasks_dir", "tickets/tasks")
56
+ config.save()
57
+ self.logger.info("Created .trackdown.yaml configuration")
58
+
59
+ # Initialize TaskManager directly with the project path
60
+ # TaskManager will handle project initialization internally
61
+ task_manager = TaskManager(self.project_path)
62
+
63
+ # Verify it's using the right directory
64
+ if hasattr(task_manager, 'tasks_dir'):
65
+ self.logger.info(f"TaskManager using tasks directory: {task_manager.tasks_dir}")
66
+ else:
67
+ self.logger.info(f"Initialized TaskManager for: {self.project_path}")
68
+
69
+ return task_manager
70
+
71
+ except ImportError:
72
+ self.logger.error("ai-trackdown-pytools not installed")
73
+ self.logger.info("Install with: pip install ai-trackdown-pytools")
74
+ return None
75
+ except Exception as e:
76
+ self.logger.error(f"Failed to initialize TaskManager: {e}")
77
+ self.logger.debug(f"Error details: {str(e)}", exc_info=True)
78
+ return None
79
+
80
+ def create_ticket(
81
+ self,
82
+ title: str,
83
+ ticket_type: str = "task",
84
+ description: str = "",
85
+ priority: str = "medium",
86
+ tags: Optional[List[str]] = None,
87
+ source: str = "claude-mpm",
88
+ parent_epic: Optional[str] = None,
89
+ parent_issue: Optional[str] = None,
90
+ **kwargs
91
+ ) -> Optional[str]:
92
+ """
93
+ Create a ticket using ai-trackdown-pytools.
94
+
95
+ Args:
96
+ title: Ticket title
97
+ ticket_type: Type (task, bug, feature, etc.)
98
+ description: Detailed description
99
+ priority: Priority level (low, medium, high)
100
+ tags: List of tags/labels
101
+ source: Source identifier
102
+ **kwargs: Additional metadata
103
+
104
+ Returns:
105
+ Ticket ID if created, None on failure
106
+ """
107
+ if not self.task_manager:
108
+ self.logger.error("TaskManager not available")
109
+ return None
110
+
111
+ try:
112
+ # Prepare tags
113
+ if tags is None:
114
+ tags = []
115
+
116
+ # Add type and source tags
117
+ tags.extend([ticket_type, f"source:{source}", "auto-extracted"])
118
+
119
+ # Remove duplicates
120
+ tags = list(set(tags))
121
+
122
+ # Prepare task data
123
+ task_data = {
124
+ 'title': title,
125
+ 'description': description or f"Auto-extracted {ticket_type} from Claude MPM session",
126
+ 'status': 'open',
127
+ 'priority': priority.lower(),
128
+ 'assignees': [],
129
+ 'tags': tags,
130
+ 'metadata': {
131
+ 'source': source,
132
+ 'ticket_type': ticket_type,
133
+ 'created_by': 'claude-mpm',
134
+ 'extracted_at': datetime.now().isoformat(),
135
+ **kwargs
136
+ }
137
+ }
138
+
139
+ # Create the task
140
+ task = self.task_manager.create_task(**task_data)
141
+
142
+ self.logger.info(f"Created ticket: {task.id} - {title}")
143
+ return task.id
144
+
145
+ except Exception as e:
146
+ self.logger.error(f"Failed to create ticket: {e}")
147
+ return None
148
+
149
+ def list_recent_tickets(self, limit: int = 10) -> List[Dict[str, Any]]:
150
+ """
151
+ List recent tickets.
152
+
153
+ Args:
154
+ limit: Maximum number of tickets to return
155
+
156
+ Returns:
157
+ List of ticket summaries
158
+ """
159
+ if not self.task_manager:
160
+ return []
161
+
162
+ try:
163
+ tasks = self.task_manager.get_recent_tasks(limit=limit)
164
+
165
+ tickets = []
166
+ for task in tasks:
167
+ tickets.append({
168
+ 'id': task.id,
169
+ 'title': task.title,
170
+ 'status': task.status,
171
+ 'priority': task.priority,
172
+ 'tags': task.tags,
173
+ 'created_at': task.created_at,
174
+ })
175
+
176
+ return tickets
177
+
178
+ except Exception as e:
179
+ self.logger.error(f"Failed to list tickets: {e}")
180
+ return []
181
+
182
+ def get_ticket(self, ticket_id: str) -> Optional[Dict[str, Any]]:
183
+ """
184
+ Get a specific ticket.
185
+
186
+ Args:
187
+ ticket_id: Ticket ID
188
+
189
+ Returns:
190
+ Ticket data or None
191
+ """
192
+ if not self.task_manager:
193
+ return None
194
+
195
+ try:
196
+ task = self.task_manager.load_task(ticket_id)
197
+
198
+ return {
199
+ 'id': task.id,
200
+ 'title': task.title,
201
+ 'description': task.description,
202
+ 'status': task.status,
203
+ 'priority': task.priority,
204
+ 'tags': task.tags,
205
+ 'assignees': task.assignees,
206
+ 'created_at': task.created_at,
207
+ 'updated_at': task.updated_at,
208
+ 'metadata': task.metadata,
209
+ }
210
+
211
+ except Exception as e:
212
+ self.logger.error(f"Failed to get ticket {ticket_id}: {e}")
213
+ return None
@@ -0,0 +1,318 @@
1
+ """
2
+ Enhanced Ticket Manager with Dependency Injection support.
3
+
4
+ This version demonstrates proper constructor injection and testability.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Optional, Dict, Any, List
9
+ from datetime import datetime
10
+
11
+ from ..core.injectable_service import InjectableService
12
+ from ..core.config import Config
13
+ from ..core.logger import get_logger
14
+
15
+
16
+ class ITaskManagerAdapter:
17
+ """Interface for task manager adapter."""
18
+
19
+ def create_task(self, **kwargs) -> Any:
20
+ """Create a task."""
21
+ raise NotImplementedError
22
+
23
+ def get_recent_tasks(self, limit: int) -> List[Any]:
24
+ """Get recent tasks."""
25
+ raise NotImplementedError
26
+
27
+ def load_task(self, task_id: str) -> Any:
28
+ """Load a specific task."""
29
+ raise NotImplementedError
30
+
31
+
32
+ class AITrackdownAdapter(ITaskManagerAdapter):
33
+ """Adapter for ai-trackdown-pytools."""
34
+
35
+ def __init__(self, project_path: Path):
36
+ """Initialize the adapter."""
37
+ self.project_path = project_path
38
+ self.logger = get_logger("ai_trackdown_adapter")
39
+ self._task_manager = self._init_task_manager()
40
+
41
+ def _init_task_manager(self):
42
+ """Initialize ai-trackdown-pytools TaskManager."""
43
+ try:
44
+ from ai_trackdown_pytools.core.task import TaskManager
45
+ from ai_trackdown_pytools import Config as TrackdownConfig, Project
46
+
47
+ # Ensure tickets directory exists
48
+ tickets_dir = self.project_path / "tickets"
49
+ if not tickets_dir.exists():
50
+ tickets_dir.mkdir(exist_ok=True)
51
+ (tickets_dir / "epics").mkdir(exist_ok=True)
52
+ (tickets_dir / "issues").mkdir(exist_ok=True)
53
+ (tickets_dir / "tasks").mkdir(exist_ok=True)
54
+ self.logger.info(f"Created tickets directory structure at: {tickets_dir}")
55
+
56
+ # Configure ai-trackdown if needed
57
+ config_file = self.project_path / ".trackdown.yaml"
58
+ if not config_file.exists():
59
+ config = TrackdownConfig.create_default(config_file)
60
+ config.set("paths.tickets_dir", "tickets")
61
+ config.set("paths.epics_dir", "tickets/epics")
62
+ config.set("paths.issues_dir", "tickets/issues")
63
+ config.set("paths.tasks_dir", "tickets/tasks")
64
+ config.save()
65
+ self.logger.info("Created .trackdown.yaml configuration")
66
+
67
+ # Initialize task manager directly
68
+ return TaskManager(self.project_path)
69
+
70
+ except ImportError:
71
+ self.logger.error("ai-trackdown-pytools not installed")
72
+ return None
73
+ except Exception as e:
74
+ self.logger.error(f"Failed to initialize TaskManager: {e}")
75
+ return None
76
+
77
+ def create_task(self, **kwargs) -> Any:
78
+ """Create a task."""
79
+ if not self._task_manager:
80
+ raise RuntimeError("Task manager not available")
81
+ return self._task_manager.create_task(**kwargs)
82
+
83
+ def get_recent_tasks(self, limit: int) -> List[Any]:
84
+ """Get recent tasks."""
85
+ if not self._task_manager:
86
+ return []
87
+ return self._task_manager.get_recent_tasks(limit=limit)
88
+
89
+ def load_task(self, task_id: str) -> Any:
90
+ """Load a specific task."""
91
+ if not self._task_manager:
92
+ raise RuntimeError("Task manager not available")
93
+ return self._task_manager.load_task(task_id)
94
+
95
+
96
+ class TicketManagerDI(InjectableService):
97
+ """
98
+ Enhanced Ticket Manager with Dependency Injection.
99
+
100
+ This version demonstrates:
101
+ - Constructor injection of dependencies
102
+ - Interface-based design for testability
103
+ - Configuration injection
104
+ - Easy mocking for tests
105
+ """
106
+
107
+ # Type annotations for dependency injection
108
+ config: Config
109
+ task_adapter: Optional[ITaskManagerAdapter]
110
+
111
+ def __init__(
112
+ self,
113
+ name: str = "ticket_manager",
114
+ config: Optional[Config] = None,
115
+ task_adapter: Optional[ITaskManagerAdapter] = None,
116
+ project_path: Optional[Path] = None,
117
+ **kwargs
118
+ ):
119
+ """
120
+ Initialize ticket manager with dependency injection.
121
+
122
+ Args:
123
+ name: Service name
124
+ config: Configuration service (injected)
125
+ task_adapter: Task manager adapter (injected)
126
+ project_path: Project path override
127
+ **kwargs: Additional arguments for base class
128
+ """
129
+ # Call parent constructor
130
+ super().__init__(name=name, config=config, **kwargs)
131
+
132
+ # Use injected adapter or create default
133
+ if task_adapter:
134
+ self.task_adapter = task_adapter
135
+ else:
136
+ # Get project path from config or parameter
137
+ if project_path is None:
138
+ project_path = Path(self.config.get('project.path', '.'))
139
+ self.task_adapter = AITrackdownAdapter(project_path)
140
+
141
+ self.logger.info(f"Initialized {name} with DI support")
142
+
143
+ async def _initialize(self) -> None:
144
+ """Initialize the service."""
145
+ self.logger.info("TicketManager service initialized")
146
+
147
+ async def _cleanup(self) -> None:
148
+ """Cleanup service resources."""
149
+ self.logger.info("TicketManager service cleaned up")
150
+
151
+ def create_ticket(
152
+ self,
153
+ title: str,
154
+ ticket_type: str = "task",
155
+ description: str = "",
156
+ priority: str = "medium",
157
+ tags: Optional[List[str]] = None,
158
+ source: str = "claude-mpm",
159
+ parent_epic: Optional[str] = None,
160
+ parent_issue: Optional[str] = None,
161
+ **kwargs
162
+ ) -> Optional[str]:
163
+ """
164
+ Create a ticket.
165
+
166
+ Args:
167
+ title: Ticket title
168
+ ticket_type: Type (task, bug, feature, etc.)
169
+ description: Detailed description
170
+ priority: Priority level
171
+ tags: List of tags
172
+ source: Source identifier
173
+ **kwargs: Additional metadata
174
+
175
+ Returns:
176
+ Ticket ID if created, None on failure
177
+ """
178
+ if not self.task_adapter:
179
+ self.logger.error("Task adapter not available")
180
+ return None
181
+
182
+ try:
183
+ # Prepare tags
184
+ if tags is None:
185
+ tags = []
186
+
187
+ # Add standard tags
188
+ tags.extend([ticket_type, f"source:{source}", "auto-extracted"])
189
+ tags = list(set(tags)) # Remove duplicates
190
+
191
+ # Prepare task data
192
+ task_data = {
193
+ 'title': title,
194
+ 'description': description or f"Auto-extracted {ticket_type}",
195
+ 'status': 'open',
196
+ 'priority': priority.lower(),
197
+ 'assignees': [],
198
+ 'tags': tags,
199
+ 'metadata': {
200
+ 'source': source,
201
+ 'ticket_type': ticket_type,
202
+ 'created_by': 'claude-mpm',
203
+ 'extracted_at': datetime.now().isoformat(),
204
+ **kwargs
205
+ }
206
+ }
207
+
208
+ # Add parent references if provided
209
+ if parent_epic:
210
+ task_data['metadata']['parent_epic'] = parent_epic
211
+ if parent_issue:
212
+ task_data['metadata']['parent_issue'] = parent_issue
213
+
214
+ # Create the task
215
+ task = self.task_adapter.create_task(**task_data)
216
+
217
+ # Update metrics
218
+ self.update_metrics(tickets_created=self._metrics.custom_metrics.get('tickets_created', 0) + 1)
219
+
220
+ self.logger.info(f"Created ticket: {task.id} - {title}")
221
+ return task.id
222
+
223
+ except Exception as e:
224
+ self.logger.error(f"Failed to create ticket: {e}")
225
+ self.update_metrics(tickets_failed=self._metrics.custom_metrics.get('tickets_failed', 0) + 1)
226
+ return None
227
+
228
+ def list_recent_tickets(self, limit: int = 10) -> List[Dict[str, Any]]:
229
+ """
230
+ List recent tickets.
231
+
232
+ Args:
233
+ limit: Maximum number of tickets
234
+
235
+ Returns:
236
+ List of ticket summaries
237
+ """
238
+ if not self.task_adapter:
239
+ return []
240
+
241
+ try:
242
+ tasks = self.task_adapter.get_recent_tasks(limit=limit)
243
+
244
+ tickets = []
245
+ for task in tasks:
246
+ tickets.append({
247
+ 'id': task.id,
248
+ 'title': task.title,
249
+ 'status': task.status,
250
+ 'priority': task.priority,
251
+ 'tags': task.tags,
252
+ 'created_at': task.created_at,
253
+ })
254
+
255
+ return tickets
256
+
257
+ except Exception as e:
258
+ self.logger.error(f"Failed to list tickets: {e}")
259
+ return []
260
+
261
+ def get_ticket(self, ticket_id: str) -> Optional[Dict[str, Any]]:
262
+ """
263
+ Get a specific ticket.
264
+
265
+ Args:
266
+ ticket_id: Ticket ID
267
+
268
+ Returns:
269
+ Ticket data or None
270
+ """
271
+ if not self.task_adapter:
272
+ return None
273
+
274
+ try:
275
+ task = self.task_adapter.load_task(ticket_id)
276
+
277
+ return {
278
+ 'id': task.id,
279
+ 'title': task.title,
280
+ 'description': task.description,
281
+ 'status': task.status,
282
+ 'priority': task.priority,
283
+ 'tags': task.tags,
284
+ 'assignees': task.assignees,
285
+ 'created_at': task.created_at,
286
+ 'updated_at': task.updated_at,
287
+ 'metadata': task.metadata,
288
+ }
289
+
290
+ except Exception as e:
291
+ self.logger.error(f"Failed to get ticket {ticket_id}: {e}")
292
+ return None
293
+
294
+ async def _health_check(self) -> Dict[str, bool]:
295
+ """Perform custom health checks."""
296
+ checks = {
297
+ 'task_adapter_available': self.task_adapter is not None,
298
+ }
299
+
300
+ # Check if we can access the ticket directory
301
+ try:
302
+ project_path = Path(self.config.get('project.path', '.'))
303
+ tickets_dir = project_path / "tickets"
304
+ checks['tickets_directory_accessible'] = tickets_dir.exists()
305
+ except Exception:
306
+ checks['tickets_directory_accessible'] = False
307
+
308
+ return checks
309
+
310
+ async def _collect_custom_metrics(self) -> None:
311
+ """Collect custom metrics."""
312
+ # Count tickets if possible
313
+ try:
314
+ if self.task_adapter:
315
+ recent_tickets = self.task_adapter.get_recent_tasks(limit=100)
316
+ self._metrics.custom_metrics['total_tickets'] = len(recent_tickets)
317
+ except Exception as e:
318
+ self.logger.warning(f"Failed to collect ticket metrics: {e}")