empathy-framework 4.6.6__py3-none-any.whl → 4.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. empathy_framework-4.7.1.dist-info/METADATA +690 -0
  2. empathy_framework-4.7.1.dist-info/RECORD +379 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/top_level.txt +1 -2
  4. empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
  5. empathy_llm_toolkit/agent_factory/__init__.py +6 -6
  6. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
  7. empathy_llm_toolkit/agents_md/__init__.py +22 -0
  8. empathy_llm_toolkit/agents_md/loader.py +218 -0
  9. empathy_llm_toolkit/agents_md/parser.py +271 -0
  10. empathy_llm_toolkit/agents_md/registry.py +307 -0
  11. empathy_llm_toolkit/commands/__init__.py +51 -0
  12. empathy_llm_toolkit/commands/context.py +375 -0
  13. empathy_llm_toolkit/commands/loader.py +301 -0
  14. empathy_llm_toolkit/commands/models.py +231 -0
  15. empathy_llm_toolkit/commands/parser.py +371 -0
  16. empathy_llm_toolkit/commands/registry.py +429 -0
  17. empathy_llm_toolkit/config/__init__.py +8 -8
  18. empathy_llm_toolkit/config/unified.py +3 -7
  19. empathy_llm_toolkit/context/__init__.py +22 -0
  20. empathy_llm_toolkit/context/compaction.py +455 -0
  21. empathy_llm_toolkit/context/manager.py +434 -0
  22. empathy_llm_toolkit/hooks/__init__.py +24 -0
  23. empathy_llm_toolkit/hooks/config.py +306 -0
  24. empathy_llm_toolkit/hooks/executor.py +289 -0
  25. empathy_llm_toolkit/hooks/registry.py +302 -0
  26. empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
  27. empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
  28. empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
  29. empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
  30. empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
  31. empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
  32. empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
  33. empathy_llm_toolkit/learning/__init__.py +30 -0
  34. empathy_llm_toolkit/learning/evaluator.py +438 -0
  35. empathy_llm_toolkit/learning/extractor.py +514 -0
  36. empathy_llm_toolkit/learning/storage.py +560 -0
  37. empathy_llm_toolkit/providers.py +4 -11
  38. empathy_llm_toolkit/security/__init__.py +17 -17
  39. empathy_llm_toolkit/utils/tokens.py +2 -5
  40. empathy_os/__init__.py +202 -70
  41. empathy_os/cache_monitor.py +5 -3
  42. empathy_os/cli/__init__.py +11 -55
  43. empathy_os/cli/__main__.py +29 -15
  44. empathy_os/cli/commands/inspection.py +21 -12
  45. empathy_os/cli/commands/memory.py +4 -12
  46. empathy_os/cli/commands/profiling.py +198 -0
  47. empathy_os/cli/commands/utilities.py +27 -7
  48. empathy_os/cli.py +28 -57
  49. empathy_os/cli_unified.py +525 -1164
  50. empathy_os/cost_tracker.py +9 -3
  51. empathy_os/dashboard/server.py +200 -2
  52. empathy_os/hot_reload/__init__.py +7 -7
  53. empathy_os/hot_reload/config.py +6 -7
  54. empathy_os/hot_reload/integration.py +35 -35
  55. empathy_os/hot_reload/reloader.py +57 -57
  56. empathy_os/hot_reload/watcher.py +28 -28
  57. empathy_os/hot_reload/websocket.py +2 -2
  58. empathy_os/memory/__init__.py +11 -4
  59. empathy_os/memory/claude_memory.py +1 -1
  60. empathy_os/memory/cross_session.py +8 -12
  61. empathy_os/memory/edges.py +6 -6
  62. empathy_os/memory/file_session.py +770 -0
  63. empathy_os/memory/graph.py +30 -30
  64. empathy_os/memory/nodes.py +6 -6
  65. empathy_os/memory/short_term.py +15 -9
  66. empathy_os/memory/unified.py +606 -140
  67. empathy_os/meta_workflows/agent_creator.py +3 -9
  68. empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
  69. empathy_os/meta_workflows/form_engine.py +6 -18
  70. empathy_os/meta_workflows/intent_detector.py +64 -24
  71. empathy_os/meta_workflows/models.py +3 -1
  72. empathy_os/meta_workflows/pattern_learner.py +13 -31
  73. empathy_os/meta_workflows/plan_generator.py +55 -47
  74. empathy_os/meta_workflows/session_context.py +2 -3
  75. empathy_os/meta_workflows/workflow.py +20 -51
  76. empathy_os/models/cli.py +2 -2
  77. empathy_os/models/tasks.py +1 -2
  78. empathy_os/models/telemetry.py +4 -1
  79. empathy_os/models/token_estimator.py +3 -1
  80. empathy_os/monitoring/alerts.py +938 -9
  81. empathy_os/monitoring/alerts_cli.py +346 -183
  82. empathy_os/orchestration/execution_strategies.py +12 -29
  83. empathy_os/orchestration/pattern_learner.py +20 -26
  84. empathy_os/orchestration/real_tools.py +6 -15
  85. empathy_os/platform_utils.py +2 -1
  86. empathy_os/plugins/__init__.py +2 -2
  87. empathy_os/plugins/base.py +64 -64
  88. empathy_os/plugins/registry.py +32 -32
  89. empathy_os/project_index/index.py +49 -15
  90. empathy_os/project_index/models.py +1 -2
  91. empathy_os/project_index/reports.py +1 -1
  92. empathy_os/project_index/scanner.py +1 -0
  93. empathy_os/redis_memory.py +10 -7
  94. empathy_os/resilience/__init__.py +1 -1
  95. empathy_os/resilience/health.py +10 -10
  96. empathy_os/routing/__init__.py +7 -7
  97. empathy_os/routing/chain_executor.py +37 -37
  98. empathy_os/routing/classifier.py +36 -36
  99. empathy_os/routing/smart_router.py +40 -40
  100. empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
  101. empathy_os/scaffolding/__init__.py +8 -8
  102. empathy_os/scaffolding/__main__.py +1 -1
  103. empathy_os/scaffolding/cli.py +28 -28
  104. empathy_os/socratic/__init__.py +3 -19
  105. empathy_os/socratic/ab_testing.py +25 -36
  106. empathy_os/socratic/blueprint.py +38 -38
  107. empathy_os/socratic/cli.py +34 -20
  108. empathy_os/socratic/collaboration.py +30 -28
  109. empathy_os/socratic/domain_templates.py +9 -1
  110. empathy_os/socratic/embeddings.py +17 -13
  111. empathy_os/socratic/engine.py +135 -70
  112. empathy_os/socratic/explainer.py +70 -60
  113. empathy_os/socratic/feedback.py +24 -19
  114. empathy_os/socratic/forms.py +15 -10
  115. empathy_os/socratic/generator.py +51 -35
  116. empathy_os/socratic/llm_analyzer.py +25 -23
  117. empathy_os/socratic/mcp_server.py +99 -159
  118. empathy_os/socratic/session.py +19 -13
  119. empathy_os/socratic/storage.py +98 -67
  120. empathy_os/socratic/success.py +38 -27
  121. empathy_os/socratic/visual_editor.py +51 -39
  122. empathy_os/socratic/web_ui.py +99 -66
  123. empathy_os/telemetry/cli.py +3 -1
  124. empathy_os/telemetry/usage_tracker.py +1 -3
  125. empathy_os/test_generator/__init__.py +3 -3
  126. empathy_os/test_generator/cli.py +28 -28
  127. empathy_os/test_generator/generator.py +64 -66
  128. empathy_os/test_generator/risk_analyzer.py +11 -11
  129. empathy_os/vscode_bridge 2.py +173 -0
  130. empathy_os/vscode_bridge.py +173 -0
  131. empathy_os/workflows/__init__.py +212 -120
  132. empathy_os/workflows/batch_processing.py +8 -24
  133. empathy_os/workflows/bug_predict.py +1 -1
  134. empathy_os/workflows/code_review.py +20 -5
  135. empathy_os/workflows/code_review_pipeline.py +13 -8
  136. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  137. empathy_os/workflows/manage_documentation.py +1 -0
  138. empathy_os/workflows/orchestrated_health_check.py +6 -11
  139. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  140. empathy_os/workflows/pr_review.py +18 -10
  141. empathy_os/workflows/progressive/README 2.md +454 -0
  142. empathy_os/workflows/progressive/__init__ 2.py +92 -0
  143. empathy_os/workflows/progressive/__init__.py +2 -12
  144. empathy_os/workflows/progressive/cli 2.py +242 -0
  145. empathy_os/workflows/progressive/cli.py +14 -37
  146. empathy_os/workflows/progressive/core 2.py +488 -0
  147. empathy_os/workflows/progressive/core.py +12 -12
  148. empathy_os/workflows/progressive/orchestrator 2.py +701 -0
  149. empathy_os/workflows/progressive/orchestrator.py +166 -144
  150. empathy_os/workflows/progressive/reports 2.py +528 -0
  151. empathy_os/workflows/progressive/reports.py +22 -31
  152. empathy_os/workflows/progressive/telemetry 2.py +280 -0
  153. empathy_os/workflows/progressive/telemetry.py +8 -14
  154. empathy_os/workflows/progressive/test_gen 2.py +514 -0
  155. empathy_os/workflows/progressive/test_gen.py +29 -48
  156. empathy_os/workflows/progressive/workflow 2.py +628 -0
  157. empathy_os/workflows/progressive/workflow.py +31 -70
  158. empathy_os/workflows/release_prep.py +21 -6
  159. empathy_os/workflows/release_prep_crew.py +1 -0
  160. empathy_os/workflows/secure_release.py +13 -6
  161. empathy_os/workflows/security_audit.py +8 -3
  162. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  163. empathy_os/workflows/test_maintenance_crew.py +1 -0
  164. empathy_os/workflows/test_runner.py +16 -12
  165. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  166. empathy_software_plugin/cli.py +0 -122
  167. patterns/README.md +119 -0
  168. patterns/__init__.py +95 -0
  169. patterns/behavior.py +298 -0
  170. patterns/code_review_memory.json +441 -0
  171. patterns/core.py +97 -0
  172. patterns/debugging.json +3763 -0
  173. patterns/empathy.py +268 -0
  174. patterns/health_check_memory.json +505 -0
  175. patterns/input.py +161 -0
  176. patterns/memory_graph.json +8 -0
  177. patterns/refactoring_memory.json +1113 -0
  178. patterns/registry.py +663 -0
  179. patterns/security_memory.json +8 -0
  180. patterns/structural.py +415 -0
  181. patterns/validation.py +194 -0
  182. coach_wizards/__init__.py +0 -45
  183. coach_wizards/accessibility_wizard.py +0 -91
  184. coach_wizards/api_wizard.py +0 -91
  185. coach_wizards/base_wizard.py +0 -209
  186. coach_wizards/cicd_wizard.py +0 -91
  187. coach_wizards/code_reviewer_README.md +0 -60
  188. coach_wizards/code_reviewer_wizard.py +0 -180
  189. coach_wizards/compliance_wizard.py +0 -91
  190. coach_wizards/database_wizard.py +0 -91
  191. coach_wizards/debugging_wizard.py +0 -91
  192. coach_wizards/documentation_wizard.py +0 -91
  193. coach_wizards/generate_wizards.py +0 -347
  194. coach_wizards/localization_wizard.py +0 -173
  195. coach_wizards/migration_wizard.py +0 -91
  196. coach_wizards/monitoring_wizard.py +0 -91
  197. coach_wizards/observability_wizard.py +0 -91
  198. coach_wizards/performance_wizard.py +0 -91
  199. coach_wizards/prompt_engineering_wizard.py +0 -661
  200. coach_wizards/refactoring_wizard.py +0 -91
  201. coach_wizards/scaling_wizard.py +0 -90
  202. coach_wizards/security_wizard.py +0 -92
  203. coach_wizards/testing_wizard.py +0 -91
  204. empathy_framework-4.6.6.dist-info/METADATA +0 -1597
  205. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  206. empathy_llm_toolkit/wizards/__init__.py +0 -43
  207. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  208. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  209. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  210. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  211. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  212. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  213. empathy_os/wizard_factory_cli.py +0 -170
  214. empathy_software_plugin/wizards/__init__.py +0 -42
  215. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  216. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  217. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  218. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  219. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  220. empathy_software_plugin/wizards/base_wizard.py +0 -288
  221. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  222. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  223. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  224. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  225. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  226. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  227. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  228. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  229. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  230. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  231. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  232. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  233. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  234. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  235. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  236. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  237. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  238. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  239. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  240. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  241. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  242. empathy_software_plugin/wizards/security/__init__.py +0 -32
  243. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  244. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  245. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  246. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  247. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  248. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  249. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  250. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  251. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  252. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  253. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  254. wizards/__init__.py +0 -82
  255. wizards/admission_assessment_wizard.py +0 -644
  256. wizards/care_plan.py +0 -321
  257. wizards/clinical_assessment.py +0 -769
  258. wizards/discharge_planning.py +0 -77
  259. wizards/discharge_summary_wizard.py +0 -468
  260. wizards/dosage_calculation.py +0 -497
  261. wizards/incident_report_wizard.py +0 -454
  262. wizards/medication_reconciliation.py +0 -85
  263. wizards/nursing_assessment.py +0 -171
  264. wizards/patient_education.py +0 -654
  265. wizards/quality_improvement.py +0 -705
  266. wizards/sbar_report.py +0 -324
  267. wizards/sbar_wizard.py +0 -608
  268. wizards/shift_handoff_wizard.py +0 -535
  269. wizards/soap_note_wizard.py +0 -679
  270. wizards/treatment_plan.py +0 -15
  271. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/WHEEL +0 -0
  272. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/entry_points.txt +0 -0
  273. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,446 +0,0 @@
1
- """Linting Configuration Loaders
2
-
3
- Reads linting configuration files to understand project standards.
4
-
5
- Copyright 2025 Smart AI Memory, LLC
6
- Licensed under Fair Source 0.9
7
- """
8
-
9
- import json
10
- import re
11
- from dataclasses import dataclass
12
- from pathlib import Path
13
- from typing import Any
14
-
15
-
16
- @dataclass
17
- class LintConfig:
18
- """Standardized linting configuration.
19
-
20
- Unified format across all linters.
21
- """
22
-
23
- linter: str
24
- config_file: str
25
- rules: dict[str, Any]
26
- extends: list[str]
27
- plugins: list[str]
28
- severity_overrides: dict[str, str]
29
- raw_config: dict[str, Any]
30
-
31
- def get_rule_severity(self, rule_name: str) -> str | None:
32
- """Get configured severity for rule"""
33
- rule_config = self.rules.get(rule_name)
34
-
35
- if rule_config is None:
36
- return None
37
-
38
- # Handle different config formats
39
- if isinstance(rule_config, str):
40
- return rule_config
41
- if isinstance(rule_config, list) and len(rule_config) > 0:
42
- return str(rule_config[0])
43
- if isinstance(rule_config, int):
44
- # ESLint: 0=off, 1=warn, 2=error
45
- return ["off", "warn", "error"][rule_config] if 0 <= rule_config <= 2 else None
46
-
47
- return None
48
-
49
- def is_rule_enabled(self, rule_name: str) -> bool:
50
- """Check if rule is enabled"""
51
- severity = self.get_rule_severity(rule_name)
52
- return severity not in [None, "off", "0", 0]
53
-
54
-
55
- class BaseConfigLoader:
56
- """Base class for configuration loaders"""
57
-
58
- def __init__(self, linter_name: str):
59
- self.linter_name = linter_name
60
-
61
- def load(self, config_path: str) -> LintConfig:
62
- """Load configuration from file"""
63
- raise NotImplementedError(
64
- f"{self.__class__.__name__}.load() must be implemented. "
65
- "Create a subclass of BaseConfigLoader and implement the load() method. "
66
- f"See ESLintConfigLoader, PylintConfigLoader, or TypeScriptConfigLoader for examples."
67
- )
68
-
69
- def find_config(self, start_dir: str) -> str | None:
70
- """Find config file starting from directory"""
71
- raise NotImplementedError(
72
- f"{self.__class__.__name__}.find_config() must be implemented. "
73
- "Create a subclass of BaseConfigLoader and implement the find_config() method. "
74
- f"See ESLintConfigLoader, PylintConfigLoader, or TypeScriptConfigLoader for examples."
75
- )
76
-
77
-
78
- class ESLintConfigLoader(BaseConfigLoader):
79
- """Load ESLint configuration.
80
-
81
- Supports:
82
- - .eslintrc.json
83
- - .eslintrc.js (limited - JSON portion only)
84
- - .eslintrc.yml (via JSON-compatible subset)
85
- - package.json (eslintConfig section)
86
- """
87
-
88
- CONFIG_FILES = [
89
- ".eslintrc.json",
90
- ".eslintrc",
91
- ".eslintrc.js",
92
- ".eslintrc.yml",
93
- ".eslintrc.yaml",
94
- "package.json",
95
- ]
96
-
97
- def __init__(self):
98
- super().__init__("eslint")
99
-
100
- def find_config(self, start_dir: str) -> str | None:
101
- """Find ESLint config file"""
102
- current = Path(start_dir).resolve()
103
-
104
- while True:
105
- for config_file in self.CONFIG_FILES:
106
- config_path = current / config_file
107
- if config_path.exists():
108
- return str(config_path)
109
-
110
- # Stop at root
111
- if current.parent == current:
112
- break
113
-
114
- current = current.parent
115
-
116
- return None
117
-
118
- def load(self, config_path: str) -> LintConfig:
119
- """Load ESLint configuration"""
120
- path = Path(config_path)
121
-
122
- if not path.exists():
123
- raise FileNotFoundError(f"Config file not found: {config_path}")
124
-
125
- # Handle package.json
126
- if path.name == "package.json":
127
- return self._load_package_json(path)
128
-
129
- # Handle .js files (try to extract JSON)
130
- if path.suffix == ".js":
131
- return self._load_js_config(path)
132
-
133
- # Handle JSON files
134
- with open(path) as f:
135
- config_data = json.load(f)
136
-
137
- return self._parse_config(config_data, str(path))
138
-
139
- def _load_package_json(self, path: Path) -> LintConfig:
140
- """Load ESLint config from package.json"""
141
- with open(path) as f:
142
- package_data = json.load(f)
143
-
144
- eslint_config = package_data.get("eslintConfig", {})
145
- return self._parse_config(eslint_config, str(path))
146
-
147
- def _load_js_config(self, path: Path) -> LintConfig:
148
- """Load ESLint config from .js file.
149
-
150
- Limited support - extracts JSON-like objects.
151
- """
152
- with open(path) as f:
153
- content = f.read()
154
-
155
- # Try to find module.exports = { ... }
156
- match = re.search(r"module\.exports\s*=\s*(\{[\s\S]*?\})\s*;?", content)
157
-
158
- if match:
159
- json_str = match.group(1)
160
- # Try to convert JS object to JSON
161
- # This is a simplified approach - may not work for complex configs
162
- try:
163
- config_data = json.loads(json_str)
164
- return self._parse_config(config_data, str(path))
165
- except json.JSONDecodeError:
166
- # Return minimal config
167
- return LintConfig(
168
- linter=self.linter_name,
169
- config_file=str(path),
170
- rules={},
171
- extends=[],
172
- plugins=[],
173
- severity_overrides={},
174
- raw_config={"note": "JS config - limited parsing"},
175
- )
176
-
177
- raise ValueError(f"Could not parse JS config: {path}") from None
178
-
179
- def _parse_config(self, config_data: dict, config_file: str) -> LintConfig:
180
- """Parse ESLint config data"""
181
- return LintConfig(
182
- linter=self.linter_name,
183
- config_file=config_file,
184
- rules=config_data.get("rules", {}),
185
- extends=self._normalize_extends(config_data.get("extends")),
186
- plugins=config_data.get("plugins", []),
187
- severity_overrides={},
188
- raw_config=config_data,
189
- )
190
-
191
- def _normalize_extends(self, extends_value: Any) -> list[str]:
192
- """Normalize extends to list"""
193
- if extends_value is None:
194
- return []
195
- if isinstance(extends_value, str):
196
- return [extends_value]
197
- if isinstance(extends_value, list):
198
- return extends_value
199
- return []
200
-
201
-
202
- class PylintConfigLoader(BaseConfigLoader):
203
- """Load Pylint configuration.
204
-
205
- Supports:
206
- - pyproject.toml (tool.pylint section)
207
- - .pylintrc
208
- - pylintrc
209
- - setup.cfg
210
- """
211
-
212
- def __init__(self):
213
- super().__init__("pylint")
214
-
215
- def find_config(self, start_dir: str) -> str | None:
216
- """Find Pylint config file"""
217
- current = Path(start_dir).resolve()
218
-
219
- config_files = ["pyproject.toml", ".pylintrc", "pylintrc", "setup.cfg"]
220
-
221
- while True:
222
- for config_file in config_files:
223
- config_path = current / config_file
224
- if config_path.exists():
225
- return str(config_path)
226
-
227
- if current.parent == current:
228
- break
229
-
230
- current = current.parent
231
-
232
- return None
233
-
234
- def load(self, config_path: str) -> LintConfig:
235
- """Load Pylint configuration"""
236
- path = Path(config_path)
237
-
238
- if not path.exists():
239
- raise FileNotFoundError(f"Config file not found: {config_path}")
240
-
241
- if path.name == "pyproject.toml":
242
- return self._load_pyproject(path)
243
- return self._load_ini_style(path)
244
-
245
- def _load_pyproject(self, path: Path) -> LintConfig:
246
- """Load from pyproject.toml"""
247
- try:
248
- import tomli
249
-
250
- toml_loader = tomli
251
- except ImportError:
252
- # Fallback for Python 3.11+
253
- try:
254
- import tomllib
255
-
256
- toml_loader = tomllib
257
- except ImportError as e:
258
- raise ImportError("tomli or tomllib required for pyproject.toml") from e
259
-
260
- with open(path, "rb") as f:
261
- data = toml_loader.load(f)
262
-
263
- pylint_config = data.get("tool", {}).get("pylint", {})
264
-
265
- # Extract rules
266
- rules = {}
267
- if "enable" in pylint_config:
268
- for rule in pylint_config["enable"]:
269
- rules[rule] = "enabled"
270
- if "disable" in pylint_config:
271
- for rule in pylint_config["disable"]:
272
- rules[rule] = "disabled"
273
-
274
- # Get messages control
275
- messages_control = pylint_config.get("MESSAGES CONTROL", {})
276
- if "disable" in messages_control:
277
- for rule in messages_control["disable"].split(","):
278
- rules[rule.strip()] = "disabled"
279
-
280
- return LintConfig(
281
- linter=self.linter_name,
282
- config_file=str(path),
283
- rules=rules,
284
- extends=[],
285
- plugins=pylint_config.get("load-plugins", []),
286
- severity_overrides={},
287
- raw_config=pylint_config,
288
- )
289
-
290
- def _load_ini_style(self, path: Path) -> LintConfig:
291
- """Load from .pylintrc or setup.cfg"""
292
- import configparser
293
-
294
- config = configparser.ConfigParser()
295
- config.read(path)
296
-
297
- rules = {}
298
-
299
- # Check for disabled rules
300
- if config.has_option("MESSAGES CONTROL", "disable"):
301
- disabled = config.get("MESSAGES CONTROL", "disable")
302
- for rule in disabled.split(","):
303
- rules[rule.strip()] = "disabled"
304
-
305
- if config.has_option("MESSAGES CONTROL", "enable"):
306
- enabled = config.get("MESSAGES CONTROL", "enable")
307
- for rule in enabled.split(","):
308
- rules[rule.strip()] = "enabled"
309
-
310
- plugins = []
311
- if config.has_option("MASTER", "load-plugins"):
312
- plugins = config.get("MASTER", "load-plugins").split(",")
313
-
314
- return LintConfig(
315
- linter=self.linter_name,
316
- config_file=str(path),
317
- rules=rules,
318
- extends=[],
319
- plugins=[p.strip() for p in plugins],
320
- severity_overrides={},
321
- raw_config=dict(config.items()) if hasattr(config, "items") else {},
322
- )
323
-
324
-
325
- class TypeScriptConfigLoader(BaseConfigLoader):
326
- """Load TypeScript configuration.
327
-
328
- Supports tsconfig.json
329
- """
330
-
331
- def __init__(self):
332
- super().__init__("typescript")
333
-
334
- def find_config(self, start_dir: str) -> str | None:
335
- """Find tsconfig.json"""
336
- current = Path(start_dir).resolve()
337
-
338
- while True:
339
- tsconfig = current / "tsconfig.json"
340
- if tsconfig.exists():
341
- return str(tsconfig)
342
-
343
- if current.parent == current:
344
- break
345
-
346
- current = current.parent
347
-
348
- return None
349
-
350
- def load(self, config_path: str) -> LintConfig:
351
- """Load TypeScript configuration"""
352
- path = Path(config_path)
353
-
354
- if not path.exists():
355
- raise FileNotFoundError(f"Config file not found: {config_path}")
356
-
357
- with open(path) as f:
358
- # TypeScript allows comments in JSON
359
- content = f.read()
360
- # Remove comments (simplified)
361
- content = re.sub(r"//.*?\n", "\n", content)
362
- content = re.sub(r"/\*.*?\*/", "", content, flags=re.DOTALL)
363
-
364
- config_data = json.loads(content)
365
-
366
- compiler_options = config_data.get("compilerOptions", {})
367
-
368
- # Convert compiler options to "rules"
369
- rules = {}
370
- for option, value in compiler_options.items():
371
- rules[option] = value
372
-
373
- return LintConfig(
374
- linter=self.linter_name,
375
- config_file=str(path),
376
- rules=rules,
377
- extends=[config_data.get("extends", "")],
378
- plugins=[],
379
- severity_overrides={},
380
- raw_config=config_data,
381
- )
382
-
383
-
384
- class ConfigLoaderFactory:
385
- """Factory for creating config loaders"""
386
-
387
- _loaders = {
388
- "eslint": ESLintConfigLoader,
389
- "pylint": PylintConfigLoader,
390
- "typescript": TypeScriptConfigLoader,
391
- "tsc": TypeScriptConfigLoader,
392
- }
393
-
394
- @classmethod
395
- def create(cls, linter_name: str) -> BaseConfigLoader:
396
- """Create config loader for linter"""
397
- loader_class = cls._loaders.get(linter_name.lower())
398
-
399
- if not loader_class:
400
- raise ValueError(
401
- f"Unsupported linter config: {linter_name}. "
402
- f"Supported: {', '.join(cls._loaders.keys())}",
403
- )
404
-
405
- return loader_class()
406
-
407
- @classmethod
408
- def get_supported_linters(cls) -> list[str]:
409
- """Get supported linters"""
410
- return list(cls._loaders.keys())
411
-
412
-
413
- def load_config(
414
- linter_name: str,
415
- config_path: str | None = None,
416
- start_dir: str | None = None,
417
- ) -> LintConfig | None:
418
- """Load linting configuration.
419
-
420
- Args:
421
- linter_name: Name of linter
422
- config_path: Explicit config path (optional)
423
- start_dir: Directory to start search from (optional)
424
-
425
- Returns:
426
- LintConfig or None if not found
427
-
428
- Example:
429
- >>> config = load_config("eslint", start_dir="/path/to/project")
430
- >>> if config:
431
- ... print(f"Rules: {len(config.rules)}")
432
-
433
- """
434
- loader = ConfigLoaderFactory.create(linter_name)
435
-
436
- # If explicit path provided
437
- if config_path:
438
- return loader.load(config_path)
439
-
440
- # Otherwise, search for config
441
- if start_dir:
442
- found_config = loader.find_config(start_dir)
443
- if found_config:
444
- return loader.load(found_config)
445
-
446
- return None