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,470 +0,0 @@
1
- """Linter Output Parsers
2
-
3
- Parses output from various linters into standardized format.
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 enum import Enum
13
- from typing import Any
14
-
15
-
16
- class Severity(Enum):
17
- """Issue severity levels"""
18
-
19
- ERROR = "error"
20
- WARNING = "warning"
21
- INFO = "info"
22
- STYLE = "style"
23
-
24
-
25
- @dataclass
26
- class LintIssue:
27
- """Standardized lint issue across all linters.
28
-
29
- This is the universal format - all parser output converts to this.
30
- """
31
-
32
- file_path: str
33
- line: int
34
- column: int
35
- rule: str
36
- message: str
37
- severity: Severity
38
- linter: str
39
- has_autofix: bool = False
40
- fix_suggestion: str | None = None
41
- context: dict[str, Any] | None = None
42
-
43
- def to_dict(self) -> dict[str, Any]:
44
- """Convert to dictionary"""
45
- return {
46
- "file_path": self.file_path,
47
- "line": self.line,
48
- "column": self.column,
49
- "rule": self.rule,
50
- "message": self.message,
51
- "severity": self.severity.value,
52
- "linter": self.linter,
53
- "has_autofix": self.has_autofix,
54
- "fix_suggestion": self.fix_suggestion,
55
- "context": self.context or {},
56
- }
57
-
58
-
59
- class BaseLinterParser:
60
- """Base class for all linter parsers"""
61
-
62
- def __init__(self, linter_name: str):
63
- self.linter_name = linter_name
64
-
65
- def parse(self, output: str, format: str = "auto") -> list[LintIssue]:
66
- """Parse linter output into standardized issues.
67
-
68
- Args:
69
- output: Raw linter output (text or JSON)
70
- format: "json", "text", or "auto" (detect)
71
-
72
- Returns:
73
- List of LintIssue objects
74
-
75
- """
76
- raise NotImplementedError(
77
- f"{self.__class__.__name__}.parse() must be implemented. "
78
- "Create a subclass of BaseLinterParser and implement the parse() method. "
79
- f"See ESLintParser, PylintParser, or MyPyParser for examples."
80
- )
81
-
82
- def parse_file(self, file_path: str, format: str = "auto") -> list[LintIssue]:
83
- """Parse linter output from file"""
84
- with open(file_path) as f:
85
- return self.parse(f.read(), format)
86
-
87
-
88
- class ESLintParser(BaseLinterParser):
89
- """Parse ESLint output.
90
-
91
- Supports both JSON and text formats.
92
- """
93
-
94
- def __init__(self):
95
- super().__init__("eslint")
96
-
97
- def parse(self, output: str, format: str = "auto") -> list[LintIssue]:
98
- """Parse ESLint output"""
99
- # Auto-detect format
100
- if format == "auto":
101
- format = "json" if output.strip().startswith("[") else "text"
102
-
103
- if format == "json":
104
- return self._parse_json(output)
105
- return self._parse_text(output)
106
-
107
- def _parse_json(self, output: str) -> list[LintIssue]:
108
- """Parse ESLint JSON format"""
109
- issues = []
110
-
111
- try:
112
- data = json.loads(output)
113
-
114
- for file_result in data:
115
- file_path = file_result.get("filePath", "")
116
-
117
- for message in file_result.get("messages", []):
118
- issues.append(
119
- LintIssue(
120
- file_path=file_path,
121
- line=message.get("line", 0),
122
- column=message.get("column", 0),
123
- rule=message.get("ruleId", "unknown"),
124
- message=message.get("message", ""),
125
- severity=self._map_severity(message.get("severity", 1)),
126
- linter=self.linter_name,
127
- has_autofix=message.get("fix") is not None,
128
- fix_suggestion=str(message.get("fix")) if message.get("fix") else None,
129
- context={
130
- "node_type": message.get("nodeType"),
131
- "end_line": message.get("endLine"),
132
- "end_column": message.get("endColumn"),
133
- },
134
- ),
135
- )
136
-
137
- except json.JSONDecodeError:
138
- # Return empty list if JSON invalid
139
- pass
140
-
141
- return issues
142
-
143
- def _parse_text(self, output: str) -> list[LintIssue]:
144
- """Parse ESLint text format"""
145
- issues = []
146
-
147
- # Pattern: /path/to/file.js
148
- # 1:5 error 'foo' is not defined no-undef
149
- pattern = r"^\s*(\d+):(\d+)\s+(error|warning)\s+(.+?)\s+([a-z-]+)$"
150
-
151
- current_file = None
152
-
153
- for line in output.split("\n"):
154
- # Check if this is a file path line
155
- if line and not line.startswith(" "):
156
- current_file = line.strip()
157
- continue
158
-
159
- # Try to match issue line
160
- match = re.match(pattern, line)
161
- if match and current_file:
162
- line_num, col_num, severity, message, rule = match.groups()
163
-
164
- issues.append(
165
- LintIssue(
166
- file_path=current_file,
167
- line=int(line_num),
168
- column=int(col_num),
169
- rule=rule,
170
- message=message,
171
- severity=Severity.ERROR if severity == "error" else Severity.WARNING,
172
- linter=self.linter_name,
173
- has_autofix=False, # Can't tell from text format
174
- ),
175
- )
176
-
177
- return issues
178
-
179
- def _map_severity(self, eslint_severity: int) -> Severity:
180
- """Map ESLint severity (1=warning, 2=error) to our enum"""
181
- return Severity.ERROR if eslint_severity == 2 else Severity.WARNING
182
-
183
-
184
- class PylintParser(BaseLinterParser):
185
- """Parse Pylint output.
186
-
187
- Supports JSON and text formats.
188
- """
189
-
190
- def __init__(self):
191
- super().__init__("pylint")
192
-
193
- def parse(self, output: str, format: str = "auto") -> list[LintIssue]:
194
- """Parse Pylint output"""
195
- # Auto-detect format
196
- if format == "auto":
197
- format = "json" if output.strip().startswith("[") else "text"
198
-
199
- if format == "json":
200
- return self._parse_json(output)
201
- return self._parse_text(output)
202
-
203
- def _parse_json(self, output: str) -> list[LintIssue]:
204
- """Parse Pylint JSON format"""
205
- issues = []
206
-
207
- try:
208
- data = json.loads(output)
209
-
210
- for item in data:
211
- issues.append(
212
- LintIssue(
213
- file_path=item.get("path", ""),
214
- line=item.get("line", 0),
215
- column=item.get("column", 0),
216
- rule=item.get("symbol", item.get("message-id", "unknown")),
217
- message=item.get("message", ""),
218
- severity=self._map_severity(item.get("type", "convention")),
219
- linter=self.linter_name,
220
- has_autofix=False, # Pylint doesn't provide autofixes
221
- context={
222
- "symbol": item.get("symbol"),
223
- "module": item.get("module"),
224
- "obj": item.get("obj"),
225
- },
226
- ),
227
- )
228
-
229
- except json.JSONDecodeError:
230
- pass
231
-
232
- return issues
233
-
234
- def _parse_text(self, output: str) -> list[LintIssue]:
235
- """Parse Pylint text format"""
236
- issues = []
237
-
238
- # Pattern: path/to/file.py:42:8: C0103: Variable name "X" doesn't conform (invalid-name)
239
- pattern = r"^(.+?):(\d+):(\d+):\s*([A-Z]\d+):\s*(.+?)\s*\(([a-z-]+)\)$"
240
-
241
- for line in output.split("\n"):
242
- match = re.match(pattern, line.strip())
243
- if match:
244
- file_path, line_num, col_num, code, message, symbol = match.groups()
245
-
246
- issues.append(
247
- LintIssue(
248
- file_path=file_path,
249
- line=int(line_num),
250
- column=int(col_num),
251
- rule=symbol,
252
- message=message,
253
- severity=self._map_severity(code[0]),
254
- linter=self.linter_name,
255
- has_autofix=False,
256
- context={"code": code, "symbol": symbol},
257
- ),
258
- )
259
-
260
- return issues
261
-
262
- def _map_severity(self, type_or_code: str) -> Severity:
263
- """Map Pylint type/code to severity"""
264
- if isinstance(type_or_code, str):
265
- first_char = type_or_code[0].upper() if type_or_code else "C"
266
-
267
- mapping = {
268
- "E": Severity.ERROR, # Error
269
- "F": Severity.ERROR, # Fatal
270
- "W": Severity.WARNING, # Warning
271
- "R": Severity.INFO, # Refactor
272
- "C": Severity.STYLE, # Convention
273
- "I": Severity.INFO, # Informational
274
- }
275
-
276
- return mapping.get(first_char, Severity.INFO)
277
-
278
- return Severity.INFO
279
-
280
-
281
- class MyPyParser(BaseLinterParser):
282
- """Parse mypy (Python type checker) output."""
283
-
284
- def __init__(self):
285
- super().__init__("mypy")
286
-
287
- def parse(self, output: str, format: str = "auto") -> list[LintIssue]:
288
- """Parse mypy output (text only)"""
289
- issues = []
290
-
291
- # Pattern: path/to/file.py:42: error: Incompatible types [assignment]
292
- pattern = r"^(.+?):(\d+):\s*(error|warning|note):\s*(.+?)(?:\s*\[([a-z-]+)\])?$"
293
-
294
- for line in output.split("\n"):
295
- match = re.match(pattern, line.strip())
296
- if match:
297
- file_path, line_num, severity, message, code = match.groups()
298
-
299
- issues.append(
300
- LintIssue(
301
- file_path=file_path,
302
- line=int(line_num),
303
- column=0, # mypy doesn't always provide column
304
- rule=code or "type-error",
305
- message=message,
306
- severity=Severity.ERROR if severity == "error" else Severity.WARNING,
307
- linter=self.linter_name,
308
- has_autofix=False,
309
- context={"severity_text": severity},
310
- ),
311
- )
312
-
313
- return issues
314
-
315
-
316
- class TypeScriptParser(BaseLinterParser):
317
- """Parse TypeScript compiler (tsc) output."""
318
-
319
- def __init__(self):
320
- super().__init__("typescript")
321
-
322
- def parse(self, output: str, format: str = "auto") -> list[LintIssue]:
323
- """Parse tsc output"""
324
- issues = []
325
-
326
- # Pattern: src/file.ts(42,8): error TS2322: Type 'string' is not assignable to type 'number'.
327
- pattern = r"^(.+?)\((\d+),(\d+)\):\s*(error|warning)\s*TS(\d+):\s*(.+)$"
328
-
329
- for line in output.split("\n"):
330
- match = re.match(pattern, line.strip())
331
- if match:
332
- file_path, line_num, col_num, severity, code, message = match.groups()
333
-
334
- issues.append(
335
- LintIssue(
336
- file_path=file_path,
337
- line=int(line_num),
338
- column=int(col_num),
339
- rule=f"TS{code}",
340
- message=message,
341
- severity=Severity.ERROR if severity == "error" else Severity.WARNING,
342
- linter=self.linter_name,
343
- has_autofix=False,
344
- context={"ts_code": code},
345
- ),
346
- )
347
-
348
- return issues
349
-
350
-
351
- class ClippyParser(BaseLinterParser):
352
- """Parse Rust Clippy output."""
353
-
354
- def __init__(self):
355
- super().__init__("clippy")
356
-
357
- def parse(self, output: str, format: str = "auto") -> list[LintIssue]:
358
- """Parse clippy output"""
359
- issues = []
360
-
361
- # Pattern: warning: unused variable: `x`
362
- # --> src/main.rs:5:9
363
- current_issue: dict[str, Any] = {}
364
-
365
- for line in output.split("\n"):
366
- # Check for severity line
367
- severity_match = re.match(r"^(error|warning):\s*(.+)$", line.strip())
368
- if severity_match:
369
- if current_issue:
370
- issues.append(self._create_issue(current_issue))
371
-
372
- current_issue = {
373
- "severity": severity_match.group(1),
374
- "message": severity_match.group(2),
375
- }
376
- continue
377
-
378
- # Check for location line
379
- loc_match = re.match(r"^\s*-->\s*(.+?):(\d+):(\d+)$", line.strip())
380
- if loc_match and current_issue:
381
- current_issue["file_path"] = loc_match.group(1)
382
- current_issue["line"] = int(loc_match.group(2))
383
- current_issue["column"] = int(loc_match.group(3))
384
-
385
- # Check for lint name
386
- lint_match = re.match(r"^\s*=\s*note:\s*#\[.*?\(([a-z_]+)\)\]", line.strip())
387
- if lint_match and current_issue:
388
- current_issue["rule"] = lint_match.group(1)
389
-
390
- # Add last issue
391
- if current_issue:
392
- issues.append(self._create_issue(current_issue))
393
-
394
- return issues
395
-
396
- def _create_issue(self, issue_dict: dict) -> LintIssue:
397
- """Create LintIssue from dict"""
398
- return LintIssue(
399
- file_path=issue_dict.get("file_path", ""),
400
- line=issue_dict.get("line", 0),
401
- column=issue_dict.get("column", 0),
402
- rule=issue_dict.get("rule", "clippy"),
403
- message=issue_dict.get("message", ""),
404
- severity=Severity.ERROR if issue_dict.get("severity") == "error" else Severity.WARNING,
405
- linter=self.linter_name,
406
- has_autofix=False,
407
- )
408
-
409
-
410
- class LinterParserFactory:
411
- """Factory for creating appropriate parser based on linter type."""
412
-
413
- _parsers = {
414
- "eslint": ESLintParser,
415
- "pylint": PylintParser,
416
- "mypy": MyPyParser,
417
- "typescript": TypeScriptParser,
418
- "tsc": TypeScriptParser,
419
- "clippy": ClippyParser,
420
- "rustc": ClippyParser,
421
- }
422
-
423
- @classmethod
424
- def create(cls, linter_name: str) -> BaseLinterParser:
425
- """Create parser for specified linter.
426
-
427
- Args:
428
- linter_name: Name of linter (eslint, pylint, mypy, etc.)
429
-
430
- Returns:
431
- Appropriate parser instance
432
-
433
- Raises:
434
- ValueError if linter not supported
435
-
436
- """
437
- parser_class = cls._parsers.get(linter_name.lower())
438
-
439
- if not parser_class:
440
- raise ValueError(
441
- f"Unsupported linter: {linter_name}. Supported: {', '.join(cls._parsers.keys())}",
442
- )
443
-
444
- return parser_class()
445
-
446
- @classmethod
447
- def get_supported_linters(cls) -> list[str]:
448
- """Get list of supported linters"""
449
- return list(cls._parsers.keys())
450
-
451
-
452
- def parse_linter_output(linter_name: str, output: str, format: str = "auto") -> list[LintIssue]:
453
- """Convenience function to parse linter output.
454
-
455
- Args:
456
- linter_name: Name of linter
457
- output: Raw linter output
458
- format: "json", "text", or "auto"
459
-
460
- Returns:
461
- List of standardized LintIssue objects
462
-
463
- Example:
464
- >>> issues = parse_linter_output("eslint", eslint_json_output)
465
- >>> for issue in issues:
466
- ... print(f"{issue.file_path}:{issue.line} - {issue.message}")
467
-
468
- """
469
- parser = LinterParserFactory.create(linter_name)
470
- return parser.parse(output, format)