empathy-framework 4.6.6__py3-none-any.whl → 4.7.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.
Files changed (247) hide show
  1. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/METADATA +7 -6
  2. empathy_framework-4.7.0.dist-info/RECORD +354 -0
  3. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/top_level.txt +0 -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.py +173 -0
  130. empathy_os/workflows/__init__.py +212 -120
  131. empathy_os/workflows/batch_processing.py +8 -24
  132. empathy_os/workflows/bug_predict.py +1 -1
  133. empathy_os/workflows/code_review.py +20 -5
  134. empathy_os/workflows/code_review_pipeline.py +13 -8
  135. empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
  136. empathy_os/workflows/manage_documentation.py +1 -0
  137. empathy_os/workflows/orchestrated_health_check.py +6 -11
  138. empathy_os/workflows/orchestrated_release_prep.py +3 -3
  139. empathy_os/workflows/pr_review.py +18 -10
  140. empathy_os/workflows/progressive/__init__.py +2 -12
  141. empathy_os/workflows/progressive/cli.py +14 -37
  142. empathy_os/workflows/progressive/core.py +12 -12
  143. empathy_os/workflows/progressive/orchestrator.py +166 -144
  144. empathy_os/workflows/progressive/reports.py +22 -31
  145. empathy_os/workflows/progressive/telemetry.py +8 -14
  146. empathy_os/workflows/progressive/test_gen.py +29 -48
  147. empathy_os/workflows/progressive/workflow.py +31 -70
  148. empathy_os/workflows/release_prep.py +21 -6
  149. empathy_os/workflows/release_prep_crew.py +1 -0
  150. empathy_os/workflows/secure_release.py +13 -6
  151. empathy_os/workflows/security_audit.py +8 -3
  152. empathy_os/workflows/test_coverage_boost_crew.py +3 -2
  153. empathy_os/workflows/test_maintenance_crew.py +1 -0
  154. empathy_os/workflows/test_runner.py +16 -12
  155. empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
  156. empathy_software_plugin/cli.py +0 -122
  157. coach_wizards/__init__.py +0 -45
  158. coach_wizards/accessibility_wizard.py +0 -91
  159. coach_wizards/api_wizard.py +0 -91
  160. coach_wizards/base_wizard.py +0 -209
  161. coach_wizards/cicd_wizard.py +0 -91
  162. coach_wizards/code_reviewer_README.md +0 -60
  163. coach_wizards/code_reviewer_wizard.py +0 -180
  164. coach_wizards/compliance_wizard.py +0 -91
  165. coach_wizards/database_wizard.py +0 -91
  166. coach_wizards/debugging_wizard.py +0 -91
  167. coach_wizards/documentation_wizard.py +0 -91
  168. coach_wizards/generate_wizards.py +0 -347
  169. coach_wizards/localization_wizard.py +0 -173
  170. coach_wizards/migration_wizard.py +0 -91
  171. coach_wizards/monitoring_wizard.py +0 -91
  172. coach_wizards/observability_wizard.py +0 -91
  173. coach_wizards/performance_wizard.py +0 -91
  174. coach_wizards/prompt_engineering_wizard.py +0 -661
  175. coach_wizards/refactoring_wizard.py +0 -91
  176. coach_wizards/scaling_wizard.py +0 -90
  177. coach_wizards/security_wizard.py +0 -92
  178. coach_wizards/testing_wizard.py +0 -91
  179. empathy_framework-4.6.6.dist-info/RECORD +0 -410
  180. empathy_llm_toolkit/wizards/__init__.py +0 -43
  181. empathy_llm_toolkit/wizards/base_wizard.py +0 -364
  182. empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
  183. empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
  184. empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
  185. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
  186. empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
  187. empathy_os/wizard_factory_cli.py +0 -170
  188. empathy_software_plugin/wizards/__init__.py +0 -42
  189. empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
  190. empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
  191. empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
  192. empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
  193. empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
  194. empathy_software_plugin/wizards/base_wizard.py +0 -288
  195. empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
  196. empathy_software_plugin/wizards/code_review_wizard.py +0 -604
  197. empathy_software_plugin/wizards/debugging/__init__.py +0 -50
  198. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
  199. empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
  200. empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
  201. empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
  202. empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
  203. empathy_software_plugin/wizards/debugging/verification.py +0 -369
  204. empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
  205. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
  206. empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
  207. empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
  208. empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
  209. empathy_software_plugin/wizards/performance/__init__.py +0 -9
  210. empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
  211. empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
  212. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
  213. empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
  214. empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
  215. empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
  216. empathy_software_plugin/wizards/security/__init__.py +0 -32
  217. empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
  218. empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
  219. empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
  220. empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
  221. empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
  222. empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
  223. empathy_software_plugin/wizards/testing/__init__.py +0 -27
  224. empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
  225. empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
  226. empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
  227. empathy_software_plugin/wizards/testing_wizard.py +0 -274
  228. wizards/__init__.py +0 -82
  229. wizards/admission_assessment_wizard.py +0 -644
  230. wizards/care_plan.py +0 -321
  231. wizards/clinical_assessment.py +0 -769
  232. wizards/discharge_planning.py +0 -77
  233. wizards/discharge_summary_wizard.py +0 -468
  234. wizards/dosage_calculation.py +0 -497
  235. wizards/incident_report_wizard.py +0 -454
  236. wizards/medication_reconciliation.py +0 -85
  237. wizards/nursing_assessment.py +0 -171
  238. wizards/patient_education.py +0 -654
  239. wizards/quality_improvement.py +0 -705
  240. wizards/sbar_report.py +0 -324
  241. wizards/sbar_wizard.py +0 -608
  242. wizards/shift_handoff_wizard.py +0 -535
  243. wizards/soap_note_wizard.py +0 -679
  244. wizards/treatment_plan.py +0 -15
  245. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/WHEEL +0 -0
  246. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/entry_points.txt +0 -0
  247. {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,369 +0,0 @@
1
- """Verification Module
2
-
3
- Re-runs linters to verify fixes were successful.
4
-
5
- Copyright 2025 Smart AI Memory, LLC
6
- Licensed under Fair Source 0.9
7
- """
8
-
9
- import logging
10
- import subprocess
11
- from dataclasses import dataclass
12
- from pathlib import Path
13
- from typing import Any
14
-
15
- from .linter_parsers import LintIssue, parse_linter_output
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- def _validate_target_path(target: str) -> bool:
21
- """Validate target path is safe for linter subprocess execution.
22
-
23
- SECURITY: Prevents command injection via malicious file/directory paths.
24
- Checks that path exists and doesn't contain shell metacharacters.
25
- """
26
- if not target:
27
- return False
28
- try:
29
- p = Path(target).resolve()
30
- # Must exist as file or directory
31
- if not p.exists():
32
- return False
33
- # Must not contain shell metacharacters
34
- shell_chars = set(";|&$`(){}[]<>\\'\"\n\r\t")
35
- if any(c in str(p) for c in shell_chars):
36
- return False
37
- return True
38
- except Exception as e:
39
- # Security: Reject any path that fails validation (malformed, permission denied, etc.)
40
- logger.debug(f"Path validation failed for {target}: {e}")
41
- return False
42
-
43
-
44
- @dataclass
45
- class VerificationResult:
46
- """Result of verification check"""
47
-
48
- linter: str
49
- success: bool
50
- issues_before: int
51
- issues_after: int
52
- issues_fixed: int
53
- issues_remaining: int
54
- new_issues: int
55
- remaining_issues: list[LintIssue]
56
- error: str | None = None
57
-
58
- def to_dict(self) -> dict[str, Any]:
59
- """Convert to dictionary"""
60
- return {
61
- "linter": self.linter,
62
- "success": self.success,
63
- "issues_before": self.issues_before,
64
- "issues_after": self.issues_after,
65
- "issues_fixed": self.issues_fixed,
66
- "issues_remaining": self.issues_remaining,
67
- "new_issues": self.new_issues,
68
- "remaining_issues": [i.to_dict() for i in self.remaining_issues],
69
- "error": self.error,
70
- }
71
-
72
-
73
- class BaseLinterRunner:
74
- """Base class for running linters"""
75
-
76
- def __init__(self, linter_name: str):
77
- self.linter_name = linter_name
78
-
79
- def run(self, target: str, output_format: str = "json") -> list[LintIssue]:
80
- """Run linter on target.
81
-
82
- Args:
83
- target: File or directory to lint
84
- output_format: Output format ("json" or "text")
85
-
86
- Returns:
87
- List of LintIssue objects
88
-
89
- """
90
- raise NotImplementedError
91
-
92
-
93
- class ESLintRunner(BaseLinterRunner):
94
- """Run ESLint"""
95
-
96
- def __init__(self):
97
- super().__init__("eslint")
98
-
99
- def run(self, target: str, output_format: str = "json") -> list[LintIssue]:
100
- """Run ESLint"""
101
- # SECURITY: Validate target path before subprocess execution
102
- if not _validate_target_path(target):
103
- raise ValueError(f"Invalid or unsafe target path: {target}")
104
-
105
- cmd = ["npx", "eslint"]
106
-
107
- if output_format == "json":
108
- cmd.append("--format=json")
109
-
110
- cmd.append(target)
111
-
112
- try:
113
- result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
114
-
115
- # ESLint exits with 1 if there are violations (expected)
116
- output = result.stdout
117
-
118
- if output_format == "json":
119
- return parse_linter_output(self.linter_name, output, "json")
120
- return parse_linter_output(self.linter_name, output, "text")
121
-
122
- except subprocess.TimeoutExpired:
123
- return []
124
- except FileNotFoundError as e:
125
- raise RuntimeError("ESLint not found. Run: npm install eslint") from e
126
-
127
-
128
- class PylintRunner(BaseLinterRunner):
129
- """Run Pylint"""
130
-
131
- def __init__(self):
132
- super().__init__("pylint")
133
-
134
- def run(self, target: str, output_format: str = "json") -> list[LintIssue]:
135
- """Run Pylint"""
136
- # SECURITY: Validate target path before subprocess execution
137
- if not _validate_target_path(target):
138
- raise ValueError(f"Invalid or unsafe target path: {target}")
139
-
140
- cmd = ["pylint"]
141
-
142
- if output_format == "json":
143
- cmd.append("--output-format=json")
144
-
145
- cmd.append(target)
146
-
147
- try:
148
- result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
149
-
150
- # Pylint exits with non-zero if violations (expected)
151
- output = result.stdout
152
-
153
- if output_format == "json":
154
- return parse_linter_output(self.linter_name, output, "json")
155
- return parse_linter_output(self.linter_name, output, "text")
156
-
157
- except subprocess.TimeoutExpired:
158
- return []
159
- except FileNotFoundError as e:
160
- raise RuntimeError("Pylint not found. Run: pip install pylint") from e
161
-
162
-
163
- class MyPyRunner(BaseLinterRunner):
164
- """Run mypy"""
165
-
166
- def __init__(self):
167
- super().__init__("mypy")
168
-
169
- def run(self, target: str, output_format: str = "json") -> list[LintIssue]:
170
- """Run mypy"""
171
- # SECURITY: Validate target path before subprocess execution
172
- if not _validate_target_path(target):
173
- raise ValueError(f"Invalid or unsafe target path: {target}")
174
-
175
- cmd = ["mypy", target]
176
-
177
- try:
178
- result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
179
-
180
- output = result.stdout
181
-
182
- return parse_linter_output(self.linter_name, output, "text")
183
-
184
- except subprocess.TimeoutExpired:
185
- return []
186
- except FileNotFoundError as e:
187
- raise RuntimeError("mypy not found. Run: pip install mypy") from e
188
-
189
-
190
- class TypeScriptRunner(BaseLinterRunner):
191
- """Run TypeScript compiler"""
192
-
193
- def __init__(self):
194
- super().__init__("typescript")
195
-
196
- def run(self, target: str, output_format: str = "json") -> list[LintIssue]:
197
- """Run tsc"""
198
- cmd = ["npx", "tsc", "--noEmit"]
199
-
200
- # If target is a directory, use project mode
201
- if Path(target).is_dir():
202
- cmd.extend(["--project", target])
203
- else:
204
- cmd.append(target)
205
-
206
- try:
207
- result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
208
-
209
- output = result.stdout
210
-
211
- return parse_linter_output(self.linter_name, output, "text")
212
-
213
- except subprocess.TimeoutExpired:
214
- return []
215
- except FileNotFoundError as e:
216
- raise RuntimeError("TypeScript not found. Run: npm install typescript") from e
217
-
218
-
219
- class LinterRunnerFactory:
220
- """Factory for creating linter runners"""
221
-
222
- _runners = {
223
- "eslint": ESLintRunner,
224
- "pylint": PylintRunner,
225
- "mypy": MyPyRunner,
226
- "typescript": TypeScriptRunner,
227
- "tsc": TypeScriptRunner,
228
- }
229
-
230
- @classmethod
231
- def create(cls, linter_name: str) -> BaseLinterRunner:
232
- """Create linter runner"""
233
- runner_class = cls._runners.get(linter_name.lower())
234
-
235
- if not runner_class:
236
- raise ValueError(
237
- f"Unsupported linter runner: {linter_name}. "
238
- f"Supported: {', '.join(cls._runners.keys())}",
239
- )
240
-
241
- return runner_class()
242
-
243
-
244
- def run_linter(linter_name: str, target: str, output_format: str = "json") -> list[LintIssue]:
245
- """Run linter on target.
246
-
247
- Args:
248
- linter_name: Name of linter
249
- target: File or directory to lint
250
- output_format: "json" or "text"
251
-
252
- Returns:
253
- List of LintIssue objects
254
-
255
- Example:
256
- >>> issues = run_linter("eslint", "/path/to/project")
257
- >>> print(f"Found {len(issues)} issues")
258
-
259
- """
260
- runner = LinterRunnerFactory.create(linter_name)
261
- return runner.run(target, output_format)
262
-
263
-
264
- def verify_fixes(
265
- linter_name: str,
266
- target: str,
267
- issues_before: list[LintIssue],
268
- ) -> VerificationResult:
269
- """Verify that fixes were successful by re-running linter.
270
-
271
- Args:
272
- linter_name: Name of linter
273
- target: File or directory that was fixed
274
- issues_before: Issues that existed before fixes
275
-
276
- Returns:
277
- VerificationResult with comparison
278
-
279
- Example:
280
- >>> result = verify_fixes("eslint", "/path/to/project", original_issues)
281
- >>> if result.success:
282
- ... print(f"Fixed {result.issues_fixed} issues!")
283
-
284
- """
285
- try:
286
- # Re-run linter
287
- issues_after = run_linter(linter_name, target)
288
-
289
- # Compare
290
- issues_before_count = len(issues_before)
291
- issues_after_count = len(issues_after)
292
-
293
- issues_fixed = max(0, issues_before_count - issues_after_count)
294
- issues_remaining = issues_after_count
295
-
296
- # Check for new issues (regressions)
297
- before_keys = {(i.file_path, i.line, i.rule) for i in issues_before}
298
- after_keys = {(i.file_path, i.line, i.rule) for i in issues_after}
299
-
300
- new_issue_keys = after_keys - before_keys
301
- new_issues = len(new_issue_keys)
302
-
303
- return VerificationResult(
304
- linter=linter_name,
305
- success=(issues_after_count < issues_before_count and new_issues == 0),
306
- issues_before=issues_before_count,
307
- issues_after=issues_after_count,
308
- issues_fixed=issues_fixed,
309
- issues_remaining=issues_remaining,
310
- new_issues=new_issues,
311
- remaining_issues=issues_after,
312
- )
313
-
314
- except Exception as e:
315
- return VerificationResult(
316
- linter=linter_name,
317
- success=False,
318
- issues_before=len(issues_before),
319
- issues_after=0,
320
- issues_fixed=0,
321
- issues_remaining=0,
322
- new_issues=0,
323
- remaining_issues=[],
324
- error=str(e),
325
- )
326
-
327
-
328
- def compare_issue_lists(before: list[LintIssue], after: list[LintIssue]) -> dict[str, Any]:
329
- """Detailed comparison of issue lists.
330
-
331
- Args:
332
- before: Issues before fixes
333
- after: Issues after fixes
334
-
335
- Returns:
336
- Dictionary with detailed comparison
337
-
338
- """
339
- before_set = {(i.file_path, i.line, i.column, i.rule) for i in before}
340
- after_set = {(i.file_path, i.line, i.column, i.rule) for i in after}
341
-
342
- fixed = before_set - after_set
343
- remaining = before_set & after_set
344
- new = after_set - before_set
345
-
346
- # Group by file
347
- files_improved = set()
348
- files_regressed = set()
349
-
350
- for issue in before:
351
- key = (issue.file_path, issue.line, issue.column, issue.rule)
352
- if key in fixed:
353
- files_improved.add(issue.file_path)
354
-
355
- for issue in after:
356
- key = (issue.file_path, issue.line, issue.column, issue.rule)
357
- if key in new:
358
- files_regressed.add(issue.file_path)
359
-
360
- return {
361
- "total_before": len(before),
362
- "total_after": len(after),
363
- "fixed_count": len(fixed),
364
- "remaining_count": len(remaining),
365
- "new_count": len(new),
366
- "files_improved": list(files_improved),
367
- "files_regressed": list(files_regressed),
368
- "net_improvement": len(fixed) - len(new),
369
- }