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,469 +0,0 @@
1
- """Fix Applier
2
-
3
- Systematically applies fixes to code based on linter violations.
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
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- def _validate_file_path(file_path: str) -> bool:
21
- """Validate file path is safe for subprocess execution.
22
-
23
- SECURITY: Prevents command injection via malicious file paths.
24
- Checks that path exists, is a file, and doesn't contain shell metacharacters.
25
- """
26
- if not file_path:
27
- return False
28
- try:
29
- p = Path(file_path).resolve()
30
- # Must exist and be a file
31
- if not p.exists() or not p.is_file():
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 {file_path}: {e}")
41
- return False
42
-
43
-
44
- @dataclass
45
- class FixResult:
46
- """Result of attempting to fix an issue"""
47
-
48
- issue: LintIssue
49
- success: bool
50
- method: str # "autofix", "manual_suggestion", "skipped"
51
- changes_made: str | None = None
52
- error: str | None = None
53
-
54
- def to_dict(self) -> dict[str, Any]:
55
- """Convert to dictionary"""
56
- return {
57
- "issue": self.issue.to_dict(),
58
- "success": self.success,
59
- "method": self.method,
60
- "changes_made": self.changes_made,
61
- "error": self.error,
62
- }
63
-
64
-
65
- class BaseFixApplier:
66
- """Base class for fix appliers"""
67
-
68
- def __init__(self, linter_name: str):
69
- self.linter_name = linter_name
70
-
71
- def can_autofix(self, issue: LintIssue) -> bool:
72
- """Check if issue can be auto-fixed"""
73
- raise NotImplementedError(
74
- f"{self.__class__.__name__}.can_autofix() must be implemented. "
75
- "Create a subclass of BaseFixApplier and implement the can_autofix() method. "
76
- f"See ESLintFixApplier, PylintFixApplier, or TypeScriptFixApplier for examples."
77
- )
78
-
79
- def apply_fix(self, issue: LintIssue, dry_run: bool = False) -> FixResult:
80
- """Apply fix for issue.
81
-
82
- Args:
83
- issue: LintIssue to fix
84
- dry_run: If True, don't actually make changes
85
-
86
- Returns:
87
- FixResult with outcome
88
-
89
- """
90
- raise NotImplementedError(
91
- f"{self.__class__.__name__}.apply_fix() must be implemented. "
92
- "Create a subclass of BaseFixApplier and implement the apply_fix() method. "
93
- f"See ESLintFixApplier, PylintFixApplier, or TypeScriptFixApplier for examples."
94
- )
95
-
96
- def apply_fixes_batch(self, issues: list[LintIssue], dry_run: bool = False) -> list[FixResult]:
97
- """Apply fixes for multiple issues"""
98
- results = []
99
- for issue in issues:
100
- result = self.apply_fix(issue, dry_run)
101
- results.append(result)
102
- return results
103
-
104
- def suggest_manual_fix(self, issue: LintIssue) -> str:
105
- """Provide suggestion for manual fix"""
106
- raise NotImplementedError(
107
- f"{self.__class__.__name__}.suggest_manual_fix() must be implemented. "
108
- "Create a subclass of BaseFixApplier and implement the suggest_manual_fix() method. "
109
- f"See ESLintFixApplier, PylintFixApplier, or TypeScriptFixApplier for examples."
110
- )
111
-
112
-
113
- class ESLintFixApplier(BaseFixApplier):
114
- """Apply ESLint fixes.
115
-
116
- Uses --fix flag for auto-fixable issues.
117
- """
118
-
119
- def __init__(self):
120
- super().__init__("eslint")
121
- self.autofixable_rules = self._get_autofixable_rules()
122
-
123
- def _get_autofixable_rules(self) -> set:
124
- """Get set of auto-fixable ESLint rules.
125
-
126
- In practice, we'd query ESLint, but for now use common ones.
127
- """
128
- return {
129
- "semi",
130
- "quotes",
131
- "comma-dangle",
132
- "no-extra-semi",
133
- "no-multi-spaces",
134
- "space-before-blocks",
135
- "keyword-spacing",
136
- "object-curly-spacing",
137
- "array-bracket-spacing",
138
- "eol-last",
139
- "no-trailing-spaces",
140
- "indent",
141
- "arrow-spacing",
142
- "prefer-const",
143
- "no-var",
144
- }
145
-
146
- def can_autofix(self, issue: LintIssue) -> bool:
147
- """Check if ESLint can auto-fix this rule"""
148
- return issue.rule in self.autofixable_rules or issue.has_autofix
149
-
150
- def apply_fix(self, issue: LintIssue, dry_run: bool = False) -> FixResult:
151
- """Apply ESLint fix"""
152
- if not self.can_autofix(issue):
153
- # Provide manual suggestion
154
- _suggestion = self.suggest_manual_fix(issue)
155
- return FixResult(
156
- issue=issue,
157
- success=False,
158
- method="manual_suggestion",
159
- changes_made=None,
160
- error=None,
161
- )
162
-
163
- if dry_run:
164
- return FixResult(
165
- issue=issue,
166
- success=True,
167
- method="autofix",
168
- changes_made="Would fix with ESLint --fix",
169
- )
170
-
171
- # Run ESLint --fix on specific file
172
- # SECURITY: Validate file path before subprocess execution
173
- if not _validate_file_path(issue.file_path):
174
- return FixResult(
175
- issue=issue,
176
- success=False,
177
- method="autofix",
178
- error=f"Invalid or unsafe file path: {issue.file_path}",
179
- )
180
-
181
- try:
182
- result = subprocess.run(
183
- ["npx", "eslint", "--fix", issue.file_path],
184
- check=False,
185
- capture_output=True,
186
- text=True,
187
- timeout=30,
188
- )
189
-
190
- return FixResult(
191
- issue=issue,
192
- success=result.returncode == 0,
193
- method="autofix",
194
- changes_made=f"ESLint --fix applied to {issue.file_path}",
195
- )
196
-
197
- except subprocess.TimeoutExpired:
198
- return FixResult(issue=issue, success=False, method="autofix", error="ESLint timeout")
199
- except FileNotFoundError:
200
- return FixResult(
201
- issue=issue,
202
- success=False,
203
- method="autofix",
204
- error="ESLint not found (run npm install)",
205
- )
206
-
207
- def suggest_manual_fix(self, issue: LintIssue) -> str:
208
- """Suggest manual fix for ESLint issue"""
209
- # Extract variable name from message if present
210
- var_name = "variable"
211
- if "'" in issue.message:
212
- parts = issue.message.split("'")
213
- if len(parts) > 1:
214
- var_name = parts[1]
215
-
216
- suggestions = {
217
- "no-undef": f"Define '{var_name}' or import it",
218
- "no-unused-vars": "Remove unused variable or prefix with _",
219
- "eqeqeq": "Use === instead of ==",
220
- "no-console": "Remove console.log or use a logger",
221
- "prefer-const": "Change 'let' to 'const' if variable never reassigned",
222
- }
223
-
224
- return suggestions.get(issue.rule, f"Manual fix required for {issue.rule}: {issue.message}")
225
-
226
-
227
- class PylintFixApplier(BaseFixApplier):
228
- """Apply Pylint fixes.
229
-
230
- Pylint doesn't have auto-fix, so we provide suggestions.
231
- Can integrate with autopep8/black for some fixes.
232
- """
233
-
234
- def __init__(self):
235
- super().__init__("pylint")
236
-
237
- def can_autofix(self, issue: LintIssue) -> bool:
238
- """Pylint itself doesn't auto-fix, but we can use other tools.
239
-
240
- Some formatting issues can be fixed with black/autopep8.
241
- """
242
- formatting_rules = {
243
- "missing-final-newline",
244
- "trailing-whitespace",
245
- "line-too-long",
246
- "bad-whitespace",
247
- "bad-indentation",
248
- }
249
-
250
- return issue.rule in formatting_rules
251
-
252
- def apply_fix(self, issue: LintIssue, dry_run: bool = False) -> FixResult:
253
- """Apply Pylint fix (via black/autopep8 if possible)"""
254
- if not self.can_autofix(issue):
255
- suggestion = self.suggest_manual_fix(issue)
256
- return FixResult(
257
- issue=issue,
258
- success=False,
259
- method="manual_suggestion",
260
- changes_made=suggestion,
261
- )
262
-
263
- if dry_run:
264
- return FixResult(
265
- issue=issue,
266
- success=True,
267
- method="autofix",
268
- changes_made="Would format with black",
269
- )
270
-
271
- # SECURITY: Validate file path before subprocess execution
272
- if not _validate_file_path(issue.file_path):
273
- return FixResult(
274
- issue=issue,
275
- success=False,
276
- method="autofix",
277
- error=f"Invalid or unsafe file path: {issue.file_path}",
278
- )
279
-
280
- # Try black first
281
- try:
282
- result = subprocess.run(
283
- ["black", issue.file_path],
284
- check=False,
285
- capture_output=True,
286
- text=True,
287
- timeout=30,
288
- )
289
-
290
- if result.returncode == 0:
291
- return FixResult(
292
- issue=issue,
293
- success=True,
294
- method="autofix",
295
- changes_made=f"Formatted with black: {issue.file_path}",
296
- )
297
-
298
- except (FileNotFoundError, subprocess.TimeoutExpired):
299
- pass
300
-
301
- # Try autopep8
302
- try:
303
- result = subprocess.run(
304
- ["autopep8", "--in-place", issue.file_path],
305
- check=False,
306
- capture_output=True,
307
- text=True,
308
- timeout=30,
309
- )
310
-
311
- if result.returncode == 0:
312
- return FixResult(
313
- issue=issue,
314
- success=True,
315
- method="autofix",
316
- changes_made=f"Formatted with autopep8: {issue.file_path}",
317
- )
318
-
319
- except (FileNotFoundError, subprocess.TimeoutExpired):
320
- pass
321
-
322
- return FixResult(
323
- issue=issue,
324
- success=False,
325
- method="manual_suggestion",
326
- error="black/autopep8 not available",
327
- )
328
-
329
- def suggest_manual_fix(self, issue: LintIssue) -> str:
330
- """Suggest manual fix"""
331
- suggestions = {
332
- "unused-variable": "Remove variable or prefix with _",
333
- "unused-import": "Remove unused import",
334
- "invalid-name": "Rename to follow naming conventions",
335
- "missing-docstring": "Add docstring to function/class",
336
- "too-many-arguments": "Reduce parameters or use config object",
337
- "no-else-return": "Remove else after return statement",
338
- }
339
-
340
- return suggestions.get(issue.rule, f"Manual fix required: {issue.message}")
341
-
342
-
343
- class TypeScriptFixApplier(BaseFixApplier):
344
- """Apply TypeScript fixes.
345
-
346
- TypeScript compiler doesn't auto-fix, but we can suggest fixes.
347
- """
348
-
349
- def __init__(self):
350
- super().__init__("typescript")
351
-
352
- def can_autofix(self, issue: LintIssue) -> bool:
353
- """TypeScript doesn't auto-fix"""
354
- return False
355
-
356
- def apply_fix(self, issue: LintIssue, dry_run: bool = False) -> FixResult:
357
- """TypeScript fixes are manual"""
358
- suggestion = self.suggest_manual_fix(issue)
359
-
360
- return FixResult(
361
- issue=issue,
362
- success=False,
363
- method="manual_suggestion",
364
- changes_made=suggestion,
365
- )
366
-
367
- def suggest_manual_fix(self, issue: LintIssue) -> str:
368
- """Suggest TypeScript fix"""
369
- # Extract TS error code
370
- if issue.rule.startswith("TS"):
371
- code = issue.rule[2:]
372
-
373
- suggestions = {
374
- "2322": "Fix type mismatch - check assigned value type",
375
- "2345": "Fix argument type - check function parameter types",
376
- "2339": "Property doesn't exist - check object structure",
377
- "2304": "Cannot find name - import or define the type/variable",
378
- "2551": "Property doesn't exist - check for typos",
379
- "7006": "Add type annotation - implicit any",
380
- }
381
-
382
- suggestion = suggestions.get(code, f"Fix type error: {issue.message}")
383
- return suggestion
384
-
385
- return f"Manual fix required: {issue.message}"
386
-
387
-
388
- class FixApplierFactory:
389
- """Factory for creating fix appliers"""
390
-
391
- _appliers = {
392
- "eslint": ESLintFixApplier,
393
- "pylint": PylintFixApplier,
394
- "typescript": TypeScriptFixApplier,
395
- "tsc": TypeScriptFixApplier,
396
- "mypy": PylintFixApplier, # Similar to Pylint (no autofix)
397
- }
398
-
399
- @classmethod
400
- def create(cls, linter_name: str) -> BaseFixApplier:
401
- """Create fix applier for linter"""
402
- applier_class = cls._appliers.get(linter_name.lower())
403
-
404
- if not applier_class:
405
- raise ValueError(
406
- f"Unsupported fix applier: {linter_name}. "
407
- f"Supported: {', '.join(cls._appliers.keys())}",
408
- )
409
-
410
- return applier_class()
411
-
412
-
413
- def apply_fixes(
414
- linter_name: str,
415
- issues: list[LintIssue],
416
- dry_run: bool = False,
417
- auto_only: bool = False,
418
- ) -> list[FixResult]:
419
- """Apply fixes for list of issues.
420
-
421
- Args:
422
- linter_name: Name of linter
423
- issues: List of issues to fix
424
- dry_run: Don't actually make changes
425
- auto_only: Only apply auto-fixable issues
426
-
427
- Returns:
428
- List of FixResult objects
429
-
430
- Example:
431
- >>> results = apply_fixes("eslint", issues, dry_run=True)
432
- >>> auto_fixed = [r for r in results if r.method == "autofix" and r.success]
433
- >>> print(f"Could auto-fix {len(auto_fixed)} issues")
434
-
435
- """
436
- applier = FixApplierFactory.create(linter_name)
437
-
438
- if auto_only:
439
- issues = [i for i in issues if applier.can_autofix(i)]
440
-
441
- return applier.apply_fixes_batch(issues, dry_run)
442
-
443
-
444
- def group_issues_by_fixability(
445
- linter_name: str,
446
- issues: list[LintIssue],
447
- ) -> dict[str, list[LintIssue]]:
448
- """Group issues by whether they can be auto-fixed.
449
-
450
- Args:
451
- linter_name: Name of linter
452
- issues: List of issues
453
-
454
- Returns:
455
- Dictionary with "auto_fixable" and "manual" keys
456
-
457
- """
458
- applier = FixApplierFactory.create(linter_name)
459
-
460
- auto_fixable = []
461
- manual = []
462
-
463
- for issue in issues:
464
- if applier.can_autofix(issue):
465
- auto_fixable.append(issue)
466
- else:
467
- manual.append(issue)
468
-
469
- return {"auto_fixable": auto_fixable, "manual": manual}