claude-mpm 3.7.4__py3-none-any.whl → 3.8.1__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 (117) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +0 -106
  3. claude_mpm/agents/INSTRUCTIONS.md +0 -78
  4. claude_mpm/agents/MEMORY.md +88 -0
  5. claude_mpm/agents/WORKFLOW.md +86 -0
  6. claude_mpm/agents/schema/agent_schema.json +1 -1
  7. claude_mpm/agents/templates/code_analyzer.json +26 -11
  8. claude_mpm/agents/templates/data_engineer.json +4 -7
  9. claude_mpm/agents/templates/documentation.json +2 -2
  10. claude_mpm/agents/templates/engineer.json +2 -2
  11. claude_mpm/agents/templates/ops.json +3 -8
  12. claude_mpm/agents/templates/qa.json +2 -3
  13. claude_mpm/agents/templates/research.json +2 -3
  14. claude_mpm/agents/templates/security.json +3 -6
  15. claude_mpm/agents/templates/ticketing.json +4 -9
  16. claude_mpm/agents/templates/version_control.json +3 -3
  17. claude_mpm/agents/templates/web_qa.json +4 -4
  18. claude_mpm/agents/templates/web_ui.json +4 -4
  19. claude_mpm/cli/__init__.py +2 -2
  20. claude_mpm/cli/commands/__init__.py +2 -1
  21. claude_mpm/cli/commands/agents.py +118 -1
  22. claude_mpm/cli/commands/tickets.py +596 -19
  23. claude_mpm/cli/parser.py +228 -5
  24. claude_mpm/config/__init__.py +30 -39
  25. claude_mpm/config/socketio_config.py +8 -5
  26. claude_mpm/constants.py +13 -0
  27. claude_mpm/core/__init__.py +8 -18
  28. claude_mpm/core/cache.py +596 -0
  29. claude_mpm/core/claude_runner.py +166 -622
  30. claude_mpm/core/config.py +5 -1
  31. claude_mpm/core/constants.py +339 -0
  32. claude_mpm/core/container.py +461 -22
  33. claude_mpm/core/exceptions.py +392 -0
  34. claude_mpm/core/framework_loader.py +208 -93
  35. claude_mpm/core/interactive_session.py +432 -0
  36. claude_mpm/core/interfaces.py +424 -0
  37. claude_mpm/core/lazy.py +467 -0
  38. claude_mpm/core/logging_config.py +444 -0
  39. claude_mpm/core/oneshot_session.py +465 -0
  40. claude_mpm/core/optimized_agent_loader.py +485 -0
  41. claude_mpm/core/optimized_startup.py +490 -0
  42. claude_mpm/core/service_registry.py +52 -26
  43. claude_mpm/core/socketio_pool.py +162 -5
  44. claude_mpm/core/types.py +292 -0
  45. claude_mpm/core/typing_utils.py +477 -0
  46. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +46 -2
  47. claude_mpm/dashboard/templates/index.html +5 -5
  48. claude_mpm/hooks/claude_hooks/hook_handler.py +213 -99
  49. claude_mpm/init.py +2 -1
  50. claude_mpm/services/__init__.py +78 -14
  51. claude_mpm/services/agent/__init__.py +24 -0
  52. claude_mpm/services/agent/deployment.py +2548 -0
  53. claude_mpm/services/agent/management.py +598 -0
  54. claude_mpm/services/agent/registry.py +813 -0
  55. claude_mpm/services/agents/deployment/agent_deployment.py +592 -269
  56. claude_mpm/services/agents/deployment/async_agent_deployment.py +5 -1
  57. claude_mpm/services/agents/management/agent_capabilities_generator.py +21 -11
  58. claude_mpm/services/agents/memory/agent_memory_manager.py +156 -1
  59. claude_mpm/services/async_session_logger.py +8 -3
  60. claude_mpm/services/communication/__init__.py +21 -0
  61. claude_mpm/services/communication/socketio.py +1933 -0
  62. claude_mpm/services/communication/websocket.py +479 -0
  63. claude_mpm/services/core/__init__.py +123 -0
  64. claude_mpm/services/core/base.py +247 -0
  65. claude_mpm/services/core/interfaces.py +951 -0
  66. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +23 -23
  67. claude_mpm/services/framework_claude_md_generator.py +3 -2
  68. claude_mpm/services/health_monitor.py +4 -3
  69. claude_mpm/services/hook_service.py +64 -4
  70. claude_mpm/services/infrastructure/__init__.py +21 -0
  71. claude_mpm/services/infrastructure/logging.py +202 -0
  72. claude_mpm/services/infrastructure/monitoring.py +893 -0
  73. claude_mpm/services/memory/indexed_memory.py +648 -0
  74. claude_mpm/services/project/__init__.py +21 -0
  75. claude_mpm/services/project/analyzer.py +864 -0
  76. claude_mpm/services/project/registry.py +608 -0
  77. claude_mpm/services/project_analyzer.py +95 -2
  78. claude_mpm/services/recovery_manager.py +15 -9
  79. claude_mpm/services/socketio/__init__.py +25 -0
  80. claude_mpm/services/socketio/handlers/__init__.py +25 -0
  81. claude_mpm/services/socketio/handlers/base.py +121 -0
  82. claude_mpm/services/socketio/handlers/connection.py +198 -0
  83. claude_mpm/services/socketio/handlers/file.py +213 -0
  84. claude_mpm/services/socketio/handlers/git.py +723 -0
  85. claude_mpm/services/socketio/handlers/memory.py +27 -0
  86. claude_mpm/services/socketio/handlers/project.py +25 -0
  87. claude_mpm/services/socketio/handlers/registry.py +145 -0
  88. claude_mpm/services/socketio_client_manager.py +12 -7
  89. claude_mpm/services/socketio_server.py +156 -30
  90. claude_mpm/services/ticket_manager.py +377 -51
  91. claude_mpm/utils/agent_dependency_loader.py +66 -15
  92. claude_mpm/utils/error_handler.py +1 -1
  93. claude_mpm/utils/robust_installer.py +587 -0
  94. claude_mpm/validation/agent_validator.py +27 -14
  95. claude_mpm/validation/frontmatter_validator.py +231 -0
  96. {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/METADATA +74 -41
  97. {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/RECORD +101 -76
  98. claude_mpm/.claude-mpm/logs/hooks_20250728.log +0 -10
  99. claude_mpm/agents/agent-template.yaml +0 -83
  100. claude_mpm/cli/README.md +0 -108
  101. claude_mpm/cli_module/refactoring_guide.md +0 -253
  102. claude_mpm/config/async_logging_config.yaml +0 -145
  103. claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +0 -34
  104. claude_mpm/dashboard/.claude-mpm/memories/README.md +0 -36
  105. claude_mpm/dashboard/README.md +0 -121
  106. claude_mpm/dashboard/static/js/dashboard.js.backup +0 -1973
  107. claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +0 -36
  108. claude_mpm/dashboard/templates/.claude-mpm/memories/engineer_agent.md +0 -39
  109. claude_mpm/dashboard/templates/.claude-mpm/memories/version_control_agent.md +0 -38
  110. claude_mpm/hooks/README.md +0 -96
  111. claude_mpm/schemas/agent_schema.json +0 -435
  112. claude_mpm/services/framework_claude_md_generator/README.md +0 -92
  113. claude_mpm/services/version_control/VERSION +0 -1
  114. {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/WHEEL +0 -0
  115. {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/entry_points.txt +0 -0
  116. {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/licenses/LICENSE +0 -0
  117. {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/top_level.txt +0 -0
@@ -4,13 +4,12 @@ from pathlib import Path
4
4
  from typing import Optional, Dict, Any, List
5
5
  from datetime import datetime
6
6
 
7
- try:
8
- from ..core.logger import get_logger
9
- except ImportError:
10
- from core.logger import get_logger
7
+ from claude_mpm.core.logging_config import get_logger
11
8
 
9
+ from ..core.interfaces import TicketManagerInterface
12
10
 
13
- class TicketManager:
11
+
12
+ class TicketManager(TicketManagerInterface):
14
13
  """
15
14
  Manage ticket creation using ai-trackdown-pytools.
16
15
 
@@ -25,7 +24,7 @@ class TicketManager:
25
24
  Args:
26
25
  project_path: Project root (defaults to current directory)
27
26
  """
28
- self.logger = get_logger("ticket_manager")
27
+ self.logger = get_logger(__name__)
29
28
  self.project_path = project_path or Path.cwd()
30
29
  self.task_manager = self._init_task_manager()
31
30
 
@@ -44,21 +43,28 @@ class TicketManager:
44
43
  (tickets_dir / "tasks").mkdir(exist_ok=True)
45
44
  self.logger.info(f"Created tickets directory structure at: {tickets_dir}")
46
45
 
47
- # Check if we need to configure ai-trackdown
46
+ # Use standardized config format (.trackdown.yaml)
48
47
  config_file = self.project_path / ".trackdown.yaml"
48
+
49
+ # Check if config exists, create if needed
49
50
  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")
51
+ try:
52
+ config = Config.create_default(str(config_file))
53
+ config.set("paths.tickets_dir", "tickets")
54
+ config.set("paths.epics_dir", "tickets/epics")
55
+ config.set("paths.issues_dir", "tickets/issues")
56
+ config.set("paths.tasks_dir", "tickets/tasks")
57
+ config.save()
58
+ self.logger.info("Created .trackdown.yaml configuration")
59
+ except Exception as config_error:
60
+ self.logger.warning(f"Could not create config file: {config_error}")
61
+ self.logger.info("Proceeding without config file - using defaults")
62
+ else:
63
+ self.logger.info(f"Using configuration from: {config_file}")
58
64
 
59
65
  # Initialize TaskManager directly with the project path
60
66
  # TaskManager will handle project initialization internally
61
- task_manager = TaskManager(self.project_path)
67
+ task_manager = TaskManager(str(self.project_path))
62
68
 
63
69
  # Verify it's using the right directory
64
70
  if hasattr(task_manager, 'tasks_dir'):
@@ -68,13 +74,41 @@ class TicketManager:
68
74
 
69
75
  return task_manager
70
76
 
71
- except ImportError:
72
- self.logger.error("ai-trackdown-pytools not installed")
73
- self.logger.info("Install with: pip install ai-trackdown-pytools")
77
+ except ImportError as e:
78
+ import_msg = str(e)
79
+ if "ai_trackdown_pytools" in import_msg.lower():
80
+ self.logger.error("ai-trackdown-pytools is not installed")
81
+ self.logger.info("Install with: pip install ai-trackdown-pytools")
82
+ else:
83
+ self.logger.error(f"Missing dependency: {import_msg}")
84
+ self.logger.info("Ensure all required packages are installed")
85
+ self.logger.debug(f"Import error details: {import_msg}")
86
+ return None
87
+ except AttributeError as e:
88
+ attr_msg = str(e)
89
+ if "TaskManager" in attr_msg:
90
+ self.logger.error("TaskManager class not found in ai-trackdown-pytools")
91
+ self.logger.info("This may indicate an incompatible version")
92
+ else:
93
+ self.logger.error(f"ai-trackdown-pytools API mismatch: {attr_msg}")
94
+ self.logger.info("Try updating: pip install --upgrade ai-trackdown-pytools")
95
+ return None
96
+ except FileNotFoundError as e:
97
+ self.logger.error(f"Required file or directory not found: {e}")
98
+ self.logger.info("Ensure the project directory exists and is accessible")
99
+ return None
100
+ except PermissionError as e:
101
+ self.logger.error(f"Permission denied accessing ticket files: {e}")
102
+ self.logger.info("Check file permissions in the tickets/ directory")
103
+ self.logger.info("You may need to run with appropriate permissions")
74
104
  return None
75
105
  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)
106
+ self.logger.error(f"Unexpected error initializing TaskManager: {e.__class__.__name__}: {e}")
107
+ self.logger.info("This could be due to:")
108
+ self.logger.info(" - Corrupted configuration files")
109
+ self.logger.info(" - Incompatible ai-trackdown-pytools version")
110
+ self.logger.info(" - Missing dependencies")
111
+ self.logger.debug(f"Full error details:", exc_info=True)
78
112
  return None
79
113
 
80
114
  def create_ticket(
@@ -96,19 +130,41 @@ class TicketManager:
96
130
  title: Ticket title
97
131
  ticket_type: Type (task, bug, feature, etc.)
98
132
  description: Detailed description
99
- priority: Priority level (low, medium, high)
133
+ priority: Priority level (low, medium, high, critical)
100
134
  tags: List of tags/labels
101
135
  source: Source identifier
136
+ parent_epic: Parent epic ID (optional)
137
+ parent_issue: Parent issue ID (optional)
102
138
  **kwargs: Additional metadata
103
139
 
104
140
  Returns:
105
141
  Ticket ID if created, None on failure
106
142
  """
107
143
  if not self.task_manager:
108
- self.logger.error("TaskManager not available")
144
+ self.logger.error("TaskManager not available - cannot create ticket")
145
+ self.logger.info("Please ensure ai-trackdown-pytools is installed and properly configured")
146
+ self.logger.info("Run: pip install ai-trackdown-pytools")
109
147
  return None
110
148
 
111
149
  try:
150
+ # Validate input
151
+ if not title or not title.strip():
152
+ self.logger.error("Cannot create ticket with empty title")
153
+ return None
154
+
155
+ # Validate priority
156
+ valid_priorities = ['low', 'medium', 'high', 'critical']
157
+ if priority.lower() not in valid_priorities:
158
+ self.logger.warning(f"Invalid priority '{priority}', using 'medium'")
159
+ self.logger.info(f"Valid priorities are: {', '.join(valid_priorities)}")
160
+ priority = 'medium'
161
+
162
+ # Validate ticket type
163
+ valid_types = ['task', 'bug', 'feature', 'issue', 'enhancement', 'documentation']
164
+ if ticket_type.lower() not in valid_types:
165
+ self.logger.warning(f"Non-standard ticket type '{ticket_type}'")
166
+ self.logger.info(f"Common types are: {', '.join(valid_types)}")
167
+
112
168
  # Prepare tags
113
169
  if tags is None:
114
170
  tags = []
@@ -121,7 +177,7 @@ class TicketManager:
121
177
 
122
178
  # Prepare task data
123
179
  task_data = {
124
- 'title': title,
180
+ 'title': title.strip(),
125
181
  'description': description or f"Auto-extracted {ticket_type} from Claude MPM session",
126
182
  'status': 'open',
127
183
  'priority': priority.lower(),
@@ -136,14 +192,49 @@ class TicketManager:
136
192
  }
137
193
  }
138
194
 
195
+ # Add parent references if provided
196
+ if parent_epic:
197
+ task_data['metadata']['parent_epic'] = parent_epic
198
+ self.logger.debug(f"Linking to parent epic: {parent_epic}")
199
+ if parent_issue:
200
+ task_data['metadata']['parent_issue'] = parent_issue
201
+ self.logger.debug(f"Linking to parent issue: {parent_issue}")
202
+
139
203
  # Create the task
140
204
  task = self.task_manager.create_task(**task_data)
141
205
 
142
- self.logger.info(f"Created ticket: {task.id} - {title}")
143
- return task.id
206
+ if task and hasattr(task, 'id'):
207
+ self.logger.info(f"Successfully created ticket: {task.id} - {title}")
208
+ return task.id
209
+ else:
210
+ self.logger.error(f"Task creation failed - no ID returned")
211
+ self.logger.info("The task may have been created but without proper ID assignment")
212
+ return None
144
213
 
214
+ except AttributeError as e:
215
+ attr_msg = str(e)
216
+ if "create_task" in attr_msg:
217
+ self.logger.error("create_task method not found in TaskManager")
218
+ self.logger.info("The ai-trackdown-pytools API may have changed")
219
+ self.logger.info("Check for updates or API documentation")
220
+ else:
221
+ self.logger.error(f"API mismatch when creating ticket: {attr_msg}")
222
+ return None
223
+ except ValueError as e:
224
+ self.logger.error(f"Invalid data provided for ticket: {e}")
225
+ self.logger.info("Check that all required fields are provided correctly")
226
+ return None
227
+ except PermissionError as e:
228
+ self.logger.error(f"Permission denied when creating ticket: {e}")
229
+ self.logger.info("Check write permissions for the tickets/ directory")
230
+ return None
145
231
  except Exception as e:
146
- self.logger.error(f"Failed to create ticket: {e}")
232
+ self.logger.error(f"Unexpected error creating ticket: {e.__class__.__name__}: {e}")
233
+ self.logger.info("This could be due to:")
234
+ self.logger.info(" - Disk full or quota exceeded")
235
+ self.logger.info(" - Invalid characters in title or description")
236
+ self.logger.info(" - Network issues (if using remote storage)")
237
+ self.logger.debug("Full error details:", exc_info=True)
147
238
  return None
148
239
 
149
240
  def list_recent_tickets(self, limit: int = 10) -> List[Dict[str, Any]]:
@@ -157,26 +248,66 @@ class TicketManager:
157
248
  List of ticket summaries
158
249
  """
159
250
  if not self.task_manager:
251
+ self.logger.warning("TaskManager not available - cannot list tickets")
252
+ self.logger.info("Run: pip install ai-trackdown-pytools")
160
253
  return []
161
254
 
162
255
  try:
256
+ # Validate limit
257
+ if limit < 1:
258
+ self.logger.warning(f"Invalid limit {limit}, using 10")
259
+ limit = 10
260
+ elif limit > 100:
261
+ self.logger.warning(f"Limit {limit} too high, capping at 100")
262
+ limit = 100
263
+
163
264
  tasks = self.task_manager.get_recent_tasks(limit=limit)
164
265
 
266
+ if not tasks:
267
+ self.logger.info("No tickets found in the system")
268
+ return []
269
+
165
270
  tickets = []
166
271
  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
- })
272
+ try:
273
+ ticket_data = {
274
+ 'id': getattr(task, 'id', 'UNKNOWN'),
275
+ 'title': getattr(task, 'title', 'Untitled'),
276
+ 'status': getattr(task, 'status', 'unknown'),
277
+ 'priority': getattr(task, 'priority', 'medium'),
278
+ 'tags': getattr(task, 'tags', []),
279
+ 'created_at': getattr(task, 'created_at', 'N/A'),
280
+ }
281
+ tickets.append(ticket_data)
282
+ except Exception as task_error:
283
+ self.logger.warning(f"Error processing task: {task_error}")
284
+ self.logger.debug(f"Task object type: {type(task)}")
285
+ continue
175
286
 
287
+ self.logger.info(f"Retrieved {len(tickets)} tickets")
176
288
  return tickets
177
289
 
290
+ except AttributeError as e:
291
+ attr_msg = str(e)
292
+ if "get_recent_tasks" in attr_msg:
293
+ self.logger.error("get_recent_tasks method not found in TaskManager")
294
+ self.logger.info("This method may not be available in your version")
295
+ self.logger.info("Try using the CLI directly: aitrackdown task list")
296
+ else:
297
+ self.logger.error(f"API mismatch when listing tickets: {attr_msg}")
298
+ return []
299
+ except FileNotFoundError as e:
300
+ self.logger.error(f"Tickets directory not found: {e}")
301
+ self.logger.info("Ensure the tickets/ directory exists")
302
+ return []
303
+ except PermissionError as e:
304
+ self.logger.error(f"Permission denied reading tickets: {e}")
305
+ self.logger.info("Check read permissions for the tickets/ directory")
306
+ return []
178
307
  except Exception as e:
179
- self.logger.error(f"Failed to list tickets: {e}")
308
+ self.logger.error(f"Failed to list tickets: {e.__class__.__name__}: {e}")
309
+ self.logger.info("Try using the CLI directly: aitrackdown task list")
310
+ self.logger.debug("Full error details:", exc_info=True)
180
311
  return []
181
312
 
182
313
  def get_ticket(self, ticket_id: str) -> Optional[Dict[str, Any]]:
@@ -190,24 +321,219 @@ class TicketManager:
190
321
  Ticket data or None
191
322
  """
192
323
  if not self.task_manager:
324
+ self.logger.error("TaskManager not available - cannot retrieve ticket")
325
+ self.logger.info("Run: pip install ai-trackdown-pytools")
326
+ return None
327
+
328
+ if not ticket_id or not ticket_id.strip():
329
+ self.logger.error("Invalid ticket ID provided")
193
330
  return None
194
331
 
195
332
  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,
333
+ task = self.task_manager.load_task(ticket_id.strip())
334
+
335
+ if not task:
336
+ self.logger.warning(f"Ticket {ticket_id} not found")
337
+ return None
338
+
339
+ # Safely extract all fields with defaults
340
+ ticket_data = {
341
+ 'id': getattr(task, 'id', ticket_id),
342
+ 'title': getattr(task, 'title', 'Untitled'),
343
+ 'description': getattr(task, 'description', ''),
344
+ 'status': getattr(task, 'status', 'unknown'),
345
+ 'priority': getattr(task, 'priority', 'medium'),
346
+ 'tags': getattr(task, 'tags', []),
347
+ 'assignees': getattr(task, 'assignees', []),
348
+ 'created_at': getattr(task, 'created_at', 'N/A'),
349
+ 'updated_at': getattr(task, 'updated_at', 'N/A'),
350
+ 'metadata': getattr(task, 'metadata', {}),
209
351
  }
210
352
 
353
+ self.logger.info(f"Successfully retrieved ticket: {ticket_id}")
354
+ return ticket_data
355
+
356
+ except AttributeError as e:
357
+ attr_msg = str(e)
358
+ if "load_task" in attr_msg:
359
+ self.logger.error("load_task method not found in TaskManager")
360
+ self.logger.info("The API may have changed or this method is not available")
361
+ else:
362
+ self.logger.error(f"Error accessing ticket attributes: {attr_msg}")
363
+ return None
364
+ except FileNotFoundError as e:
365
+ self.logger.error(f"Ticket file not found: {ticket_id}")
366
+ self.logger.info(f"The ticket may have been deleted or the ID is incorrect")
367
+ return None
368
+ except PermissionError as e:
369
+ self.logger.error(f"Permission denied reading ticket {ticket_id}: {e}")
370
+ return None
211
371
  except Exception as e:
212
- self.logger.error(f"Failed to get ticket {ticket_id}: {e}")
213
- return None
372
+ self.logger.error(f"Failed to get ticket {ticket_id}: {e.__class__.__name__}: {e}")
373
+ self.logger.info(f"Try using the CLI: aitrackdown show {ticket_id}")
374
+ self.logger.debug("Full error details:", exc_info=True)
375
+ return None
376
+
377
+ # ================================================================================
378
+ # Interface Adapter Methods
379
+ # ================================================================================
380
+ # These methods adapt the existing implementation to comply with TicketManagerInterface
381
+
382
+ def create_task(self, title: str, description: str, **kwargs) -> Optional[str]:
383
+ """Create a new task ticket.
384
+
385
+ WHY: This adapter method provides interface compliance by wrapping
386
+ the underlying task manager's create functionality.
387
+
388
+ Args:
389
+ title: Task title
390
+ description: Task description
391
+ **kwargs: Additional task properties
392
+
393
+ Returns:
394
+ Task ID if created successfully, None otherwise
395
+ """
396
+ if not self.task_manager:
397
+ self.logger.error("Task manager not initialized")
398
+ return None
399
+
400
+ try:
401
+ # Create task using ai-trackdown-pytools
402
+ from ai_trackdown_pytools.core.task import Task
403
+
404
+ task = Task(
405
+ title=title,
406
+ description=description,
407
+ status=kwargs.get('status', 'open'),
408
+ priority=kwargs.get('priority', 'medium'),
409
+ tags=kwargs.get('tags', []),
410
+ assignees=kwargs.get('assignees', [])
411
+ )
412
+
413
+ # Save the task
414
+ task_id = self.task_manager.create_task(task)
415
+ self.logger.info(f"Created task {task_id}: {title}")
416
+ return task_id
417
+
418
+ except ImportError:
419
+ self.logger.error("ai-trackdown-pytools not available")
420
+ return None
421
+ except Exception as e:
422
+ self.logger.error(f"Failed to create task: {e}")
423
+ return None
424
+
425
+ def update_task(self, task_id: str, **updates) -> bool:
426
+ """Update an existing task.
427
+
428
+ WHY: This adapter method provides interface compliance by wrapping
429
+ task update operations.
430
+
431
+ Args:
432
+ task_id: ID of task to update
433
+ **updates: Fields to update
434
+
435
+ Returns:
436
+ True if update successful
437
+ """
438
+ if not self.task_manager:
439
+ self.logger.error("Task manager not initialized")
440
+ return False
441
+
442
+ try:
443
+ # Get the existing task
444
+ task = self.task_manager.get_task(task_id)
445
+ if not task:
446
+ self.logger.error(f"Task {task_id} not found")
447
+ return False
448
+
449
+ # Apply updates
450
+ for key, value in updates.items():
451
+ if hasattr(task, key):
452
+ setattr(task, key, value)
453
+
454
+ # Save the updated task
455
+ self.task_manager.update_task(task)
456
+ self.logger.info(f"Updated task {task_id}")
457
+ return True
458
+
459
+ except Exception as e:
460
+ self.logger.error(f"Failed to update task {task_id}: {e}")
461
+ return False
462
+
463
+ def get_task(self, task_id: str) -> Optional[Dict[str, Any]]:
464
+ """Get task details.
465
+
466
+ WHY: This adapter method provides interface compliance by wrapping
467
+ the existing get_ticket method.
468
+
469
+ Args:
470
+ task_id: ID of task to retrieve
471
+
472
+ Returns:
473
+ Task data dictionary or None if not found
474
+ """
475
+ # Use existing get_ticket method which already returns dict format
476
+ return self.get_ticket(task_id)
477
+
478
+ def list_tasks(self, status: Optional[str] = None, **filters) -> List[Dict[str, Any]]:
479
+ """List tasks with optional filtering.
480
+
481
+ WHY: This adapter method provides interface compliance by wrapping
482
+ task listing operations.
483
+
484
+ Args:
485
+ status: Optional status filter
486
+ **filters: Additional filter criteria
487
+
488
+ Returns:
489
+ List of task dictionaries
490
+ """
491
+ if not self.task_manager:
492
+ self.logger.error("Task manager not initialized")
493
+ return []
494
+
495
+ try:
496
+ # Get all tasks
497
+ tasks = self.task_manager.list_tasks()
498
+
499
+ # Apply filters
500
+ filtered_tasks = []
501
+ for task in tasks:
502
+ # Check status filter
503
+ if status and task.get('status') != status:
504
+ continue
505
+
506
+ # Check additional filters
507
+ match = True
508
+ for key, value in filters.items():
509
+ if task.get(key) != value:
510
+ match = False
511
+ break
512
+
513
+ if match:
514
+ filtered_tasks.append(task)
515
+
516
+ return filtered_tasks
517
+
518
+ except Exception as e:
519
+ self.logger.error(f"Failed to list tasks: {e}")
520
+ return []
521
+
522
+ def close_task(self, task_id: str, resolution: Optional[str] = None) -> bool:
523
+ """Close a task.
524
+
525
+ WHY: This adapter method provides interface compliance by updating
526
+ task status to closed.
527
+
528
+ Args:
529
+ task_id: ID of task to close
530
+ resolution: Optional resolution description
531
+
532
+ Returns:
533
+ True if close successful
534
+ """
535
+ updates = {'status': 'closed'}
536
+ if resolution:
537
+ updates['resolution'] = resolution
538
+
539
+ return self.update_task(task_id, **updates)
@@ -278,7 +278,11 @@ class AgentDependencyLoader:
278
278
 
279
279
  def install_missing_dependencies(self, dependencies: List[str]) -> Tuple[bool, str]:
280
280
  """
281
- Install missing Python dependencies.
281
+ Install missing Python dependencies using robust retry logic.
282
+
283
+ WHY: Network issues and temporary package unavailability can cause
284
+ installation failures. Using the robust installer with retries
285
+ significantly improves success rate.
282
286
 
283
287
  Args:
284
288
  dependencies: List of package specifications to install
@@ -299,27 +303,74 @@ class AgentDependencyLoader:
299
303
 
300
304
  if not compatible:
301
305
  return True, "No compatible packages to install"
302
-
306
+
307
+ # Use robust installer with retry logic
303
308
  try:
304
- cmd = [sys.executable, "-m", "pip", "install"] + compatible
305
- logger.info(f"Installing {len(compatible)} compatible dependencies...")
309
+ from .robust_installer import RobustPackageInstaller
310
+
311
+ logger.info(f"Installing {len(compatible)} compatible dependencies with retry logic...")
306
312
  if incompatible:
307
313
  logger.info(f"(Skipping {len(incompatible)} incompatible with Python {sys.version_info.major}.{sys.version_info.minor})")
308
314
 
309
- result = subprocess.run(
310
- cmd,
311
- capture_output=True,
312
- text=True,
315
+ # Create installer with sensible defaults
316
+ installer = RobustPackageInstaller(
317
+ max_retries=3,
318
+ retry_delay=2.0,
313
319
  timeout=300
314
320
  )
315
321
 
316
- if result.returncode == 0:
317
- logger.info("Successfully installed compatible dependencies")
318
- if incompatible:
319
- return True, f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible"
320
- return True, ""
321
- else:
322
- error_msg = f"Installation failed: {result.stderr}"
322
+ # Install packages
323
+ successful, failed, errors = installer.install_packages(compatible)
324
+
325
+ if failed:
326
+ # Provide detailed error information
327
+ error_details = []
328
+ for pkg in failed:
329
+ error_details.append(f"{pkg}: {errors.get(pkg, 'Unknown error')}")
330
+
331
+ error_msg = f"Failed to install {len(failed)} packages:\n" + "\n".join(error_details)
332
+ logger.error(error_msg)
333
+
334
+ # Partial success handling
335
+ if successful:
336
+ partial_msg = f"Partially successful: installed {len(successful)} of {len(compatible)} packages"
337
+ logger.info(partial_msg)
338
+ if incompatible:
339
+ return True, f"{partial_msg}. Also skipped {len(incompatible)} incompatible"
340
+ return True, partial_msg
341
+
342
+ return False, error_msg
343
+
344
+ logger.info(f"Successfully installed all {len(successful)} compatible dependencies")
345
+ if incompatible:
346
+ return True, f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible"
347
+ return True, ""
348
+
349
+ except ImportError:
350
+ # Fallback to simple installation if robust installer not available
351
+ logger.warning("Robust installer not available, falling back to simple installation")
352
+ try:
353
+ cmd = [sys.executable, "-m", "pip", "install"] + compatible
354
+
355
+ result = subprocess.run(
356
+ cmd,
357
+ capture_output=True,
358
+ text=True,
359
+ timeout=300
360
+ )
361
+
362
+ if result.returncode == 0:
363
+ logger.info("Successfully installed compatible dependencies")
364
+ if incompatible:
365
+ return True, f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible"
366
+ return True, ""
367
+ else:
368
+ error_msg = f"Installation failed: {result.stderr}"
369
+ logger.error(error_msg)
370
+ return False, error_msg
371
+
372
+ except Exception as e:
373
+ error_msg = f"Failed to install dependencies: {e}"
323
374
  logger.error(error_msg)
324
375
  return False, error_msg
325
376
 
@@ -6,7 +6,7 @@ Inspired by awesome-claude-code's comprehensive error handling approach.
6
6
 
7
7
  import logging
8
8
  import sys
9
- from typing import Optional, Type, Callable, Any, Dict
9
+ from typing import Optional, Type, Callable, Any, Dict, List
10
10
  from functools import wraps
11
11
  import traceback
12
12
  from datetime import datetime