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,508 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Ticketing Service
4
+ =================
5
+
6
+ Core service that wraps ai-trackdown-pytools for simplified ticket management.
7
+ Provides a clean interface for PM orchestration and agent ticket operations.
8
+
9
+ Key Features:
10
+ - Singleton pattern for consistent ticket management
11
+ - Simplified API wrapping ai-trackdown-pytools
12
+ - Automatic ticket directory management
13
+ - Thread-safe operations
14
+ - Comprehensive error handling and logging
15
+ - Integration with Claude PM Framework
16
+
17
+ Usage:
18
+ from claude_pm.services.ticketing_service import TicketingService
19
+
20
+ # Get singleton instance
21
+ ticketing = TicketingService.get_instance()
22
+
23
+ # Create a ticket
24
+ ticket = ticketing.create_ticket(
25
+ title="Implement new feature",
26
+ description="Detailed description",
27
+ priority="high"
28
+ )
29
+
30
+ # List tickets
31
+ tickets = ticketing.list_tickets(status="open")
32
+
33
+ # Update ticket
34
+ ticketing.update_ticket("CLAUDE-001", status="in_progress")
35
+ """
36
+
37
+ import asyncio
38
+ import json
39
+ import logging
40
+ import os
41
+ import threading
42
+ from dataclasses import dataclass, field
43
+ from datetime import datetime
44
+ from pathlib import Path
45
+ from typing import Any, Dict, List, Optional, Union
46
+
47
+ # Import ai-trackdown-pytools
48
+ try:
49
+ from ai_trackdown_pytools import Task, Project
50
+ from ai_trackdown_pytools.core.task import TaskManager
51
+ AI_TRACKDOWN_AVAILABLE = True
52
+ # Map to expected names
53
+ Ticket = Task
54
+ TicketManager = TaskManager
55
+ except ImportError:
56
+ AI_TRACKDOWN_AVAILABLE = False
57
+ # Define fallback classes for type hints
58
+ class TicketStatus:
59
+ OPEN = "open"
60
+ IN_PROGRESS = "in_progress"
61
+ RESOLVED = "resolved"
62
+ CLOSED = "closed"
63
+
64
+ class TicketPriority:
65
+ LOW = "low"
66
+ MEDIUM = "medium"
67
+ HIGH = "high"
68
+ CRITICAL = "critical"
69
+
70
+ import logging
71
+
72
+ logger = logging.getLogger(__name__)
73
+
74
+
75
+ @dataclass
76
+ class TicketData:
77
+ """Simplified ticket data structure for easy use."""
78
+ id: str
79
+ title: str
80
+ description: str
81
+ status: str = "open"
82
+ priority: str = "medium"
83
+ assignee: Optional[str] = None
84
+ labels: List[str] = field(default_factory=list)
85
+ created_at: Optional[datetime] = None
86
+ updated_at: Optional[datetime] = None
87
+ metadata: Dict[str, Any] = field(default_factory=dict)
88
+
89
+
90
+ class TicketingService:
91
+ """
92
+ Core ticketing service wrapping ai-trackdown-pytools.
93
+
94
+ Provides simplified interface for ticket management operations
95
+ within the Claude PM Framework.
96
+ """
97
+
98
+ _instance = None
99
+ _lock = threading.Lock()
100
+
101
+ def __new__(cls):
102
+ """Implement singleton pattern."""
103
+ if cls._instance is None:
104
+ with cls._lock:
105
+ if cls._instance is None:
106
+ cls._instance = super().__new__(cls)
107
+ return cls._instance
108
+
109
+ def __init__(self):
110
+ """Initialize the ticketing service."""
111
+ if not hasattr(self, '_initialized'):
112
+ self._initialized = True
113
+ self._ticket_manager = None
114
+ self._tickets_dir = None
115
+ self._setup_service()
116
+
117
+ @classmethod
118
+ def get_instance(cls) -> 'TicketingService':
119
+ """Get singleton instance of TicketingService."""
120
+ return cls()
121
+
122
+ def _setup_service(self):
123
+ """Set up the ticketing service."""
124
+ try:
125
+ # Find or create tickets directory
126
+ self._tickets_dir = self._find_tickets_directory()
127
+
128
+ if AI_TRACKDOWN_AVAILABLE:
129
+ # Initialize ai-trackdown ticket manager
130
+ # TaskManager expects project_path (parent of tickets directory)
131
+ project_path = self._tickets_dir.parent
132
+ self._ticket_manager = TicketManager(project_path)
133
+ logger.info(f"Ticketing service initialized with project path: {project_path}")
134
+ else:
135
+ logger.warning("ai-trackdown-pytools not available, using stub implementation")
136
+
137
+ except Exception as e:
138
+ logger.error(f"Failed to initialize ticketing service: {e}")
139
+ raise
140
+
141
+ def _find_tickets_directory(self) -> Path:
142
+ """Find or create the tickets directory."""
143
+ # Check current directory first
144
+ current_dir = Path.cwd()
145
+ tickets_path = current_dir / "tickets"
146
+
147
+ if tickets_path.exists():
148
+ return tickets_path
149
+
150
+ # Check for .claude-pm directory
151
+ claude_pm_dir = current_dir / ".claude-pm"
152
+ if claude_pm_dir.exists():
153
+ tickets_path = claude_pm_dir / "tickets"
154
+ tickets_path.mkdir(exist_ok=True)
155
+ return tickets_path
156
+
157
+ # Create in current directory
158
+ tickets_path.mkdir(exist_ok=True)
159
+ return tickets_path
160
+
161
+ # Core ticket operations
162
+
163
+ def create_ticket(
164
+ self,
165
+ title: str,
166
+ description: str,
167
+ priority: str = "medium",
168
+ assignee: Optional[str] = None,
169
+ labels: Optional[List[str]] = None,
170
+ metadata: Optional[Dict[str, Any]] = None
171
+ ) -> TicketData:
172
+ """
173
+ Create a new ticket.
174
+
175
+ Args:
176
+ title: Ticket title
177
+ description: Detailed description
178
+ priority: Priority level (low, medium, high, critical)
179
+ assignee: Optional assignee
180
+ labels: Optional list of labels
181
+ metadata: Optional metadata dictionary
182
+
183
+ Returns:
184
+ TicketData object with created ticket information
185
+ """
186
+ try:
187
+ if not AI_TRACKDOWN_AVAILABLE:
188
+ # Stub implementation
189
+ ticket_id = f"CLAUDE-{datetime.now().strftime('%Y%m%d%H%M%S')}"
190
+ return TicketData(
191
+ id=ticket_id,
192
+ title=title,
193
+ description=description,
194
+ status="open",
195
+ priority=priority,
196
+ assignee=assignee,
197
+ labels=labels or [],
198
+ created_at=datetime.now(),
199
+ updated_at=datetime.now(),
200
+ metadata=metadata or {}
201
+ )
202
+
203
+ # Use ai-trackdown to create task
204
+ # create_task expects: title, description, status, priority, assignees, tags, metadata
205
+ task_data = {
206
+ 'title': title,
207
+ 'description': description,
208
+ 'status': 'open',
209
+ 'priority': priority.lower(),
210
+ 'assignees': [assignee] if assignee else [],
211
+ 'tags': labels or [],
212
+ 'metadata': metadata or {}
213
+ }
214
+
215
+ ticket = self._ticket_manager.create_task(**task_data)
216
+
217
+ return self._convert_to_ticket_data(ticket)
218
+
219
+ except Exception as e:
220
+ logger.error(f"Failed to create ticket: {e}")
221
+ raise
222
+
223
+ def get_ticket(self, ticket_id: str) -> Optional[TicketData]:
224
+ """
225
+ Get a ticket by ID.
226
+
227
+ Args:
228
+ ticket_id: Ticket identifier
229
+
230
+ Returns:
231
+ TicketData object or None if not found
232
+ """
233
+ try:
234
+ if not AI_TRACKDOWN_AVAILABLE:
235
+ return None
236
+
237
+ ticket = self._ticket_manager.get_ticket(ticket_id)
238
+ if ticket:
239
+ return self._convert_to_ticket_data(ticket)
240
+ return None
241
+
242
+ except Exception as e:
243
+ logger.error(f"Failed to get ticket {ticket_id}: {e}")
244
+ return None
245
+
246
+ def list_tickets(
247
+ self,
248
+ status: Optional[str] = None,
249
+ priority: Optional[str] = None,
250
+ assignee: Optional[str] = None,
251
+ labels: Optional[List[str]] = None,
252
+ limit: Optional[int] = None
253
+ ) -> List[TicketData]:
254
+ """
255
+ List tickets with optional filters.
256
+
257
+ Args:
258
+ status: Filter by status (open, in_progress, resolved, closed)
259
+ priority: Filter by priority (low, medium, high, critical)
260
+ assignee: Filter by assignee
261
+ labels: Filter by labels (tickets must have all specified labels)
262
+ limit: Maximum number of tickets to return
263
+
264
+ Returns:
265
+ List of TicketData objects
266
+ """
267
+ try:
268
+ if not AI_TRACKDOWN_AVAILABLE:
269
+ return []
270
+
271
+ # Build filter criteria
272
+ filters = {}
273
+ if status:
274
+ filters['status'] = getattr(TicketStatus, status.upper(), None)
275
+ if priority:
276
+ filters['priority'] = getattr(TicketPriority, priority.upper(), None)
277
+ if assignee:
278
+ filters['assignee'] = assignee
279
+ if labels:
280
+ filters['labels'] = labels
281
+
282
+ tickets = self._ticket_manager.list_tickets(**filters)
283
+
284
+ # Convert and limit results
285
+ result = [self._convert_to_ticket_data(t) for t in tickets]
286
+ if limit:
287
+ result = result[:limit]
288
+
289
+ return result
290
+
291
+ except Exception as e:
292
+ logger.error(f"Failed to list tickets: {e}")
293
+ return []
294
+
295
+ def update_ticket(
296
+ self,
297
+ ticket_id: str,
298
+ title: Optional[str] = None,
299
+ description: Optional[str] = None,
300
+ status: Optional[str] = None,
301
+ priority: Optional[str] = None,
302
+ assignee: Optional[str] = None,
303
+ labels: Optional[List[str]] = None,
304
+ metadata: Optional[Dict[str, Any]] = None
305
+ ) -> Optional[TicketData]:
306
+ """
307
+ Update an existing ticket.
308
+
309
+ Args:
310
+ ticket_id: Ticket identifier
311
+ title: New title (optional)
312
+ description: New description (optional)
313
+ status: New status (optional)
314
+ priority: New priority (optional)
315
+ assignee: New assignee (optional)
316
+ labels: New labels (optional, replaces existing)
317
+ metadata: New metadata (optional, merges with existing)
318
+
319
+ Returns:
320
+ Updated TicketData object or None if not found
321
+ """
322
+ try:
323
+ if not AI_TRACKDOWN_AVAILABLE:
324
+ return None
325
+
326
+ # Build update data
327
+ updates = {}
328
+ if title is not None:
329
+ updates['title'] = title
330
+ if description is not None:
331
+ updates['description'] = description
332
+ if status is not None:
333
+ updates['status'] = getattr(TicketStatus, status.upper(), None)
334
+ if priority is not None:
335
+ updates['priority'] = getattr(TicketPriority, priority.upper(), None)
336
+ if assignee is not None:
337
+ updates['assignee'] = assignee
338
+ if labels is not None:
339
+ updates['labels'] = labels
340
+ if metadata is not None:
341
+ updates['metadata'] = metadata
342
+
343
+ ticket = self._ticket_manager.update_ticket(ticket_id, **updates)
344
+ if ticket:
345
+ return self._convert_to_ticket_data(ticket)
346
+ return None
347
+
348
+ except Exception as e:
349
+ logger.error(f"Failed to update ticket {ticket_id}: {e}")
350
+ return None
351
+
352
+ def add_comment(
353
+ self,
354
+ ticket_id: str,
355
+ comment: str,
356
+ author: Optional[str] = None
357
+ ) -> bool:
358
+ """
359
+ Add a comment to a ticket.
360
+
361
+ Args:
362
+ ticket_id: Ticket identifier
363
+ comment: Comment text
364
+ author: Comment author (optional)
365
+
366
+ Returns:
367
+ True if successful, False otherwise
368
+ """
369
+ try:
370
+ if not AI_TRACKDOWN_AVAILABLE:
371
+ return False
372
+
373
+ self._ticket_manager.add_comment(
374
+ ticket_id=ticket_id,
375
+ comment=comment,
376
+ author=author or "claude-pm"
377
+ )
378
+ return True
379
+
380
+ except Exception as e:
381
+ logger.error(f"Failed to add comment to ticket {ticket_id}: {e}")
382
+ return False
383
+
384
+ def close_ticket(
385
+ self,
386
+ ticket_id: str,
387
+ resolution: Optional[str] = None
388
+ ) -> Optional[TicketData]:
389
+ """
390
+ Close a ticket.
391
+
392
+ Args:
393
+ ticket_id: Ticket identifier
394
+ resolution: Optional resolution description
395
+
396
+ Returns:
397
+ Updated TicketData object or None if not found
398
+ """
399
+ try:
400
+ if resolution:
401
+ self.add_comment(ticket_id, f"Resolution: {resolution}")
402
+
403
+ return self.update_ticket(ticket_id, status="closed")
404
+
405
+ except Exception as e:
406
+ logger.error(f"Failed to close ticket {ticket_id}: {e}")
407
+ return None
408
+
409
+ # Utility methods
410
+
411
+ def search_tickets(self, query: str, limit: Optional[int] = None) -> List[TicketData]:
412
+ """
413
+ Search tickets by text query.
414
+
415
+ Args:
416
+ query: Search query (searches in title and description)
417
+ limit: Maximum number of results
418
+
419
+ Returns:
420
+ List of matching TicketData objects
421
+ """
422
+ try:
423
+ if not AI_TRACKDOWN_AVAILABLE:
424
+ return []
425
+
426
+ tickets = self._ticket_manager.search_tickets(query)
427
+ result = [self._convert_to_ticket_data(t) for t in tickets]
428
+
429
+ if limit:
430
+ result = result[:limit]
431
+
432
+ return result
433
+
434
+ except Exception as e:
435
+ logger.error(f"Failed to search tickets: {e}")
436
+ return []
437
+
438
+ def get_ticket_statistics(self) -> Dict[str, Any]:
439
+ """
440
+ Get ticket statistics.
441
+
442
+ Returns:
443
+ Dictionary with ticket statistics
444
+ """
445
+ try:
446
+ if not AI_TRACKDOWN_AVAILABLE:
447
+ return {
448
+ "total": 0,
449
+ "by_status": {},
450
+ "by_priority": {},
451
+ "unassigned": 0
452
+ }
453
+
454
+ stats = self._ticket_manager.get_statistics()
455
+ return stats
456
+
457
+ except Exception as e:
458
+ logger.error(f"Failed to get ticket statistics: {e}")
459
+ return {}
460
+
461
+ def _convert_to_ticket_data(self, ticket: Any) -> TicketData:
462
+ """Convert ai-trackdown ticket to TicketData."""
463
+ # Task uses 'assignees' (list) not 'assignee' (single)
464
+ assignee = ticket.assignees[0] if ticket.assignees else None
465
+
466
+ # Task uses 'tags' not 'labels'
467
+ labels = ticket.tags if hasattr(ticket, 'tags') else []
468
+
469
+ return TicketData(
470
+ id=ticket.id,
471
+ title=ticket.title,
472
+ description=ticket.description,
473
+ status=ticket.status if isinstance(ticket.status, str) else str(ticket.status),
474
+ priority=ticket.priority if isinstance(ticket.priority, str) else str(ticket.priority),
475
+ assignee=assignee,
476
+ labels=labels,
477
+ created_at=ticket.created_at,
478
+ updated_at=ticket.updated_at if hasattr(ticket, 'updated_at') else ticket.created_at,
479
+ metadata=ticket.metadata or {}
480
+ )
481
+
482
+ # Async support for PM orchestration
483
+
484
+ async def acreate_ticket(self, **kwargs) -> TicketData:
485
+ """Async version of create_ticket."""
486
+ loop = asyncio.get_event_loop()
487
+ return await loop.run_in_executor(None, self.create_ticket, **kwargs)
488
+
489
+ async def aget_ticket(self, ticket_id: str) -> Optional[TicketData]:
490
+ """Async version of get_ticket."""
491
+ loop = asyncio.get_event_loop()
492
+ return await loop.run_in_executor(None, self.get_ticket, ticket_id)
493
+
494
+ async def alist_tickets(self, **kwargs) -> List[TicketData]:
495
+ """Async version of list_tickets."""
496
+ loop = asyncio.get_event_loop()
497
+ return await loop.run_in_executor(None, self.list_tickets, **kwargs)
498
+
499
+ async def aupdate_ticket(self, ticket_id: str, **kwargs) -> Optional[TicketData]:
500
+ """Async version of update_ticket."""
501
+ loop = asyncio.get_event_loop()
502
+ return await loop.run_in_executor(None, self.update_ticket, ticket_id, **kwargs)
503
+
504
+
505
+ # Convenience functions for direct import
506
+ def get_ticketing_service() -> TicketingService:
507
+ """Get the singleton TicketingService instance."""
508
+ return TicketingService.get_instance()
@@ -0,0 +1 @@
1
+ 0.9.0
@@ -0,0 +1,70 @@
1
+ """
2
+ Version Control Services - Modular services for Git and version management.
3
+
4
+ This package provides modular services for the Version Control Agent including:
5
+ - Git operations management
6
+ - Semantic versioning
7
+ - Branch strategy implementation
8
+ - Conflict resolution
9
+ """
10
+
11
+ from .git_operations import (
12
+ GitOperationsManager,
13
+ GitBranchInfo,
14
+ GitOperationResult,
15
+ GitOperationError,
16
+ )
17
+
18
+ from .semantic_versioning import (
19
+ SemanticVersionManager,
20
+ SemanticVersion,
21
+ VersionBumpType,
22
+ VersionMetadata,
23
+ ChangeAnalysis,
24
+ )
25
+
26
+ from .branch_strategy import (
27
+ BranchStrategyManager,
28
+ BranchStrategyType,
29
+ BranchType,
30
+ BranchWorkflow,
31
+ BranchNamingRule,
32
+ BranchLifecycleRule,
33
+ )
34
+
35
+ from .conflict_resolution import (
36
+ ConflictResolutionManager,
37
+ ConflictType,
38
+ ResolutionStrategy,
39
+ FileConflict,
40
+ ConflictResolution,
41
+ ConflictAnalysis,
42
+ )
43
+
44
+ __all__ = [
45
+ # Git Operations
46
+ "GitOperationsManager",
47
+ "GitBranchInfo",
48
+ "GitOperationResult",
49
+ "GitOperationError",
50
+ # Semantic Versioning
51
+ "SemanticVersionManager",
52
+ "SemanticVersion",
53
+ "VersionBumpType",
54
+ "VersionMetadata",
55
+ "ChangeAnalysis",
56
+ # Branch Strategy
57
+ "BranchStrategyManager",
58
+ "BranchStrategyType",
59
+ "BranchType",
60
+ "BranchWorkflow",
61
+ "BranchNamingRule",
62
+ "BranchLifecycleRule",
63
+ # Conflict Resolution
64
+ "ConflictResolutionManager",
65
+ "ConflictType",
66
+ "ResolutionStrategy",
67
+ "FileConflict",
68
+ "ConflictResolution",
69
+ "ConflictAnalysis",
70
+ ]