claude-mpm 4.12.4__py3-none-any.whl → 4.13.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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (29) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/cli/__init__.py +10 -0
  3. claude_mpm/cli/commands/agents.py +31 -0
  4. claude_mpm/cli/commands/agents_detect.py +380 -0
  5. claude_mpm/cli/commands/agents_recommend.py +309 -0
  6. claude_mpm/cli/commands/auto_configure.py +572 -0
  7. claude_mpm/cli/parsers/agents_parser.py +9 -0
  8. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  9. claude_mpm/cli/parsers/base_parser.py +7 -0
  10. claude_mpm/services/agents/__init__.py +18 -5
  11. claude_mpm/services/agents/auto_config_manager.py +797 -0
  12. claude_mpm/services/agents/observers.py +547 -0
  13. claude_mpm/services/agents/recommender.py +568 -0
  14. claude_mpm/services/core/__init__.py +33 -1
  15. claude_mpm/services/core/interfaces/__init__.py +16 -1
  16. claude_mpm/services/core/interfaces/agent.py +184 -0
  17. claude_mpm/services/core/interfaces/project.py +121 -0
  18. claude_mpm/services/core/models/__init__.py +46 -0
  19. claude_mpm/services/core/models/agent_config.py +397 -0
  20. claude_mpm/services/core/models/toolchain.py +306 -0
  21. claude_mpm/services/project/__init__.py +23 -0
  22. claude_mpm/services/project/detection_strategies.py +719 -0
  23. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  24. {claude_mpm-4.12.4.dist-info → claude_mpm-4.13.1.dist-info}/METADATA +1 -1
  25. {claude_mpm-4.12.4.dist-info → claude_mpm-4.13.1.dist-info}/RECORD +29 -16
  26. {claude_mpm-4.12.4.dist-info → claude_mpm-4.13.1.dist-info}/WHEEL +0 -0
  27. {claude_mpm-4.12.4.dist-info → claude_mpm-4.13.1.dist-info}/entry_points.txt +0 -0
  28. {claude_mpm-4.12.4.dist-info → claude_mpm-4.13.1.dist-info}/licenses/LICENSE +0 -0
  29. {claude_mpm-4.12.4.dist-info → claude_mpm-4.13.1.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 4.12.4
1
+ 4.13.1
@@ -720,6 +720,16 @@ def _execute_command(command: str, args) -> int:
720
720
  result = handle_verify(args)
721
721
  return result if result is not None else 0
722
722
 
723
+ # Handle auto-configure command with lazy import
724
+ if command == "auto-configure":
725
+ # Lazy import to avoid loading unless needed
726
+ from .commands.auto_configure import AutoConfigureCommand
727
+
728
+ cmd = AutoConfigureCommand()
729
+ result = cmd.run(args)
730
+ # Convert CommandResult to exit code
731
+ return result.exit_code if result else 0
732
+
723
733
  # Map stable commands to their implementations
724
734
  command_map = {
725
735
  CLICommands.RUN.value: run_session,
@@ -122,6 +122,9 @@ class AgentsCommand(AgentCommand):
122
122
  "delete": self._delete_local_agent,
123
123
  "manage": self._manage_local_agents,
124
124
  "configure": self._configure_deployment,
125
+ # Auto-configuration commands (TSK-0054 Phase 5)
126
+ "detect": self._detect_toolchain,
127
+ "recommend": self._recommend_agents,
125
128
  }
126
129
 
127
130
  if args.agents_command in command_map:
@@ -1361,6 +1364,34 @@ class AgentsCommand(AgentCommand):
1361
1364
  f"Error in interactive configuration: {e}"
1362
1365
  )
1363
1366
 
1367
+ def _detect_toolchain(self, args) -> CommandResult:
1368
+ """Detect project toolchain without deploying agents.
1369
+
1370
+ Part of TSK-0054 Phase 5: Auto-configuration CLI integration.
1371
+ """
1372
+ try:
1373
+ from .agents_detect import AgentsDetectCommand
1374
+
1375
+ cmd = AgentsDetectCommand()
1376
+ return cmd.run(args)
1377
+ except Exception as e:
1378
+ self.logger.error(f"Error detecting toolchain: {e}", exc_info=True)
1379
+ return CommandResult.error_result(f"Error detecting toolchain: {e}")
1380
+
1381
+ def _recommend_agents(self, args) -> CommandResult:
1382
+ """Recommend agents based on project toolchain.
1383
+
1384
+ Part of TSK-0054 Phase 5: Auto-configuration CLI integration.
1385
+ """
1386
+ try:
1387
+ from .agents_recommend import AgentsRecommendCommand
1388
+
1389
+ cmd = AgentsRecommendCommand()
1390
+ return cmd.run(args)
1391
+ except Exception as e:
1392
+ self.logger.error(f"Error recommending agents: {e}", exc_info=True)
1393
+ return CommandResult.error_result(f"Error recommending agents: {e}")
1394
+
1364
1395
 
1365
1396
  def manage_agents(args):
1366
1397
  """
@@ -0,0 +1,380 @@
1
+ """
2
+ Agents Detect CLI Command for Claude MPM Framework
3
+ ===================================================
4
+
5
+ WHY: This module provides a CLI interface for detecting project toolchain
6
+ without making any changes. Useful for debugging and verification of the
7
+ toolchain detection system.
8
+
9
+ DESIGN DECISION: Focused solely on detection and display, with no side effects.
10
+ Supports multiple output formats for different use cases (human-readable,
11
+ JSON for scripting).
12
+
13
+ Part of TSK-0054: Auto-Configuration Feature - Phase 5
14
+ """
15
+
16
+ import json
17
+ from pathlib import Path
18
+ from typing import Optional
19
+
20
+ try:
21
+ from rich.console import Console
22
+ from rich.table import Table
23
+
24
+ RICH_AVAILABLE = True
25
+ except ImportError:
26
+ RICH_AVAILABLE = False
27
+
28
+ from ...services.project.toolchain_analyzer import ToolchainAnalyzerService
29
+ from ..shared import BaseCommand, CommandResult
30
+
31
+
32
+ class AgentsDetectCommand(BaseCommand):
33
+ """
34
+ Handle agents detect CLI command.
35
+
36
+ This command analyzes the project to detect languages, frameworks,
37
+ and deployment targets without making any configuration changes.
38
+ """
39
+
40
+ def __init__(self):
41
+ """Initialize the agents detect command."""
42
+ super().__init__("agents-detect")
43
+ self.console = Console() if RICH_AVAILABLE else None
44
+ self._toolchain_analyzer = None
45
+
46
+ @property
47
+ def toolchain_analyzer(self) -> ToolchainAnalyzerService:
48
+ """Get toolchain analyzer (lazy loaded)."""
49
+ if self._toolchain_analyzer is None:
50
+ self._toolchain_analyzer = ToolchainAnalyzerService()
51
+ return self._toolchain_analyzer
52
+
53
+ def validate_args(self, args) -> Optional[str]:
54
+ """Validate command arguments."""
55
+ # Validate project path
56
+ project_path = (
57
+ Path(args.project_path)
58
+ if hasattr(args, "project_path") and args.project_path
59
+ else Path.cwd()
60
+ )
61
+ if not project_path.exists():
62
+ return f"Project path does not exist: {project_path}"
63
+
64
+ return None
65
+
66
+ def run(self, args) -> CommandResult:
67
+ """
68
+ Execute agents detect command.
69
+
70
+ Returns:
71
+ CommandResult with success status and exit code
72
+ """
73
+ try:
74
+ # Setup logging
75
+ self.setup_logging(args)
76
+
77
+ # Validate arguments
78
+ error = self.validate_args(args)
79
+ if error:
80
+ return CommandResult.error_result(error)
81
+
82
+ # Get configuration options
83
+ project_path = (
84
+ Path(args.project_path)
85
+ if hasattr(args, "project_path") and args.project_path
86
+ else Path.cwd()
87
+ )
88
+ json_output = args.json if hasattr(args, "json") and args.json else False
89
+ verbose = (
90
+ args.verbose if hasattr(args, "verbose") and args.verbose else False
91
+ )
92
+
93
+ # Analyze toolchain
94
+ if self.console and not json_output:
95
+ with self.console.status("[bold green]Analyzing project toolchain..."):
96
+ analysis = self.toolchain_analyzer.analyze_project(
97
+ str(project_path)
98
+ )
99
+ else:
100
+ analysis = self.toolchain_analyzer.analyze_project(str(project_path))
101
+
102
+ # Output results
103
+ if json_output:
104
+ return self._output_json(analysis, verbose)
105
+ return self._display_results(analysis, verbose)
106
+
107
+ except KeyboardInterrupt:
108
+ if self.console:
109
+ self.console.print("\n\n❌ Operation cancelled by user")
110
+ else:
111
+ print("\n\nOperation cancelled by user")
112
+ return CommandResult.error_result("Operation cancelled", exit_code=130)
113
+
114
+ except Exception as e:
115
+ self.logger.exception("Toolchain detection failed")
116
+ error_msg = f"Toolchain detection failed: {e!s}"
117
+ if self.console:
118
+ self.console.print(f"\n❌ {error_msg}")
119
+ else:
120
+ print(f"\n{error_msg}")
121
+ return CommandResult.error_result(error_msg)
122
+
123
+ def _display_results(self, analysis, verbose: bool) -> CommandResult:
124
+ """Display toolchain analysis results with Rich formatting."""
125
+ if not self.console:
126
+ return self._display_results_plain(analysis, verbose)
127
+
128
+ # Display header
129
+ self.console.print("\n📊 Project Toolchain Analysis", style="bold blue")
130
+ self.console.print(f"Project: {analysis.project_path}\n")
131
+
132
+ # Display detected languages
133
+ if analysis.languages:
134
+ self.console.print("🔤 Detected Languages:", style="bold green")
135
+ lang_table = Table(show_header=True, header_style="bold")
136
+ lang_table.add_column("Language", style="cyan")
137
+ lang_table.add_column("Version", style="yellow")
138
+ lang_table.add_column("Confidence", style="green")
139
+ if verbose:
140
+ lang_table.add_column("Evidence", style="dim")
141
+
142
+ for lang in analysis.languages:
143
+ confidence_pct = int(lang.confidence * 100)
144
+ bar = "█" * (confidence_pct // 10) + "░" * (10 - confidence_pct // 10)
145
+ confidence_str = f"{bar} {confidence_pct}%"
146
+
147
+ if verbose:
148
+ evidence = ", ".join(lang.evidence[:3]) # First 3 items
149
+ if len(lang.evidence) > 3:
150
+ evidence += f" (+{len(lang.evidence) - 3} more)"
151
+ lang_table.add_row(
152
+ lang.language,
153
+ lang.version or "Unknown",
154
+ confidence_str,
155
+ evidence,
156
+ )
157
+ else:
158
+ lang_table.add_row(
159
+ lang.language, lang.version or "Unknown", confidence_str
160
+ )
161
+
162
+ self.console.print(lang_table)
163
+ else:
164
+ self.console.print(" No languages detected", style="yellow")
165
+
166
+ # Display detected frameworks
167
+ if analysis.frameworks:
168
+ self.console.print("\n🏗️ Detected Frameworks:", style="bold green")
169
+ fw_table = Table(show_header=True, header_style="bold")
170
+ fw_table.add_column("Framework", style="cyan")
171
+ fw_table.add_column("Version", style="yellow")
172
+ fw_table.add_column("Category", style="magenta")
173
+ fw_table.add_column("Confidence", style="green")
174
+
175
+ for fw in analysis.frameworks:
176
+ confidence_pct = int(fw.confidence * 100)
177
+ bar = "█" * (confidence_pct // 10) + "░" * (10 - confidence_pct // 10)
178
+ confidence_str = f"{bar} {confidence_pct}%"
179
+
180
+ fw_table.add_row(
181
+ fw.name,
182
+ fw.version or "Unknown",
183
+ (
184
+ fw.category.value
185
+ if hasattr(fw.category, "value")
186
+ else str(fw.category)
187
+ ),
188
+ confidence_str,
189
+ )
190
+
191
+ self.console.print(fw_table)
192
+ else:
193
+ self.console.print("\n No frameworks detected", style="yellow")
194
+
195
+ # Display deployment targets
196
+ if analysis.deployment_targets:
197
+ self.console.print("\n🚀 Deployment Targets:", style="bold green")
198
+ dt_table = Table(show_header=True, header_style="bold")
199
+ dt_table.add_column("Target", style="cyan")
200
+ dt_table.add_column("Confidence", style="green")
201
+ if verbose:
202
+ dt_table.add_column("Evidence", style="dim")
203
+
204
+ for target in analysis.deployment_targets:
205
+ confidence_pct = int(target.confidence * 100)
206
+ bar = "█" * (confidence_pct // 10) + "░" * (10 - confidence_pct // 10)
207
+ confidence_str = f"{bar} {confidence_pct}%"
208
+
209
+ if verbose:
210
+ evidence = ", ".join(target.evidence[:3])
211
+ if len(target.evidence) > 3:
212
+ evidence += f" (+{len(target.evidence) - 3} more)"
213
+ dt_table.add_row(
214
+ (
215
+ target.target_type.value
216
+ if hasattr(target.target_type, "value")
217
+ else str(target.target_type)
218
+ ),
219
+ confidence_str,
220
+ evidence,
221
+ )
222
+ else:
223
+ dt_table.add_row(
224
+ (
225
+ target.target_type.value
226
+ if hasattr(target.target_type, "value")
227
+ else str(target.target_type)
228
+ ),
229
+ confidence_str,
230
+ )
231
+
232
+ self.console.print(dt_table)
233
+ else:
234
+ self.console.print("\n No deployment targets detected", style="yellow")
235
+
236
+ # Display all components summary
237
+ if analysis.components:
238
+ self.console.print("\n📦 All Components:", style="bold blue")
239
+ comp_table = Table(show_header=True, header_style="bold")
240
+ comp_table.add_column("Type", style="cyan")
241
+ comp_table.add_column("Name", style="yellow")
242
+ comp_table.add_column("Version", style="magenta")
243
+ comp_table.add_column("Confidence", style="green")
244
+
245
+ for comp in analysis.components:
246
+ confidence_pct = int(comp.confidence * 100)
247
+ bar = "█" * (confidence_pct // 10) + "░" * (10 - confidence_pct // 10)
248
+ confidence_str = f"{bar} {confidence_pct}%"
249
+
250
+ comp_table.add_row(
251
+ comp.type.value if hasattr(comp.type, "value") else str(comp.type),
252
+ comp.name or "-",
253
+ comp.version or "Unknown",
254
+ confidence_str,
255
+ )
256
+
257
+ self.console.print(comp_table)
258
+
259
+ # Summary
260
+ self.console.print(
261
+ f"\n✅ Analysis complete: {len(analysis.languages)} language(s), "
262
+ f"{len(analysis.frameworks)} framework(s), "
263
+ f"{len(analysis.deployment_targets)} deployment target(s)",
264
+ style="bold green",
265
+ )
266
+
267
+ return CommandResult.success_result()
268
+
269
+ def _display_results_plain(self, analysis, verbose: bool) -> CommandResult:
270
+ """Display results in plain text (fallback)."""
271
+ print("\n📊 Project Toolchain Analysis")
272
+ print(f"Project: {analysis.project_path}\n")
273
+
274
+ # Languages
275
+ if analysis.languages:
276
+ print("Detected Languages:")
277
+ for lang in analysis.languages:
278
+ confidence_pct = int(lang.confidence * 100)
279
+ print(
280
+ f" - {lang.language} {lang.version or 'Unknown'} ({confidence_pct}%)"
281
+ )
282
+ if verbose:
283
+ print(f" Evidence: {', '.join(lang.evidence[:5])}")
284
+ else:
285
+ print("No languages detected")
286
+
287
+ # Frameworks
288
+ if analysis.frameworks:
289
+ print("\nDetected Frameworks:")
290
+ for fw in analysis.frameworks:
291
+ confidence_pct = int(fw.confidence * 100)
292
+ print(
293
+ f" - {fw.name} {fw.version or 'Unknown'} ({fw.category}) ({confidence_pct}%)"
294
+ )
295
+ else:
296
+ print("\nNo frameworks detected")
297
+
298
+ # Deployment targets
299
+ if analysis.deployment_targets:
300
+ print("\nDeployment Targets:")
301
+ for target in analysis.deployment_targets:
302
+ confidence_pct = int(target.confidence * 100)
303
+ print(f" - {target.target_type} ({confidence_pct}%)")
304
+ if verbose:
305
+ print(f" Evidence: {', '.join(target.evidence[:5])}")
306
+ else:
307
+ print("\nNo deployment targets detected")
308
+
309
+ print(
310
+ f"\nAnalysis complete: {len(analysis.languages)} language(s), "
311
+ f"{len(analysis.frameworks)} framework(s), "
312
+ f"{len(analysis.deployment_targets)} deployment target(s)"
313
+ )
314
+
315
+ return CommandResult.success_result()
316
+
317
+ def _output_json(self, analysis, verbose: bool) -> CommandResult:
318
+ """Output toolchain analysis as JSON."""
319
+ output = {
320
+ "project_path": analysis.project_path,
321
+ "analysis_time": analysis.analysis_time,
322
+ "languages": [
323
+ {
324
+ "language": lang.language,
325
+ "version": lang.version,
326
+ "confidence": lang.confidence,
327
+ "evidence": lang.evidence if verbose else None,
328
+ }
329
+ for lang in analysis.languages
330
+ ],
331
+ "frameworks": [
332
+ {
333
+ "name": fw.name,
334
+ "version": fw.version,
335
+ "category": (
336
+ fw.category.value
337
+ if hasattr(fw.category, "value")
338
+ else str(fw.category)
339
+ ),
340
+ "confidence": fw.confidence,
341
+ "config_file": fw.config_file,
342
+ }
343
+ for fw in analysis.frameworks
344
+ ],
345
+ "deployment_targets": [
346
+ {
347
+ "target_type": (
348
+ target.target_type.value
349
+ if hasattr(target.target_type, "value")
350
+ else str(target.target_type)
351
+ ),
352
+ "confidence": target.confidence,
353
+ "evidence": target.evidence if verbose else None,
354
+ }
355
+ for target in analysis.deployment_targets
356
+ ],
357
+ "components": [
358
+ {
359
+ "type": (
360
+ comp.type.value
361
+ if hasattr(comp.type, "value")
362
+ else str(comp.type)
363
+ ),
364
+ "name": comp.name,
365
+ "version": comp.version,
366
+ "confidence": comp.confidence,
367
+ }
368
+ for comp in analysis.components
369
+ ],
370
+ }
371
+
372
+ # Remove None values if not verbose
373
+ if not verbose:
374
+ for lang in output["languages"]:
375
+ lang.pop("evidence", None)
376
+ for target in output["deployment_targets"]:
377
+ target.pop("evidence", None)
378
+
379
+ print(json.dumps(output, indent=2))
380
+ return CommandResult.success_result(data=output)