skill-seekers 2.7.3__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 (79) hide show
  1. skill_seekers/__init__.py +22 -0
  2. skill_seekers/cli/__init__.py +39 -0
  3. skill_seekers/cli/adaptors/__init__.py +120 -0
  4. skill_seekers/cli/adaptors/base.py +221 -0
  5. skill_seekers/cli/adaptors/claude.py +485 -0
  6. skill_seekers/cli/adaptors/gemini.py +453 -0
  7. skill_seekers/cli/adaptors/markdown.py +269 -0
  8. skill_seekers/cli/adaptors/openai.py +503 -0
  9. skill_seekers/cli/ai_enhancer.py +310 -0
  10. skill_seekers/cli/api_reference_builder.py +373 -0
  11. skill_seekers/cli/architectural_pattern_detector.py +525 -0
  12. skill_seekers/cli/code_analyzer.py +1462 -0
  13. skill_seekers/cli/codebase_scraper.py +1225 -0
  14. skill_seekers/cli/config_command.py +563 -0
  15. skill_seekers/cli/config_enhancer.py +431 -0
  16. skill_seekers/cli/config_extractor.py +871 -0
  17. skill_seekers/cli/config_manager.py +452 -0
  18. skill_seekers/cli/config_validator.py +394 -0
  19. skill_seekers/cli/conflict_detector.py +528 -0
  20. skill_seekers/cli/constants.py +72 -0
  21. skill_seekers/cli/dependency_analyzer.py +757 -0
  22. skill_seekers/cli/doc_scraper.py +2332 -0
  23. skill_seekers/cli/enhance_skill.py +488 -0
  24. skill_seekers/cli/enhance_skill_local.py +1096 -0
  25. skill_seekers/cli/enhance_status.py +194 -0
  26. skill_seekers/cli/estimate_pages.py +433 -0
  27. skill_seekers/cli/generate_router.py +1209 -0
  28. skill_seekers/cli/github_fetcher.py +534 -0
  29. skill_seekers/cli/github_scraper.py +1466 -0
  30. skill_seekers/cli/guide_enhancer.py +723 -0
  31. skill_seekers/cli/how_to_guide_builder.py +1267 -0
  32. skill_seekers/cli/install_agent.py +461 -0
  33. skill_seekers/cli/install_skill.py +178 -0
  34. skill_seekers/cli/language_detector.py +614 -0
  35. skill_seekers/cli/llms_txt_detector.py +60 -0
  36. skill_seekers/cli/llms_txt_downloader.py +104 -0
  37. skill_seekers/cli/llms_txt_parser.py +150 -0
  38. skill_seekers/cli/main.py +558 -0
  39. skill_seekers/cli/markdown_cleaner.py +132 -0
  40. skill_seekers/cli/merge_sources.py +806 -0
  41. skill_seekers/cli/package_multi.py +77 -0
  42. skill_seekers/cli/package_skill.py +241 -0
  43. skill_seekers/cli/pattern_recognizer.py +1825 -0
  44. skill_seekers/cli/pdf_extractor_poc.py +1166 -0
  45. skill_seekers/cli/pdf_scraper.py +617 -0
  46. skill_seekers/cli/quality_checker.py +519 -0
  47. skill_seekers/cli/rate_limit_handler.py +438 -0
  48. skill_seekers/cli/resume_command.py +160 -0
  49. skill_seekers/cli/run_tests.py +230 -0
  50. skill_seekers/cli/setup_wizard.py +93 -0
  51. skill_seekers/cli/split_config.py +390 -0
  52. skill_seekers/cli/swift_patterns.py +560 -0
  53. skill_seekers/cli/test_example_extractor.py +1081 -0
  54. skill_seekers/cli/test_unified_simple.py +179 -0
  55. skill_seekers/cli/unified_codebase_analyzer.py +572 -0
  56. skill_seekers/cli/unified_scraper.py +932 -0
  57. skill_seekers/cli/unified_skill_builder.py +1605 -0
  58. skill_seekers/cli/upload_skill.py +162 -0
  59. skill_seekers/cli/utils.py +432 -0
  60. skill_seekers/mcp/__init__.py +33 -0
  61. skill_seekers/mcp/agent_detector.py +316 -0
  62. skill_seekers/mcp/git_repo.py +273 -0
  63. skill_seekers/mcp/server.py +231 -0
  64. skill_seekers/mcp/server_fastmcp.py +1249 -0
  65. skill_seekers/mcp/server_legacy.py +2302 -0
  66. skill_seekers/mcp/source_manager.py +285 -0
  67. skill_seekers/mcp/tools/__init__.py +115 -0
  68. skill_seekers/mcp/tools/config_tools.py +251 -0
  69. skill_seekers/mcp/tools/packaging_tools.py +826 -0
  70. skill_seekers/mcp/tools/scraping_tools.py +842 -0
  71. skill_seekers/mcp/tools/source_tools.py +828 -0
  72. skill_seekers/mcp/tools/splitting_tools.py +212 -0
  73. skill_seekers/py.typed +0 -0
  74. skill_seekers-2.7.3.dist-info/METADATA +2027 -0
  75. skill_seekers-2.7.3.dist-info/RECORD +79 -0
  76. skill_seekers-2.7.3.dist-info/WHEEL +5 -0
  77. skill_seekers-2.7.3.dist-info/entry_points.txt +19 -0
  78. skill_seekers-2.7.3.dist-info/licenses/LICENSE +21 -0
  79. skill_seekers-2.7.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,525 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Architectural Pattern Detection (C3.7)
4
+
5
+ Detects high-level architectural patterns by analyzing multi-file relationships,
6
+ directory structures, and framework conventions.
7
+
8
+ Detected Patterns:
9
+ - MVC (Model-View-Controller)
10
+ - MVVM (Model-View-ViewModel)
11
+ - MVP (Model-View-Presenter)
12
+ - Repository Pattern
13
+ - Service Layer Pattern
14
+ - Layered Architecture (3-tier, N-tier)
15
+ - Clean Architecture
16
+ - Hexagonal/Ports & Adapters
17
+
18
+ Credits:
19
+ - Architectural pattern definitions from industry standards
20
+ - Framework detection based on official documentation
21
+ """
22
+
23
+ import logging
24
+ from collections import defaultdict
25
+ from dataclasses import dataclass
26
+ from pathlib import Path
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ @dataclass
32
+ class ArchitecturalPattern:
33
+ """Detected architectural pattern"""
34
+
35
+ pattern_name: str # e.g., "MVC", "MVVM", "Repository"
36
+ confidence: float # 0.0-1.0
37
+ evidence: list[str] # List of evidence supporting detection
38
+ components: dict[str, list[str]] # Component type -> file paths
39
+ framework: str | None = None # Detected framework (Django, Spring, etc.)
40
+ description: str = "" # Human-readable description
41
+
42
+
43
+ @dataclass
44
+ class ArchitecturalReport:
45
+ """Complete architectural analysis report"""
46
+
47
+ patterns: list[ArchitecturalPattern]
48
+ directory_structure: dict[str, int] # Directory name -> file count
49
+ total_files_analyzed: int
50
+ frameworks_detected: list[str]
51
+ ai_analysis: dict | None = None # AI enhancement (C3.6 integration)
52
+
53
+ def to_dict(self) -> dict:
54
+ """Export to dictionary"""
55
+ return {
56
+ "patterns": [
57
+ {
58
+ "pattern_name": p.pattern_name,
59
+ "confidence": p.confidence,
60
+ "evidence": p.evidence,
61
+ "components": p.components,
62
+ "framework": p.framework,
63
+ "description": p.description,
64
+ }
65
+ for p in self.patterns
66
+ ],
67
+ "directory_structure": self.directory_structure,
68
+ "total_files_analyzed": self.total_files_analyzed,
69
+ "frameworks_detected": self.frameworks_detected,
70
+ "ai_analysis": self.ai_analysis,
71
+ }
72
+
73
+
74
+ class ArchitecturalPatternDetector:
75
+ """
76
+ Detect high-level architectural patterns.
77
+
78
+ Analyzes entire codebase structure, not individual files.
79
+ """
80
+
81
+ # Common directory patterns for architectures
82
+ MVC_DIRS = {"models", "views", "controllers", "model", "view", "controller"}
83
+ MVVM_DIRS = {"models", "views", "viewmodels", "viewmodel"}
84
+ LAYERED_DIRS = {"presentation", "business", "data", "dal", "bll", "ui"}
85
+ CLEAN_ARCH_DIRS = {"domain", "application", "infrastructure", "presentation"}
86
+ REPO_DIRS = {"repositories", "repository"}
87
+ SERVICE_DIRS = {"services", "service"}
88
+
89
+ # Framework detection patterns
90
+ FRAMEWORK_MARKERS = {
91
+ "Django": ["django", "manage.py", "settings.py", "urls.py"],
92
+ "Flask": ["flask", "app.py", "wsgi.py"],
93
+ "Spring": ["springframework", "@Controller", "@Service", "@Repository"],
94
+ "ASP.NET": ["Controllers", "Models", "Views", ".cshtml", "Startup.cs"],
95
+ "Rails": ["app/models", "app/views", "app/controllers", "config/routes.rb"],
96
+ "Angular": ["app.module.ts", "@Component", "@Injectable", "angular.json"],
97
+ "React": ["package.json", "react", "components"],
98
+ "Vue.js": ["vue", ".vue", "components"],
99
+ "Express": ["express", "app.js", "routes"],
100
+ "Laravel": ["artisan", "app/Http/Controllers", "app/Models"],
101
+ }
102
+
103
+ def __init__(self, enhance_with_ai: bool = True):
104
+ """
105
+ Initialize detector.
106
+
107
+ Args:
108
+ enhance_with_ai: Enable AI enhancement for detected patterns (C3.6)
109
+ """
110
+ self.enhance_with_ai = enhance_with_ai
111
+ self.ai_enhancer = None
112
+
113
+ if self.enhance_with_ai:
114
+ try:
115
+ from skill_seekers.cli.ai_enhancer import AIEnhancer
116
+
117
+ self.ai_enhancer = AIEnhancer()
118
+ except Exception as e:
119
+ logger.warning(f"⚠️ Failed to initialize AI enhancer: {e}")
120
+ self.enhance_with_ai = False
121
+
122
+ def analyze(self, directory: Path, files_analysis: list[dict]) -> ArchitecturalReport:
123
+ """
124
+ Analyze codebase for architectural patterns.
125
+
126
+ Args:
127
+ directory: Root directory of codebase
128
+ files_analysis: List of analyzed files from CodeAnalyzer
129
+
130
+ Returns:
131
+ ArchitecturalReport with detected patterns
132
+ """
133
+ logger.info(f"🏗️ Analyzing architectural patterns in {directory}")
134
+
135
+ # Build directory structure map
136
+ dir_structure = self._analyze_directory_structure(directory)
137
+
138
+ # Detect frameworks
139
+ frameworks = self._detect_frameworks(directory, files_analysis)
140
+
141
+ # Detect architectural patterns
142
+ patterns = []
143
+
144
+ patterns.extend(self._detect_mvc(dir_structure, files_analysis, frameworks))
145
+ patterns.extend(self._detect_mvvm(dir_structure, files_analysis, frameworks))
146
+ patterns.extend(self._detect_repository(dir_structure, files_analysis))
147
+ patterns.extend(self._detect_service_layer(dir_structure, files_analysis))
148
+ patterns.extend(self._detect_layered_architecture(dir_structure, files_analysis))
149
+ patterns.extend(self._detect_clean_architecture(dir_structure, files_analysis))
150
+
151
+ report = ArchitecturalReport(
152
+ patterns=patterns,
153
+ directory_structure=dir_structure,
154
+ total_files_analyzed=len(files_analysis),
155
+ frameworks_detected=frameworks,
156
+ )
157
+
158
+ # Enhance with AI if enabled (C3.6)
159
+ if self.enhance_with_ai and self.ai_enhancer and patterns:
160
+ report.ai_analysis = self._enhance_with_ai(report)
161
+
162
+ logger.info(f"✅ Detected {len(patterns)} architectural patterns")
163
+ return report
164
+
165
+ def _analyze_directory_structure(self, directory: Path) -> dict[str, int]:
166
+ """Analyze directory structure and count files"""
167
+ structure = defaultdict(int)
168
+
169
+ for path in directory.rglob("*"):
170
+ if path.is_file():
171
+ # Get relative directory path
172
+ rel_dir = path.parent.relative_to(directory)
173
+ dir_name = str(rel_dir).lower()
174
+
175
+ # Extract top-level and leaf directory names
176
+ parts = Path(dir_name).parts
177
+ if parts:
178
+ structure[parts[0]] += 1 # Top-level dir
179
+ if len(parts) > 1:
180
+ structure[parts[-1]] += 1 # Leaf dir
181
+
182
+ return dict(structure)
183
+
184
+ def _detect_frameworks(self, _directory: Path, files: list[dict]) -> list[str]:
185
+ """Detect frameworks being used"""
186
+ detected = []
187
+
188
+ # Check file paths and content
189
+ all_paths = [str(f.get("file", "")) for f in files]
190
+ all_content = " ".join(all_paths)
191
+
192
+ for framework, markers in self.FRAMEWORK_MARKERS.items():
193
+ matches = sum(1 for marker in markers if marker.lower() in all_content.lower())
194
+ if matches >= 2: # Require at least 2 markers
195
+ detected.append(framework)
196
+ logger.info(f" 📦 Detected framework: {framework}")
197
+
198
+ return detected
199
+
200
+ def _detect_mvc(
201
+ self, dirs: dict[str, int], files: list[dict], frameworks: list[str]
202
+ ) -> list[ArchitecturalPattern]:
203
+ """Detect MVC pattern"""
204
+ patterns = []
205
+
206
+ # Check for MVC directory structure
207
+ mvc_dir_matches = sum(1 for d in self.MVC_DIRS if d in dirs)
208
+ has_mvc_structure = mvc_dir_matches >= 2
209
+
210
+ if not has_mvc_structure:
211
+ return patterns
212
+
213
+ # Build evidence
214
+ evidence = []
215
+ components = defaultdict(list)
216
+
217
+ # Find MVC files
218
+ for file in files:
219
+ file_path = str(file.get("file", "")).lower()
220
+
221
+ if "model" in file_path and ("models/" in file_path or "/model/" in file_path):
222
+ components["Models"].append(file.get("file", ""))
223
+ if len(components["Models"]) == 1:
224
+ evidence.append("Models directory with model classes")
225
+
226
+ if "view" in file_path and ("views/" in file_path or "/view/" in file_path):
227
+ components["Views"].append(file.get("file", ""))
228
+ if len(components["Views"]) == 1:
229
+ evidence.append("Views directory with view files")
230
+
231
+ if "controller" in file_path and (
232
+ "controllers/" in file_path or "/controller/" in file_path
233
+ ):
234
+ components["Controllers"].append(file.get("file", ""))
235
+ if len(components["Controllers"]) == 1:
236
+ evidence.append("Controllers directory with controller classes")
237
+
238
+ # Calculate confidence
239
+ has_models = len(components["Models"]) > 0
240
+ has_views = len(components["Views"]) > 0
241
+ has_controllers = len(components["Controllers"]) > 0
242
+
243
+ if sum([has_models, has_views, has_controllers]) >= 2:
244
+ confidence = 0.6 + (sum([has_models, has_views, has_controllers]) * 0.15)
245
+
246
+ # Boost confidence if framework detected
247
+ framework = None
248
+ for fw in ["Django", "Flask", "Spring", "ASP.NET", "Rails", "Laravel"]:
249
+ if fw in frameworks:
250
+ confidence = min(0.95, confidence + 0.1)
251
+ framework = fw
252
+ evidence.append(f"{fw} framework detected (uses MVC)")
253
+ break
254
+
255
+ patterns.append(
256
+ ArchitecturalPattern(
257
+ pattern_name="MVC (Model-View-Controller)",
258
+ confidence=confidence,
259
+ evidence=evidence,
260
+ components=dict(components),
261
+ framework=framework,
262
+ description="Separates application into Models (data), Views (UI), and Controllers (logic)",
263
+ )
264
+ )
265
+
266
+ return patterns
267
+
268
+ def _detect_mvvm(
269
+ self, dirs: dict[str, int], files: list[dict], frameworks: list[str]
270
+ ) -> list[ArchitecturalPattern]:
271
+ """Detect MVVM pattern"""
272
+ patterns = []
273
+
274
+ # Look for ViewModels directory or classes ending with ViewModel
275
+ has_viewmodel_dir = "viewmodels" in dirs or "viewmodel" in dirs
276
+ viewmodel_files = [f for f in files if "viewmodel" in str(f.get("file", "")).lower()]
277
+
278
+ if not (has_viewmodel_dir or len(viewmodel_files) >= 2):
279
+ return patterns
280
+
281
+ evidence = []
282
+ components = defaultdict(list)
283
+
284
+ # Find MVVM files
285
+ for file in files:
286
+ file_path = str(file.get("file", "")).lower()
287
+ classes = file.get("classes", [])
288
+
289
+ if "model" in file_path and "viewmodel" not in file_path:
290
+ components["Models"].append(file.get("file", ""))
291
+
292
+ if "view" in file_path:
293
+ components["Views"].append(file.get("file", ""))
294
+
295
+ if "viewmodel" in file_path or any(
296
+ "viewmodel" in c.get("name", "").lower() for c in classes
297
+ ):
298
+ components["ViewModels"].append(file.get("file", ""))
299
+
300
+ if len(components["ViewModels"]) >= 2:
301
+ evidence.append(
302
+ f"ViewModels directory with {len(components['ViewModels'])} ViewModel classes"
303
+ )
304
+
305
+ if len(components["Views"]) >= 2:
306
+ evidence.append(f"Views directory with {len(components['Views'])} view files")
307
+
308
+ if len(components["Models"]) >= 1:
309
+ evidence.append(f"Models directory with {len(components['Models'])} model files")
310
+
311
+ # Calculate confidence
312
+ has_models = len(components["Models"]) > 0
313
+ has_views = len(components["Views"]) > 0
314
+ has_viewmodels = len(components["ViewModels"]) >= 2
315
+
316
+ if has_viewmodels and (has_models or has_views):
317
+ confidence = 0.7 if (has_models and has_views and has_viewmodels) else 0.6
318
+
319
+ framework = None
320
+ for fw in ["ASP.NET", "Angular", "Vue.js"]:
321
+ if fw in frameworks:
322
+ confidence = min(0.95, confidence + 0.1)
323
+ framework = fw
324
+ evidence.append(f"{fw} framework detected (supports MVVM)")
325
+ break
326
+
327
+ patterns.append(
328
+ ArchitecturalPattern(
329
+ pattern_name="MVVM (Model-View-ViewModel)",
330
+ confidence=confidence,
331
+ evidence=evidence,
332
+ components=dict(components),
333
+ framework=framework,
334
+ description="ViewModels provide data-binding between Views and Models",
335
+ )
336
+ )
337
+
338
+ return patterns
339
+
340
+ def _detect_repository(
341
+ self, dirs: dict[str, int], files: list[dict]
342
+ ) -> list[ArchitecturalPattern]:
343
+ """Detect Repository pattern"""
344
+ patterns = []
345
+
346
+ # Look for repositories directory or classes ending with Repository
347
+ has_repo_dir = any(d in dirs for d in self.REPO_DIRS)
348
+ repo_files = [
349
+ f
350
+ for f in files
351
+ if "repository" in str(f.get("file", "")).lower()
352
+ or any("repository" in c.get("name", "").lower() for c in f.get("classes", []))
353
+ ]
354
+
355
+ if not (has_repo_dir or len(repo_files) >= 2):
356
+ return patterns
357
+
358
+ evidence = []
359
+ components = defaultdict(list)
360
+
361
+ for file in repo_files:
362
+ components["Repositories"].append(file.get("file", ""))
363
+
364
+ if len(components["Repositories"]) >= 2:
365
+ evidence.append(
366
+ f"Repository pattern: {len(components['Repositories'])} repository classes"
367
+ )
368
+ evidence.append("Repositories abstract data access logic")
369
+
370
+ patterns.append(
371
+ ArchitecturalPattern(
372
+ pattern_name="Repository Pattern",
373
+ confidence=0.75,
374
+ evidence=evidence,
375
+ components=dict(components),
376
+ description="Encapsulates data access logic in repository classes",
377
+ )
378
+ )
379
+
380
+ return patterns
381
+
382
+ def _detect_service_layer(
383
+ self, dirs: dict[str, int], files: list[dict]
384
+ ) -> list[ArchitecturalPattern]:
385
+ """Detect Service Layer pattern"""
386
+ patterns = []
387
+
388
+ has_service_dir = any(d in dirs for d in self.SERVICE_DIRS)
389
+ service_files = [
390
+ f
391
+ for f in files
392
+ if "service" in str(f.get("file", "")).lower()
393
+ or any("service" in c.get("name", "").lower() for c in f.get("classes", []))
394
+ ]
395
+
396
+ if not (has_service_dir or len(service_files) >= 3):
397
+ return patterns
398
+
399
+ evidence = []
400
+ components = defaultdict(list)
401
+
402
+ for file in service_files:
403
+ components["Services"].append(file.get("file", ""))
404
+
405
+ if len(components["Services"]) >= 3:
406
+ evidence.append(f"Service layer: {len(components['Services'])} service classes")
407
+ evidence.append("Services encapsulate business logic")
408
+
409
+ patterns.append(
410
+ ArchitecturalPattern(
411
+ pattern_name="Service Layer Pattern",
412
+ confidence=0.75,
413
+ evidence=evidence,
414
+ components=dict(components),
415
+ description="Encapsulates business logic in service classes",
416
+ )
417
+ )
418
+
419
+ return patterns
420
+
421
+ def _detect_layered_architecture(
422
+ self, dirs: dict[str, int], _files: list[dict]
423
+ ) -> list[ArchitecturalPattern]:
424
+ """Detect Layered Architecture (3-tier, N-tier)"""
425
+ patterns = []
426
+
427
+ layered_matches = sum(1 for d in self.LAYERED_DIRS if d in dirs)
428
+
429
+ if layered_matches < 2:
430
+ return patterns
431
+
432
+ evidence = []
433
+ _components = defaultdict(list)
434
+ layers_found = []
435
+
436
+ if "presentation" in dirs or "ui" in dirs:
437
+ layers_found.append("Presentation Layer")
438
+ evidence.append("Presentation/UI layer detected")
439
+
440
+ if "business" in dirs or "bll" in dirs:
441
+ layers_found.append("Business Logic Layer")
442
+ evidence.append("Business logic layer detected")
443
+
444
+ if "data" in dirs or "dal" in dirs:
445
+ layers_found.append("Data Access Layer")
446
+ evidence.append("Data access layer detected")
447
+
448
+ if len(layers_found) >= 2:
449
+ confidence = 0.65 + (len(layers_found) * 0.1)
450
+
451
+ patterns.append(
452
+ ArchitecturalPattern(
453
+ pattern_name=f"Layered Architecture ({len(layers_found)}-tier)",
454
+ confidence=min(confidence, 0.9),
455
+ evidence=evidence,
456
+ components={"Layers": layers_found},
457
+ description=f"Separates concerns into {len(layers_found)} distinct layers",
458
+ )
459
+ )
460
+
461
+ return patterns
462
+
463
+ def _detect_clean_architecture(
464
+ self, dirs: dict[str, int], _files: list[dict]
465
+ ) -> list[ArchitecturalPattern]:
466
+ """Detect Clean Architecture"""
467
+ patterns = []
468
+
469
+ clean_matches = sum(1 for d in self.CLEAN_ARCH_DIRS if d in dirs)
470
+
471
+ if clean_matches < 3:
472
+ return patterns
473
+
474
+ evidence = []
475
+ components = defaultdict(list)
476
+
477
+ if "domain" in dirs:
478
+ evidence.append("Domain layer (core business logic)")
479
+ components["Domain"].append("domain/")
480
+
481
+ if "application" in dirs:
482
+ evidence.append("Application layer (use cases)")
483
+ components["Application"].append("application/")
484
+
485
+ if "infrastructure" in dirs:
486
+ evidence.append("Infrastructure layer (external dependencies)")
487
+ components["Infrastructure"].append("infrastructure/")
488
+
489
+ if "presentation" in dirs:
490
+ evidence.append("Presentation layer (UI/API)")
491
+ components["Presentation"].append("presentation/")
492
+
493
+ if len(components) >= 3:
494
+ patterns.append(
495
+ ArchitecturalPattern(
496
+ pattern_name="Clean Architecture",
497
+ confidence=0.85,
498
+ evidence=evidence,
499
+ components=dict(components),
500
+ description="Dependency inversion with domain at center, infrastructure at edges",
501
+ )
502
+ )
503
+
504
+ return patterns
505
+
506
+ def _enhance_with_ai(self, report: ArchitecturalReport) -> dict:
507
+ """Enhance architectural analysis with AI insights"""
508
+ if not self.ai_enhancer:
509
+ return {}
510
+
511
+ # Prepare summary for AI
512
+ summary = f"""Detected {len(report.patterns)} architectural patterns:
513
+ {chr(10).join(f"- {p.pattern_name} (confidence: {p.confidence:.2f})" for p in report.patterns)}
514
+
515
+ Frameworks: {", ".join(report.frameworks_detected) if report.frameworks_detected else "None"}
516
+ Total files: {report.total_files_analyzed}
517
+
518
+ Provide brief architectural insights and recommendations."""
519
+
520
+ try:
521
+ response = self.ai_enhancer._call_claude(summary, max_tokens=500)
522
+ return {"insights": response} if response else {}
523
+ except Exception as e:
524
+ logger.warning(f"⚠️ AI enhancement failed: {e}")
525
+ return {}