claude-mpm 3.7.1__py3-none-any.whl → 3.7.8__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 (52) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +18 -0
  3. claude_mpm/agents/frontmatter_validator.py +116 -17
  4. claude_mpm/agents/schema/agent_schema.json +1 -1
  5. claude_mpm/{dashboard → agents}/templates/.claude-mpm/memories/engineer_agent.md +1 -1
  6. claude_mpm/{dashboard/templates/.claude-mpm/memories/version_control_agent.md → agents/templates/.claude-mpm/memories/qa_agent.md} +2 -2
  7. claude_mpm/agents/templates/.claude-mpm/memories/research_agent.md +39 -0
  8. claude_mpm/agents/templates/code_analyzer.json +34 -12
  9. claude_mpm/agents/templates/data_engineer.json +5 -8
  10. claude_mpm/agents/templates/documentation.json +2 -2
  11. claude_mpm/agents/templates/engineer.json +6 -6
  12. claude_mpm/agents/templates/ops.json +3 -8
  13. claude_mpm/agents/templates/qa.json +2 -3
  14. claude_mpm/agents/templates/research.json +12 -9
  15. claude_mpm/agents/templates/security.json +4 -7
  16. claude_mpm/agents/templates/ticketing.json +161 -0
  17. claude_mpm/agents/templates/version_control.json +3 -3
  18. claude_mpm/agents/templates/web_qa.json +214 -0
  19. claude_mpm/agents/templates/web_ui.json +176 -0
  20. claude_mpm/cli/commands/agents.py +118 -1
  21. claude_mpm/cli/parser.py +11 -0
  22. claude_mpm/cli/ticket_cli.py +31 -0
  23. claude_mpm/core/framework_loader.py +102 -49
  24. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +46 -2
  25. claude_mpm/dashboard/templates/index.html +5 -5
  26. claude_mpm/services/agents/deployment/agent_deployment.py +9 -1
  27. claude_mpm/services/agents/deployment/async_agent_deployment.py +174 -13
  28. claude_mpm/services/agents/management/agent_capabilities_generator.py +21 -11
  29. claude_mpm/services/ticket_manager.py +207 -44
  30. claude_mpm/utils/agent_dependency_loader.py +66 -15
  31. claude_mpm/utils/robust_installer.py +587 -0
  32. {claude_mpm-3.7.1.dist-info → claude_mpm-3.7.8.dist-info}/METADATA +17 -21
  33. {claude_mpm-3.7.1.dist-info → claude_mpm-3.7.8.dist-info}/RECORD +37 -46
  34. claude_mpm/.claude-mpm/logs/hooks_20250728.log +0 -10
  35. claude_mpm/agents/agent-template.yaml +0 -83
  36. claude_mpm/agents/templates/test_integration.json +0 -113
  37. claude_mpm/cli/README.md +0 -108
  38. claude_mpm/cli_module/refactoring_guide.md +0 -253
  39. claude_mpm/config/async_logging_config.yaml +0 -145
  40. claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +0 -34
  41. claude_mpm/dashboard/.claude-mpm/memories/README.md +0 -36
  42. claude_mpm/dashboard/README.md +0 -121
  43. claude_mpm/dashboard/static/js/dashboard.js.backup +0 -1973
  44. claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +0 -36
  45. claude_mpm/hooks/README.md +0 -96
  46. claude_mpm/schemas/agent_schema.json +0 -435
  47. claude_mpm/services/framework_claude_md_generator/README.md +0 -92
  48. claude_mpm/services/version_control/VERSION +0 -1
  49. {claude_mpm-3.7.1.dist-info → claude_mpm-3.7.8.dist-info}/WHEEL +0 -0
  50. {claude_mpm-3.7.1.dist-info → claude_mpm-3.7.8.dist-info}/entry_points.txt +0 -0
  51. {claude_mpm-3.7.1.dist-info → claude_mpm-3.7.8.dist-info}/licenses/LICENSE +0 -0
  52. {claude_mpm-3.7.1.dist-info → claude_mpm-3.7.8.dist-info}/top_level.txt +0 -0
@@ -44,21 +44,28 @@ class TicketManager:
44
44
  (tickets_dir / "tasks").mkdir(exist_ok=True)
45
45
  self.logger.info(f"Created tickets directory structure at: {tickets_dir}")
46
46
 
47
- # Check if we need to configure ai-trackdown
47
+ # Use standardized config format (.trackdown.yaml)
48
48
  config_file = self.project_path / ".trackdown.yaml"
49
+
50
+ # Check if config exists, create if needed
49
51
  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")
52
+ try:
53
+ config = Config.create_default(str(config_file))
54
+ config.set("paths.tickets_dir", "tickets")
55
+ config.set("paths.epics_dir", "tickets/epics")
56
+ config.set("paths.issues_dir", "tickets/issues")
57
+ config.set("paths.tasks_dir", "tickets/tasks")
58
+ config.save()
59
+ self.logger.info("Created .trackdown.yaml configuration")
60
+ except Exception as config_error:
61
+ self.logger.warning(f"Could not create config file: {config_error}")
62
+ self.logger.info("Proceeding without config file - using defaults")
63
+ else:
64
+ self.logger.info(f"Using configuration from: {config_file}")
58
65
 
59
66
  # Initialize TaskManager directly with the project path
60
67
  # TaskManager will handle project initialization internally
61
- task_manager = TaskManager(self.project_path)
68
+ task_manager = TaskManager(str(self.project_path))
62
69
 
63
70
  # Verify it's using the right directory
64
71
  if hasattr(task_manager, 'tasks_dir'):
@@ -68,13 +75,41 @@ class TicketManager:
68
75
 
69
76
  return task_manager
70
77
 
71
- except ImportError:
72
- self.logger.error("ai-trackdown-pytools not installed")
73
- self.logger.info("Install with: pip install ai-trackdown-pytools")
78
+ except ImportError as e:
79
+ import_msg = str(e)
80
+ if "ai_trackdown_pytools" in import_msg.lower():
81
+ self.logger.error("ai-trackdown-pytools is not installed")
82
+ self.logger.info("Install with: pip install ai-trackdown-pytools")
83
+ else:
84
+ self.logger.error(f"Missing dependency: {import_msg}")
85
+ self.logger.info("Ensure all required packages are installed")
86
+ self.logger.debug(f"Import error details: {import_msg}")
87
+ return None
88
+ except AttributeError as e:
89
+ attr_msg = str(e)
90
+ if "TaskManager" in attr_msg:
91
+ self.logger.error("TaskManager class not found in ai-trackdown-pytools")
92
+ self.logger.info("This may indicate an incompatible version")
93
+ else:
94
+ self.logger.error(f"ai-trackdown-pytools API mismatch: {attr_msg}")
95
+ self.logger.info("Try updating: pip install --upgrade ai-trackdown-pytools")
96
+ return None
97
+ except FileNotFoundError as e:
98
+ self.logger.error(f"Required file or directory not found: {e}")
99
+ self.logger.info("Ensure the project directory exists and is accessible")
100
+ return None
101
+ except PermissionError as e:
102
+ self.logger.error(f"Permission denied accessing ticket files: {e}")
103
+ self.logger.info("Check file permissions in the tickets/ directory")
104
+ self.logger.info("You may need to run with appropriate permissions")
74
105
  return None
75
106
  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)
107
+ self.logger.error(f"Unexpected error initializing TaskManager: {e.__class__.__name__}: {e}")
108
+ self.logger.info("This could be due to:")
109
+ self.logger.info(" - Corrupted configuration files")
110
+ self.logger.info(" - Incompatible ai-trackdown-pytools version")
111
+ self.logger.info(" - Missing dependencies")
112
+ self.logger.debug(f"Full error details:", exc_info=True)
78
113
  return None
79
114
 
80
115
  def create_ticket(
@@ -96,19 +131,41 @@ class TicketManager:
96
131
  title: Ticket title
97
132
  ticket_type: Type (task, bug, feature, etc.)
98
133
  description: Detailed description
99
- priority: Priority level (low, medium, high)
134
+ priority: Priority level (low, medium, high, critical)
100
135
  tags: List of tags/labels
101
136
  source: Source identifier
137
+ parent_epic: Parent epic ID (optional)
138
+ parent_issue: Parent issue ID (optional)
102
139
  **kwargs: Additional metadata
103
140
 
104
141
  Returns:
105
142
  Ticket ID if created, None on failure
106
143
  """
107
144
  if not self.task_manager:
108
- self.logger.error("TaskManager not available")
145
+ self.logger.error("TaskManager not available - cannot create ticket")
146
+ self.logger.info("Please ensure ai-trackdown-pytools is installed and properly configured")
147
+ self.logger.info("Run: pip install ai-trackdown-pytools")
109
148
  return None
110
149
 
111
150
  try:
151
+ # Validate input
152
+ if not title or not title.strip():
153
+ self.logger.error("Cannot create ticket with empty title")
154
+ return None
155
+
156
+ # Validate priority
157
+ valid_priorities = ['low', 'medium', 'high', 'critical']
158
+ if priority.lower() not in valid_priorities:
159
+ self.logger.warning(f"Invalid priority '{priority}', using 'medium'")
160
+ self.logger.info(f"Valid priorities are: {', '.join(valid_priorities)}")
161
+ priority = 'medium'
162
+
163
+ # Validate ticket type
164
+ valid_types = ['task', 'bug', 'feature', 'issue', 'enhancement', 'documentation']
165
+ if ticket_type.lower() not in valid_types:
166
+ self.logger.warning(f"Non-standard ticket type '{ticket_type}'")
167
+ self.logger.info(f"Common types are: {', '.join(valid_types)}")
168
+
112
169
  # Prepare tags
113
170
  if tags is None:
114
171
  tags = []
@@ -121,7 +178,7 @@ class TicketManager:
121
178
 
122
179
  # Prepare task data
123
180
  task_data = {
124
- 'title': title,
181
+ 'title': title.strip(),
125
182
  'description': description or f"Auto-extracted {ticket_type} from Claude MPM session",
126
183
  'status': 'open',
127
184
  'priority': priority.lower(),
@@ -136,14 +193,49 @@ class TicketManager:
136
193
  }
137
194
  }
138
195
 
196
+ # Add parent references if provided
197
+ if parent_epic:
198
+ task_data['metadata']['parent_epic'] = parent_epic
199
+ self.logger.debug(f"Linking to parent epic: {parent_epic}")
200
+ if parent_issue:
201
+ task_data['metadata']['parent_issue'] = parent_issue
202
+ self.logger.debug(f"Linking to parent issue: {parent_issue}")
203
+
139
204
  # Create the task
140
205
  task = self.task_manager.create_task(**task_data)
141
206
 
142
- self.logger.info(f"Created ticket: {task.id} - {title}")
143
- return task.id
207
+ if task and hasattr(task, 'id'):
208
+ self.logger.info(f"Successfully created ticket: {task.id} - {title}")
209
+ return task.id
210
+ else:
211
+ self.logger.error(f"Task creation failed - no ID returned")
212
+ self.logger.info("The task may have been created but without proper ID assignment")
213
+ return None
144
214
 
215
+ except AttributeError as e:
216
+ attr_msg = str(e)
217
+ if "create_task" in attr_msg:
218
+ self.logger.error("create_task method not found in TaskManager")
219
+ self.logger.info("The ai-trackdown-pytools API may have changed")
220
+ self.logger.info("Check for updates or API documentation")
221
+ else:
222
+ self.logger.error(f"API mismatch when creating ticket: {attr_msg}")
223
+ return None
224
+ except ValueError as e:
225
+ self.logger.error(f"Invalid data provided for ticket: {e}")
226
+ self.logger.info("Check that all required fields are provided correctly")
227
+ return None
228
+ except PermissionError as e:
229
+ self.logger.error(f"Permission denied when creating ticket: {e}")
230
+ self.logger.info("Check write permissions for the tickets/ directory")
231
+ return None
145
232
  except Exception as e:
146
- self.logger.error(f"Failed to create ticket: {e}")
233
+ self.logger.error(f"Unexpected error creating ticket: {e.__class__.__name__}: {e}")
234
+ self.logger.info("This could be due to:")
235
+ self.logger.info(" - Disk full or quota exceeded")
236
+ self.logger.info(" - Invalid characters in title or description")
237
+ self.logger.info(" - Network issues (if using remote storage)")
238
+ self.logger.debug("Full error details:", exc_info=True)
147
239
  return None
148
240
 
149
241
  def list_recent_tickets(self, limit: int = 10) -> List[Dict[str, Any]]:
@@ -157,26 +249,66 @@ class TicketManager:
157
249
  List of ticket summaries
158
250
  """
159
251
  if not self.task_manager:
252
+ self.logger.warning("TaskManager not available - cannot list tickets")
253
+ self.logger.info("Run: pip install ai-trackdown-pytools")
160
254
  return []
161
255
 
162
256
  try:
257
+ # Validate limit
258
+ if limit < 1:
259
+ self.logger.warning(f"Invalid limit {limit}, using 10")
260
+ limit = 10
261
+ elif limit > 100:
262
+ self.logger.warning(f"Limit {limit} too high, capping at 100")
263
+ limit = 100
264
+
163
265
  tasks = self.task_manager.get_recent_tasks(limit=limit)
164
266
 
267
+ if not tasks:
268
+ self.logger.info("No tickets found in the system")
269
+ return []
270
+
165
271
  tickets = []
166
272
  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
- })
273
+ try:
274
+ ticket_data = {
275
+ 'id': getattr(task, 'id', 'UNKNOWN'),
276
+ 'title': getattr(task, 'title', 'Untitled'),
277
+ 'status': getattr(task, 'status', 'unknown'),
278
+ 'priority': getattr(task, 'priority', 'medium'),
279
+ 'tags': getattr(task, 'tags', []),
280
+ 'created_at': getattr(task, 'created_at', 'N/A'),
281
+ }
282
+ tickets.append(ticket_data)
283
+ except Exception as task_error:
284
+ self.logger.warning(f"Error processing task: {task_error}")
285
+ self.logger.debug(f"Task object type: {type(task)}")
286
+ continue
175
287
 
288
+ self.logger.info(f"Retrieved {len(tickets)} tickets")
176
289
  return tickets
177
290
 
291
+ except AttributeError as e:
292
+ attr_msg = str(e)
293
+ if "get_recent_tasks" in attr_msg:
294
+ self.logger.error("get_recent_tasks method not found in TaskManager")
295
+ self.logger.info("This method may not be available in your version")
296
+ self.logger.info("Try using the CLI directly: aitrackdown task list")
297
+ else:
298
+ self.logger.error(f"API mismatch when listing tickets: {attr_msg}")
299
+ return []
300
+ except FileNotFoundError as e:
301
+ self.logger.error(f"Tickets directory not found: {e}")
302
+ self.logger.info("Ensure the tickets/ directory exists")
303
+ return []
304
+ except PermissionError as e:
305
+ self.logger.error(f"Permission denied reading tickets: {e}")
306
+ self.logger.info("Check read permissions for the tickets/ directory")
307
+ return []
178
308
  except Exception as e:
179
- self.logger.error(f"Failed to list tickets: {e}")
309
+ self.logger.error(f"Failed to list tickets: {e.__class__.__name__}: {e}")
310
+ self.logger.info("Try using the CLI directly: aitrackdown task list")
311
+ self.logger.debug("Full error details:", exc_info=True)
180
312
  return []
181
313
 
182
314
  def get_ticket(self, ticket_id: str) -> Optional[Dict[str, Any]]:
@@ -190,24 +322,55 @@ class TicketManager:
190
322
  Ticket data or None
191
323
  """
192
324
  if not self.task_manager:
325
+ self.logger.error("TaskManager not available - cannot retrieve ticket")
326
+ self.logger.info("Run: pip install ai-trackdown-pytools")
327
+ return None
328
+
329
+ if not ticket_id or not ticket_id.strip():
330
+ self.logger.error("Invalid ticket ID provided")
193
331
  return None
194
332
 
195
333
  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,
334
+ task = self.task_manager.load_task(ticket_id.strip())
335
+
336
+ if not task:
337
+ self.logger.warning(f"Ticket {ticket_id} not found")
338
+ return None
339
+
340
+ # Safely extract all fields with defaults
341
+ ticket_data = {
342
+ 'id': getattr(task, 'id', ticket_id),
343
+ 'title': getattr(task, 'title', 'Untitled'),
344
+ 'description': getattr(task, 'description', ''),
345
+ 'status': getattr(task, 'status', 'unknown'),
346
+ 'priority': getattr(task, 'priority', 'medium'),
347
+ 'tags': getattr(task, 'tags', []),
348
+ 'assignees': getattr(task, 'assignees', []),
349
+ 'created_at': getattr(task, 'created_at', 'N/A'),
350
+ 'updated_at': getattr(task, 'updated_at', 'N/A'),
351
+ 'metadata': getattr(task, 'metadata', {}),
209
352
  }
210
353
 
354
+ self.logger.info(f"Successfully retrieved ticket: {ticket_id}")
355
+ return ticket_data
356
+
357
+ except AttributeError as e:
358
+ attr_msg = str(e)
359
+ if "load_task" in attr_msg:
360
+ self.logger.error("load_task method not found in TaskManager")
361
+ self.logger.info("The API may have changed or this method is not available")
362
+ else:
363
+ self.logger.error(f"Error accessing ticket attributes: {attr_msg}")
364
+ return None
365
+ except FileNotFoundError as e:
366
+ self.logger.error(f"Ticket file not found: {ticket_id}")
367
+ self.logger.info(f"The ticket may have been deleted or the ID is incorrect")
368
+ return None
369
+ except PermissionError as e:
370
+ self.logger.error(f"Permission denied reading ticket {ticket_id}: {e}")
371
+ return None
211
372
  except Exception as e:
212
- self.logger.error(f"Failed to get ticket {ticket_id}: {e}")
373
+ self.logger.error(f"Failed to get ticket {ticket_id}: {e.__class__.__name__}: {e}")
374
+ self.logger.info(f"Try using the CLI: aitrackdown show {ticket_id}")
375
+ self.logger.debug("Full error details:", exc_info=True)
213
376
  return None
@@ -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