claude-mpm 4.12.1__py3-none-any.whl → 4.13.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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +110 -459
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/circuit_breakers.md +638 -0
- claude_mpm/agents/templates/git_file_tracking.md +584 -0
- claude_mpm/agents/templates/pm_examples.md +474 -0
- claude_mpm/agents/templates/pm_red_flags.md +240 -0
- claude_mpm/agents/templates/response_format.md +583 -0
- claude_mpm/agents/templates/validation_templates.md +312 -0
- claude_mpm/cli/__init__.py +10 -0
- claude_mpm/cli/commands/agents.py +31 -0
- claude_mpm/cli/commands/agents_detect.py +380 -0
- claude_mpm/cli/commands/agents_recommend.py +309 -0
- claude_mpm/cli/commands/auto_configure.py +564 -0
- claude_mpm/cli/parsers/agents_parser.py +9 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +253 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/core/log_manager.py +2 -0
- claude_mpm/services/agents/__init__.py +18 -5
- claude_mpm/services/agents/auto_config_manager.py +797 -0
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +568 -0
- claude_mpm/services/core/__init__.py +33 -1
- claude_mpm/services/core/interfaces/__init__.py +16 -1
- claude_mpm/services/core/interfaces/agent.py +184 -0
- claude_mpm/services/core/interfaces/project.py +121 -0
- claude_mpm/services/core/models/__init__.py +46 -0
- claude_mpm/services/core/models/agent_config.py +397 -0
- claude_mpm/services/core/models/toolchain.py +306 -0
- claude_mpm/services/project/__init__.py +23 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/toolchain_analyzer.py +581 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/RECORD +38 -18
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.12.1.dist-info → claude_mpm-4.13.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Configuration Data Models for Claude MPM Framework
|
|
3
|
+
========================================================
|
|
4
|
+
|
|
5
|
+
WHY: These models represent agent capabilities, recommendations, and
|
|
6
|
+
configuration results. They provide a standardized way to communicate
|
|
7
|
+
agent suitability, deployment plans, and validation outcomes.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISION: Uses dataclasses with validation to ensure data consistency.
|
|
10
|
+
Includes confidence scores and reasoning to enable transparent decision-making.
|
|
11
|
+
Supports both successful and error states for robust error handling.
|
|
12
|
+
|
|
13
|
+
Part of TSK-0054: Auto-Configuration Feature - Phase 1
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AgentSpecialization(str, Enum):
|
|
22
|
+
"""Agent specialization categories.
|
|
23
|
+
|
|
24
|
+
WHY: Agents have different areas of expertise. This enum provides
|
|
25
|
+
a standardized taxonomy for categorizing agent capabilities.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
GENERAL = "general"
|
|
29
|
+
LANGUAGE_SPECIFIC = "language_specific"
|
|
30
|
+
FRAMEWORK_SPECIFIC = "framework_specific"
|
|
31
|
+
DEVOPS = "devops"
|
|
32
|
+
SECURITY = "security"
|
|
33
|
+
TESTING = "testing"
|
|
34
|
+
DOCUMENTATION = "documentation"
|
|
35
|
+
PERFORMANCE = "performance"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class AgentCapabilities:
|
|
40
|
+
"""Represents the capabilities of an agent.
|
|
41
|
+
|
|
42
|
+
WHY: Understanding what an agent can do is essential for matching
|
|
43
|
+
agents to projects. This model captures all relevant capability
|
|
44
|
+
information in a structured format.
|
|
45
|
+
|
|
46
|
+
DESIGN DECISION: Frozen to prevent modification of capability definitions.
|
|
47
|
+
Includes both broad categories (specializations) and specific skills
|
|
48
|
+
(languages, frameworks) to enable fine-grained matching.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
agent_id: str
|
|
52
|
+
agent_name: str
|
|
53
|
+
specializations: List[AgentSpecialization] = field(default_factory=list)
|
|
54
|
+
supported_languages: List[str] = field(default_factory=list)
|
|
55
|
+
supported_frameworks: List[str] = field(default_factory=list)
|
|
56
|
+
required_tools: List[str] = field(default_factory=list)
|
|
57
|
+
optional_tools: List[str] = field(default_factory=list)
|
|
58
|
+
deployment_targets: List[str] = field(default_factory=list)
|
|
59
|
+
description: str = ""
|
|
60
|
+
strengths: List[str] = field(default_factory=list)
|
|
61
|
+
limitations: List[str] = field(default_factory=list)
|
|
62
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
63
|
+
|
|
64
|
+
def __post_init__(self):
|
|
65
|
+
"""Validate agent capabilities."""
|
|
66
|
+
if not self.agent_id or not self.agent_id.strip():
|
|
67
|
+
raise ValueError("Agent ID cannot be empty")
|
|
68
|
+
if not self.agent_name or not self.agent_name.strip():
|
|
69
|
+
raise ValueError("Agent name cannot be empty")
|
|
70
|
+
|
|
71
|
+
def supports_language(self, language: str) -> bool:
|
|
72
|
+
"""Check if agent supports a specific language (case-insensitive)."""
|
|
73
|
+
return any(
|
|
74
|
+
lang.lower() == language.lower() for lang in self.supported_languages
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def supports_framework(self, framework: str) -> bool:
|
|
78
|
+
"""Check if agent supports a specific framework (case-insensitive)."""
|
|
79
|
+
return any(fw.lower() == framework.lower() for fw in self.supported_frameworks)
|
|
80
|
+
|
|
81
|
+
def has_specialization(self, specialization: AgentSpecialization) -> bool:
|
|
82
|
+
"""Check if agent has a specific specialization."""
|
|
83
|
+
return specialization in self.specializations
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class AgentRecommendation:
|
|
88
|
+
"""Represents a recommended agent with reasoning.
|
|
89
|
+
|
|
90
|
+
WHY: Users need to understand why agents are recommended. This model
|
|
91
|
+
captures the recommendation along with confidence score, match reasoning,
|
|
92
|
+
and any warnings or considerations.
|
|
93
|
+
|
|
94
|
+
DESIGN DECISION: Includes detailed reasoning to support transparency
|
|
95
|
+
and enable users to make informed decisions. Confidence score enables
|
|
96
|
+
filtering and ranking of recommendations.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
agent_id: str
|
|
100
|
+
agent_name: str
|
|
101
|
+
confidence_score: float # 0.0-1.0
|
|
102
|
+
match_reasons: List[str] = field(default_factory=list)
|
|
103
|
+
concerns: List[str] = field(default_factory=list)
|
|
104
|
+
capabilities: Optional[AgentCapabilities] = None
|
|
105
|
+
deployment_priority: int = 1 # Lower = higher priority
|
|
106
|
+
configuration_hints: Dict[str, Any] = field(default_factory=dict)
|
|
107
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
108
|
+
|
|
109
|
+
def __post_init__(self):
|
|
110
|
+
"""Validate agent recommendation."""
|
|
111
|
+
if not self.agent_id or not self.agent_id.strip():
|
|
112
|
+
raise ValueError("Agent ID cannot be empty")
|
|
113
|
+
if not self.agent_name or not self.agent_name.strip():
|
|
114
|
+
raise ValueError("Agent name cannot be empty")
|
|
115
|
+
if not (0.0 <= self.confidence_score <= 1.0):
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"Confidence score must be 0.0-1.0, got {self.confidence_score}"
|
|
118
|
+
)
|
|
119
|
+
if self.deployment_priority < 1:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"Deployment priority must be >= 1, got {self.deployment_priority}"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def is_high_confidence(self) -> bool:
|
|
126
|
+
"""Check if recommendation has high confidence (>= 0.8)."""
|
|
127
|
+
return self.confidence_score >= 0.8
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def is_medium_confidence(self) -> bool:
|
|
131
|
+
"""Check if recommendation has medium confidence (0.5-0.8)."""
|
|
132
|
+
return 0.5 <= self.confidence_score < 0.8
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def is_low_confidence(self) -> bool:
|
|
136
|
+
"""Check if recommendation has low confidence (< 0.5)."""
|
|
137
|
+
return self.confidence_score < 0.5
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def has_concerns(self) -> bool:
|
|
141
|
+
"""Check if recommendation has any concerns."""
|
|
142
|
+
return len(self.concerns) > 0
|
|
143
|
+
|
|
144
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
145
|
+
"""Convert recommendation to dictionary."""
|
|
146
|
+
return {
|
|
147
|
+
"agent_id": self.agent_id,
|
|
148
|
+
"agent_name": self.agent_name,
|
|
149
|
+
"confidence_score": self.confidence_score,
|
|
150
|
+
"match_reasons": self.match_reasons,
|
|
151
|
+
"concerns": self.concerns,
|
|
152
|
+
"deployment_priority": self.deployment_priority,
|
|
153
|
+
"configuration_hints": self.configuration_hints,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ConfigurationStatus(str, Enum):
|
|
158
|
+
"""Status of configuration operation.
|
|
159
|
+
|
|
160
|
+
WHY: Configuration can succeed, fail, or partially succeed. This enum
|
|
161
|
+
provides a standardized way to communicate operation outcomes.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
SUCCESS = "success"
|
|
165
|
+
PARTIAL_SUCCESS = "partial_success"
|
|
166
|
+
FAILURE = "failure"
|
|
167
|
+
VALIDATION_ERROR = "validation_error"
|
|
168
|
+
USER_CANCELLED = "user_cancelled"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@dataclass
|
|
172
|
+
class ConfigurationResult:
|
|
173
|
+
"""Result of automated configuration operation.
|
|
174
|
+
|
|
175
|
+
WHY: Configuration operations need to return comprehensive results
|
|
176
|
+
including what was deployed, what failed, and any warnings. This model
|
|
177
|
+
provides a complete picture of the configuration outcome.
|
|
178
|
+
|
|
179
|
+
DESIGN DECISION: Separates successful and failed deployments to enable
|
|
180
|
+
proper error handling. Includes validation results and user-facing
|
|
181
|
+
messages for transparency.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
status: ConfigurationStatus
|
|
185
|
+
deployed_agents: List[str] = field(default_factory=list)
|
|
186
|
+
failed_agents: List[str] = field(default_factory=list)
|
|
187
|
+
validation_warnings: List[str] = field(default_factory=list)
|
|
188
|
+
validation_errors: List[str] = field(default_factory=list)
|
|
189
|
+
recommendations: List[AgentRecommendation] = field(default_factory=list)
|
|
190
|
+
message: str = ""
|
|
191
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def is_successful(self) -> bool:
|
|
195
|
+
"""Check if configuration was completely successful."""
|
|
196
|
+
return self.status == ConfigurationStatus.SUCCESS
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def has_failures(self) -> bool:
|
|
200
|
+
"""Check if any agents failed to deploy."""
|
|
201
|
+
return len(self.failed_agents) > 0
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def has_warnings(self) -> bool:
|
|
205
|
+
"""Check if there are any warnings."""
|
|
206
|
+
return len(self.validation_warnings) > 0
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def deployment_count(self) -> int:
|
|
210
|
+
"""Get number of successfully deployed agents."""
|
|
211
|
+
return len(self.deployed_agents)
|
|
212
|
+
|
|
213
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
214
|
+
"""Convert result to dictionary."""
|
|
215
|
+
return {
|
|
216
|
+
"status": self.status.value,
|
|
217
|
+
"deployed_agents": self.deployed_agents,
|
|
218
|
+
"failed_agents": self.failed_agents,
|
|
219
|
+
"validation_warnings": self.validation_warnings,
|
|
220
|
+
"validation_errors": self.validation_errors,
|
|
221
|
+
"message": self.message,
|
|
222
|
+
"deployment_count": self.deployment_count,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class ValidationSeverity(str, Enum):
|
|
227
|
+
"""Severity level for validation issues.
|
|
228
|
+
|
|
229
|
+
WHY: Not all validation issues are equally critical. This enum enables
|
|
230
|
+
categorization of issues by severity to support appropriate handling.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
ERROR = "error" # Blocks deployment
|
|
234
|
+
WARNING = "warning" # Should be reviewed but doesn't block
|
|
235
|
+
INFO = "info" # Informational only
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass
|
|
239
|
+
class ValidationIssue:
|
|
240
|
+
"""Represents a validation issue.
|
|
241
|
+
|
|
242
|
+
WHY: Validation can identify multiple types of issues. This model
|
|
243
|
+
provides structured representation of issues with context and
|
|
244
|
+
suggested resolutions.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
severity: ValidationSeverity
|
|
248
|
+
message: str
|
|
249
|
+
agent_id: Optional[str] = None
|
|
250
|
+
field: Optional[str] = None
|
|
251
|
+
suggested_fix: Optional[str] = None
|
|
252
|
+
|
|
253
|
+
def __post_init__(self):
|
|
254
|
+
"""Validate issue data."""
|
|
255
|
+
if not self.message or not self.message.strip():
|
|
256
|
+
raise ValueError("Validation issue message cannot be empty")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@dataclass
|
|
260
|
+
class ValidationResult:
|
|
261
|
+
"""Result of configuration validation.
|
|
262
|
+
|
|
263
|
+
WHY: Validation produces multiple types of findings (errors, warnings, info).
|
|
264
|
+
This model aggregates all validation results and provides summary properties.
|
|
265
|
+
|
|
266
|
+
DESIGN DECISION: Separates issues by severity to enable appropriate handling.
|
|
267
|
+
Provides convenience properties for common checks (is_valid, has_errors).
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
is_valid: bool
|
|
271
|
+
issues: List[ValidationIssue] = field(default_factory=list)
|
|
272
|
+
validated_agents: List[str] = field(default_factory=list)
|
|
273
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def errors(self) -> List[ValidationIssue]:
|
|
277
|
+
"""Get all error-level issues."""
|
|
278
|
+
return [
|
|
279
|
+
issue for issue in self.issues if issue.severity == ValidationSeverity.ERROR
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def warnings(self) -> List[ValidationIssue]:
|
|
284
|
+
"""Get all warning-level issues."""
|
|
285
|
+
return [
|
|
286
|
+
issue
|
|
287
|
+
for issue in self.issues
|
|
288
|
+
if issue.severity == ValidationSeverity.WARNING
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def infos(self) -> List[ValidationIssue]:
|
|
293
|
+
"""Get all info-level issues."""
|
|
294
|
+
return [
|
|
295
|
+
issue for issue in self.issues if issue.severity == ValidationSeverity.INFO
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def has_errors(self) -> bool:
|
|
300
|
+
"""Check if validation has any errors."""
|
|
301
|
+
return len(self.errors) > 0
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def has_warnings(self) -> bool:
|
|
305
|
+
"""Check if validation has any warnings."""
|
|
306
|
+
return len(self.warnings) > 0
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def error_count(self) -> int:
|
|
310
|
+
"""Get number of errors."""
|
|
311
|
+
return len(self.errors)
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def warning_count(self) -> int:
|
|
315
|
+
"""Get number of warnings."""
|
|
316
|
+
return len(self.warnings)
|
|
317
|
+
|
|
318
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
319
|
+
"""Convert validation result to dictionary."""
|
|
320
|
+
return {
|
|
321
|
+
"is_valid": self.is_valid,
|
|
322
|
+
"error_count": self.error_count,
|
|
323
|
+
"warning_count": self.warning_count,
|
|
324
|
+
"validated_agents": self.validated_agents,
|
|
325
|
+
"errors": [
|
|
326
|
+
{
|
|
327
|
+
"severity": issue.severity.value,
|
|
328
|
+
"message": issue.message,
|
|
329
|
+
"agent_id": issue.agent_id,
|
|
330
|
+
}
|
|
331
|
+
for issue in self.errors
|
|
332
|
+
],
|
|
333
|
+
"warnings": [
|
|
334
|
+
{
|
|
335
|
+
"severity": issue.severity.value,
|
|
336
|
+
"message": issue.message,
|
|
337
|
+
"agent_id": issue.agent_id,
|
|
338
|
+
}
|
|
339
|
+
for issue in self.warnings
|
|
340
|
+
],
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@dataclass
|
|
345
|
+
class ConfigurationPreview:
|
|
346
|
+
"""Preview of what would be configured.
|
|
347
|
+
|
|
348
|
+
WHY: Users need to see what would change before committing. This model
|
|
349
|
+
provides a complete preview including recommendations, validation results,
|
|
350
|
+
and estimated impact.
|
|
351
|
+
|
|
352
|
+
DESIGN DECISION: Includes validation results to show potential issues
|
|
353
|
+
before deployment. Provides summary statistics for quick assessment.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
recommendations: List[AgentRecommendation] = field(default_factory=list)
|
|
357
|
+
validation_result: Optional[ValidationResult] = None
|
|
358
|
+
estimated_deployment_time: float = 0.0 # seconds
|
|
359
|
+
would_deploy: List[str] = field(default_factory=list)
|
|
360
|
+
would_skip: List[str] = field(default_factory=list)
|
|
361
|
+
requires_confirmation: bool = True
|
|
362
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def deployment_count(self) -> int:
|
|
366
|
+
"""Get number of agents that would be deployed."""
|
|
367
|
+
return len(self.would_deploy)
|
|
368
|
+
|
|
369
|
+
@property
|
|
370
|
+
def skip_count(self) -> int:
|
|
371
|
+
"""Get number of agents that would be skipped."""
|
|
372
|
+
return len(self.would_skip)
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def is_valid(self) -> bool:
|
|
376
|
+
"""Check if preview represents a valid configuration."""
|
|
377
|
+
if self.validation_result is None:
|
|
378
|
+
return True
|
|
379
|
+
return self.validation_result.is_valid
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def high_confidence_count(self) -> int:
|
|
383
|
+
"""Get number of high-confidence recommendations."""
|
|
384
|
+
return sum(1 for rec in self.recommendations if rec.is_high_confidence)
|
|
385
|
+
|
|
386
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
387
|
+
"""Convert preview to dictionary."""
|
|
388
|
+
return {
|
|
389
|
+
"deployment_count": self.deployment_count,
|
|
390
|
+
"skip_count": self.skip_count,
|
|
391
|
+
"high_confidence_count": self.high_confidence_count,
|
|
392
|
+
"estimated_deployment_time": self.estimated_deployment_time,
|
|
393
|
+
"is_valid": self.is_valid,
|
|
394
|
+
"would_deploy": self.would_deploy,
|
|
395
|
+
"would_skip": self.would_skip,
|
|
396
|
+
"recommendations": [rec.to_dict() for rec in self.recommendations],
|
|
397
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Toolchain Data Models for Claude MPM Framework
|
|
3
|
+
==============================================
|
|
4
|
+
|
|
5
|
+
WHY: These models represent the structure of project toolchain analysis results.
|
|
6
|
+
They provide a standardized way to represent detected languages, frameworks,
|
|
7
|
+
deployment targets, and overall toolchain characteristics.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISION: Uses dataclasses with field validation and default values
|
|
10
|
+
to ensure data consistency. Confidence levels are included to represent
|
|
11
|
+
uncertainty in detection. Immutable where possible to prevent accidental
|
|
12
|
+
modification of analysis results.
|
|
13
|
+
|
|
14
|
+
Part of TSK-0054: Auto-Configuration Feature - Phase 1
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ConfidenceLevel(str, Enum):
|
|
24
|
+
"""Confidence level for detection results.
|
|
25
|
+
|
|
26
|
+
WHY: Not all detections are equally certain. This enum provides a
|
|
27
|
+
standardized way to communicate confidence levels to users and
|
|
28
|
+
enable threshold-based decision making.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
HIGH = "high" # >80% confidence, very strong indicators
|
|
32
|
+
MEDIUM = "medium" # 50-80% confidence, good indicators
|
|
33
|
+
LOW = "low" # 20-50% confidence, weak indicators
|
|
34
|
+
VERY_LOW = "very_low" # <20% confidence, speculative
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class ToolchainComponent:
|
|
39
|
+
"""Represents a component in the project's toolchain.
|
|
40
|
+
|
|
41
|
+
WHY: Toolchain components (languages, frameworks, tools) share common
|
|
42
|
+
attributes like name, version, and confidence. This base model enables
|
|
43
|
+
consistent representation across different component types.
|
|
44
|
+
|
|
45
|
+
DESIGN DECISION: Frozen dataclass to prevent modification after creation,
|
|
46
|
+
ensuring analysis results remain consistent. Version is optional as not
|
|
47
|
+
all components have detectable versions.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
name: str
|
|
51
|
+
version: Optional[str] = None
|
|
52
|
+
confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
53
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
|
|
55
|
+
def __post_init__(self):
|
|
56
|
+
"""Validate component data after initialization."""
|
|
57
|
+
if not self.name or not self.name.strip():
|
|
58
|
+
raise ValueError("Component name cannot be empty")
|
|
59
|
+
if self.version is not None and not self.version.strip():
|
|
60
|
+
raise ValueError("Component version cannot be empty string")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True)
|
|
64
|
+
class LanguageDetection:
|
|
65
|
+
"""Result of language detection analysis.
|
|
66
|
+
|
|
67
|
+
WHY: Projects often use multiple languages. This model captures both
|
|
68
|
+
primary (main codebase) and secondary (scripts, config) languages
|
|
69
|
+
with their relative proportions and confidence levels.
|
|
70
|
+
|
|
71
|
+
DESIGN DECISION: Includes percentage breakdown to help understand
|
|
72
|
+
language distribution. Confidence per language enables threshold-based
|
|
73
|
+
filtering of uncertain detections.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
primary_language: str
|
|
77
|
+
primary_version: Optional[str] = None
|
|
78
|
+
primary_confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
79
|
+
secondary_languages: List[ToolchainComponent] = field(default_factory=list)
|
|
80
|
+
language_percentages: Dict[str, float] = field(default_factory=dict)
|
|
81
|
+
|
|
82
|
+
def __post_init__(self):
|
|
83
|
+
"""Validate language detection data."""
|
|
84
|
+
if not self.primary_language or not self.primary_language.strip():
|
|
85
|
+
raise ValueError("Primary language cannot be empty")
|
|
86
|
+
|
|
87
|
+
# Validate language percentages sum to ~100% (allow small floating point error)
|
|
88
|
+
if self.language_percentages:
|
|
89
|
+
total = sum(self.language_percentages.values())
|
|
90
|
+
if not (99.0 <= total <= 101.0):
|
|
91
|
+
raise ValueError(f"Language percentages must sum to 100%, got {total}%")
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def all_languages(self) -> List[str]:
|
|
95
|
+
"""Get list of all detected languages (primary + secondary)."""
|
|
96
|
+
languages = [self.primary_language]
|
|
97
|
+
languages.extend(comp.name for comp in self.secondary_languages)
|
|
98
|
+
return languages
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def high_confidence_languages(self) -> List[str]:
|
|
102
|
+
"""Get languages detected with high confidence."""
|
|
103
|
+
languages = []
|
|
104
|
+
if self.primary_confidence == ConfidenceLevel.HIGH:
|
|
105
|
+
languages.append(self.primary_language)
|
|
106
|
+
languages.extend(
|
|
107
|
+
comp.name
|
|
108
|
+
for comp in self.secondary_languages
|
|
109
|
+
if comp.confidence == ConfidenceLevel.HIGH
|
|
110
|
+
)
|
|
111
|
+
return languages
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True)
|
|
115
|
+
class Framework:
|
|
116
|
+
"""Represents a detected framework or library.
|
|
117
|
+
|
|
118
|
+
WHY: Frameworks are critical for agent recommendation as different agents
|
|
119
|
+
specialize in different frameworks. This model captures framework identity,
|
|
120
|
+
version, type, and usage characteristics.
|
|
121
|
+
|
|
122
|
+
DESIGN DECISION: Includes framework type (web, testing, ORM, etc.) to
|
|
123
|
+
enable category-based recommendations. Popularity metric helps prioritize
|
|
124
|
+
agent recommendations for commonly-used frameworks.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
name: str
|
|
128
|
+
version: Optional[str] = None
|
|
129
|
+
framework_type: Optional[str] = None # web, testing, orm, cli, etc.
|
|
130
|
+
confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
131
|
+
is_dev_dependency: bool = False
|
|
132
|
+
popularity_score: float = 0.0 # 0.0-1.0, higher = more popular/used
|
|
133
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
134
|
+
|
|
135
|
+
def __post_init__(self):
|
|
136
|
+
"""Validate framework data."""
|
|
137
|
+
if not self.name or not self.name.strip():
|
|
138
|
+
raise ValueError("Framework name cannot be empty")
|
|
139
|
+
if not (0.0 <= self.popularity_score <= 1.0):
|
|
140
|
+
raise ValueError(
|
|
141
|
+
f"Popularity score must be 0.0-1.0, got {self.popularity_score}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def display_name(self) -> str:
|
|
146
|
+
"""Get formatted display name with version."""
|
|
147
|
+
if self.version:
|
|
148
|
+
return f"{self.name} {self.version}"
|
|
149
|
+
return self.name
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class DeploymentTarget:
|
|
154
|
+
"""Represents the detected deployment target environment.
|
|
155
|
+
|
|
156
|
+
WHY: Deployment target affects agent recommendations (e.g., DevOps agents
|
|
157
|
+
for Kubernetes, serverless agents for Lambda). This model captures the
|
|
158
|
+
deployment platform and configuration details.
|
|
159
|
+
|
|
160
|
+
DESIGN DECISION: Includes target type (cloud, container, serverless, etc.)
|
|
161
|
+
and platform-specific configuration. Confidence level enables fallback
|
|
162
|
+
to generic recommendations when deployment target is unclear.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
target_type: str # cloud, container, serverless, on-premise, edge
|
|
166
|
+
platform: Optional[str] = None # aws, gcp, azure, kubernetes, docker, etc.
|
|
167
|
+
configuration: Dict[str, Any] = field(default_factory=dict)
|
|
168
|
+
confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
169
|
+
requires_ops_agent: bool = False
|
|
170
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
171
|
+
|
|
172
|
+
def __post_init__(self):
|
|
173
|
+
"""Validate deployment target data."""
|
|
174
|
+
valid_types = {"cloud", "container", "serverless", "on-premise", "edge"}
|
|
175
|
+
if self.target_type not in valid_types:
|
|
176
|
+
raise ValueError(
|
|
177
|
+
f"Invalid target_type '{self.target_type}'. "
|
|
178
|
+
f"Must be one of: {valid_types}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def display_name(self) -> str:
|
|
183
|
+
"""Get formatted display name."""
|
|
184
|
+
if self.platform:
|
|
185
|
+
return f"{self.target_type} ({self.platform})"
|
|
186
|
+
return self.target_type
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass
|
|
190
|
+
class ToolchainAnalysis:
|
|
191
|
+
"""Complete toolchain analysis result.
|
|
192
|
+
|
|
193
|
+
WHY: This is the primary output of toolchain analysis, aggregating all
|
|
194
|
+
detected components into a single structure. It provides a complete
|
|
195
|
+
picture of the project's technical stack.
|
|
196
|
+
|
|
197
|
+
DESIGN DECISION: Not frozen to allow caching and updating of analysis
|
|
198
|
+
results. Includes project_path for reference and validation. Provides
|
|
199
|
+
convenience methods for common queries (e.g., has framework, get languages).
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
project_path: Path
|
|
203
|
+
language_detection: LanguageDetection
|
|
204
|
+
frameworks: List[Framework] = field(default_factory=list)
|
|
205
|
+
deployment_target: Optional[DeploymentTarget] = None
|
|
206
|
+
build_tools: List[ToolchainComponent] = field(default_factory=list)
|
|
207
|
+
package_managers: List[ToolchainComponent] = field(default_factory=list)
|
|
208
|
+
development_tools: List[ToolchainComponent] = field(default_factory=list)
|
|
209
|
+
overall_confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
210
|
+
analysis_timestamp: Optional[float] = None
|
|
211
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
212
|
+
|
|
213
|
+
def __post_init__(self):
|
|
214
|
+
"""Validate toolchain analysis data."""
|
|
215
|
+
if not self.project_path.exists():
|
|
216
|
+
raise ValueError(f"Project path does not exist: {self.project_path}")
|
|
217
|
+
if not self.project_path.is_dir():
|
|
218
|
+
raise ValueError(f"Project path is not a directory: {self.project_path}")
|
|
219
|
+
|
|
220
|
+
def has_framework(self, framework_name: str) -> bool:
|
|
221
|
+
"""Check if a specific framework is detected."""
|
|
222
|
+
return any(fw.name.lower() == framework_name.lower() for fw in self.frameworks)
|
|
223
|
+
|
|
224
|
+
def get_framework(self, framework_name: str) -> Optional[Framework]:
|
|
225
|
+
"""Get framework by name (case-insensitive)."""
|
|
226
|
+
for fw in self.frameworks:
|
|
227
|
+
if fw.name.lower() == framework_name.lower():
|
|
228
|
+
return fw
|
|
229
|
+
return None
|
|
230
|
+
|
|
231
|
+
def get_frameworks_by_type(self, framework_type: str) -> List[Framework]:
|
|
232
|
+
"""Get all frameworks of a specific type."""
|
|
233
|
+
return [
|
|
234
|
+
fw
|
|
235
|
+
for fw in self.frameworks
|
|
236
|
+
if fw.framework_type and fw.framework_type.lower() == framework_type.lower()
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def primary_language(self) -> str:
|
|
241
|
+
"""Get the primary language detected."""
|
|
242
|
+
return self.language_detection.primary_language
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def all_languages(self) -> List[str]:
|
|
246
|
+
"""Get all detected languages."""
|
|
247
|
+
return self.language_detection.all_languages
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def web_frameworks(self) -> List[Framework]:
|
|
251
|
+
"""Get all web frameworks."""
|
|
252
|
+
return self.get_frameworks_by_type("web")
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def is_web_project(self) -> bool:
|
|
256
|
+
"""Check if this appears to be a web project."""
|
|
257
|
+
return len(self.web_frameworks) > 0
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def requires_devops_agent(self) -> bool:
|
|
261
|
+
"""Check if project likely needs DevOps agent."""
|
|
262
|
+
if self.deployment_target and self.deployment_target.requires_ops_agent:
|
|
263
|
+
return True
|
|
264
|
+
# Check for containerization
|
|
265
|
+
return any(
|
|
266
|
+
tool.name.lower() in {"docker", "kubernetes", "terraform"}
|
|
267
|
+
for tool in self.development_tools
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
271
|
+
"""Convert analysis to dictionary for serialization."""
|
|
272
|
+
return {
|
|
273
|
+
"project_path": str(self.project_path),
|
|
274
|
+
"language_detection": {
|
|
275
|
+
"primary_language": self.language_detection.primary_language,
|
|
276
|
+
"primary_version": self.language_detection.primary_version,
|
|
277
|
+
"primary_confidence": self.language_detection.primary_confidence.value,
|
|
278
|
+
"secondary_languages": [
|
|
279
|
+
{"name": lang.name, "version": lang.version}
|
|
280
|
+
for lang in self.language_detection.secondary_languages
|
|
281
|
+
],
|
|
282
|
+
"language_percentages": self.language_detection.language_percentages,
|
|
283
|
+
},
|
|
284
|
+
"frameworks": [
|
|
285
|
+
{
|
|
286
|
+
"name": fw.name,
|
|
287
|
+
"version": fw.version,
|
|
288
|
+
"type": fw.framework_type,
|
|
289
|
+
"confidence": fw.confidence.value,
|
|
290
|
+
}
|
|
291
|
+
for fw in self.frameworks
|
|
292
|
+
],
|
|
293
|
+
"deployment_target": (
|
|
294
|
+
{
|
|
295
|
+
"type": self.deployment_target.target_type,
|
|
296
|
+
"platform": self.deployment_target.platform,
|
|
297
|
+
"confidence": self.deployment_target.confidence.value,
|
|
298
|
+
}
|
|
299
|
+
if self.deployment_target
|
|
300
|
+
else None
|
|
301
|
+
),
|
|
302
|
+
"build_tools": [tool.name for tool in self.build_tools],
|
|
303
|
+
"package_managers": [pm.name for pm in self.package_managers],
|
|
304
|
+
"overall_confidence": self.overall_confidence.value,
|
|
305
|
+
"metadata": self.metadata,
|
|
306
|
+
}
|