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.
- empathy_framework-4.7.1.dist-info/METADATA +690 -0
- empathy_framework-4.7.1.dist-info/RECORD +379 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/top_level.txt +1 -2
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
- empathy_llm_toolkit/agent_factory/__init__.py +6 -6
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
- empathy_llm_toolkit/agents_md/__init__.py +22 -0
- empathy_llm_toolkit/agents_md/loader.py +218 -0
- empathy_llm_toolkit/agents_md/parser.py +271 -0
- empathy_llm_toolkit/agents_md/registry.py +307 -0
- empathy_llm_toolkit/commands/__init__.py +51 -0
- empathy_llm_toolkit/commands/context.py +375 -0
- empathy_llm_toolkit/commands/loader.py +301 -0
- empathy_llm_toolkit/commands/models.py +231 -0
- empathy_llm_toolkit/commands/parser.py +371 -0
- empathy_llm_toolkit/commands/registry.py +429 -0
- empathy_llm_toolkit/config/__init__.py +8 -8
- empathy_llm_toolkit/config/unified.py +3 -7
- empathy_llm_toolkit/context/__init__.py +22 -0
- empathy_llm_toolkit/context/compaction.py +455 -0
- empathy_llm_toolkit/context/manager.py +434 -0
- empathy_llm_toolkit/hooks/__init__.py +24 -0
- empathy_llm_toolkit/hooks/config.py +306 -0
- empathy_llm_toolkit/hooks/executor.py +289 -0
- empathy_llm_toolkit/hooks/registry.py +302 -0
- empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
- empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
- empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
- empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
- empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
- empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
- empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
- empathy_llm_toolkit/learning/__init__.py +30 -0
- empathy_llm_toolkit/learning/evaluator.py +438 -0
- empathy_llm_toolkit/learning/extractor.py +514 -0
- empathy_llm_toolkit/learning/storage.py +560 -0
- empathy_llm_toolkit/providers.py +4 -11
- empathy_llm_toolkit/security/__init__.py +17 -17
- empathy_llm_toolkit/utils/tokens.py +2 -5
- empathy_os/__init__.py +202 -70
- empathy_os/cache_monitor.py +5 -3
- empathy_os/cli/__init__.py +11 -55
- empathy_os/cli/__main__.py +29 -15
- empathy_os/cli/commands/inspection.py +21 -12
- empathy_os/cli/commands/memory.py +4 -12
- empathy_os/cli/commands/profiling.py +198 -0
- empathy_os/cli/commands/utilities.py +27 -7
- empathy_os/cli.py +28 -57
- empathy_os/cli_unified.py +525 -1164
- empathy_os/cost_tracker.py +9 -3
- empathy_os/dashboard/server.py +200 -2
- empathy_os/hot_reload/__init__.py +7 -7
- empathy_os/hot_reload/config.py +6 -7
- empathy_os/hot_reload/integration.py +35 -35
- empathy_os/hot_reload/reloader.py +57 -57
- empathy_os/hot_reload/watcher.py +28 -28
- empathy_os/hot_reload/websocket.py +2 -2
- empathy_os/memory/__init__.py +11 -4
- empathy_os/memory/claude_memory.py +1 -1
- empathy_os/memory/cross_session.py +8 -12
- empathy_os/memory/edges.py +6 -6
- empathy_os/memory/file_session.py +770 -0
- empathy_os/memory/graph.py +30 -30
- empathy_os/memory/nodes.py +6 -6
- empathy_os/memory/short_term.py +15 -9
- empathy_os/memory/unified.py +606 -140
- empathy_os/meta_workflows/agent_creator.py +3 -9
- empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
- empathy_os/meta_workflows/form_engine.py +6 -18
- empathy_os/meta_workflows/intent_detector.py +64 -24
- empathy_os/meta_workflows/models.py +3 -1
- empathy_os/meta_workflows/pattern_learner.py +13 -31
- empathy_os/meta_workflows/plan_generator.py +55 -47
- empathy_os/meta_workflows/session_context.py +2 -3
- empathy_os/meta_workflows/workflow.py +20 -51
- empathy_os/models/cli.py +2 -2
- empathy_os/models/tasks.py +1 -2
- empathy_os/models/telemetry.py +4 -1
- empathy_os/models/token_estimator.py +3 -1
- empathy_os/monitoring/alerts.py +938 -9
- empathy_os/monitoring/alerts_cli.py +346 -183
- empathy_os/orchestration/execution_strategies.py +12 -29
- empathy_os/orchestration/pattern_learner.py +20 -26
- empathy_os/orchestration/real_tools.py +6 -15
- empathy_os/platform_utils.py +2 -1
- empathy_os/plugins/__init__.py +2 -2
- empathy_os/plugins/base.py +64 -64
- empathy_os/plugins/registry.py +32 -32
- empathy_os/project_index/index.py +49 -15
- empathy_os/project_index/models.py +1 -2
- empathy_os/project_index/reports.py +1 -1
- empathy_os/project_index/scanner.py +1 -0
- empathy_os/redis_memory.py +10 -7
- empathy_os/resilience/__init__.py +1 -1
- empathy_os/resilience/health.py +10 -10
- empathy_os/routing/__init__.py +7 -7
- empathy_os/routing/chain_executor.py +37 -37
- empathy_os/routing/classifier.py +36 -36
- empathy_os/routing/smart_router.py +40 -40
- empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
- empathy_os/scaffolding/__init__.py +8 -8
- empathy_os/scaffolding/__main__.py +1 -1
- empathy_os/scaffolding/cli.py +28 -28
- empathy_os/socratic/__init__.py +3 -19
- empathy_os/socratic/ab_testing.py +25 -36
- empathy_os/socratic/blueprint.py +38 -38
- empathy_os/socratic/cli.py +34 -20
- empathy_os/socratic/collaboration.py +30 -28
- empathy_os/socratic/domain_templates.py +9 -1
- empathy_os/socratic/embeddings.py +17 -13
- empathy_os/socratic/engine.py +135 -70
- empathy_os/socratic/explainer.py +70 -60
- empathy_os/socratic/feedback.py +24 -19
- empathy_os/socratic/forms.py +15 -10
- empathy_os/socratic/generator.py +51 -35
- empathy_os/socratic/llm_analyzer.py +25 -23
- empathy_os/socratic/mcp_server.py +99 -159
- empathy_os/socratic/session.py +19 -13
- empathy_os/socratic/storage.py +98 -67
- empathy_os/socratic/success.py +38 -27
- empathy_os/socratic/visual_editor.py +51 -39
- empathy_os/socratic/web_ui.py +99 -66
- empathy_os/telemetry/cli.py +3 -1
- empathy_os/telemetry/usage_tracker.py +1 -3
- empathy_os/test_generator/__init__.py +3 -3
- empathy_os/test_generator/cli.py +28 -28
- empathy_os/test_generator/generator.py +64 -66
- empathy_os/test_generator/risk_analyzer.py +11 -11
- empathy_os/vscode_bridge 2.py +173 -0
- empathy_os/vscode_bridge.py +173 -0
- empathy_os/workflows/__init__.py +212 -120
- empathy_os/workflows/batch_processing.py +8 -24
- empathy_os/workflows/bug_predict.py +1 -1
- empathy_os/workflows/code_review.py +20 -5
- empathy_os/workflows/code_review_pipeline.py +13 -8
- empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
- empathy_os/workflows/manage_documentation.py +1 -0
- empathy_os/workflows/orchestrated_health_check.py +6 -11
- empathy_os/workflows/orchestrated_release_prep.py +3 -3
- empathy_os/workflows/pr_review.py +18 -10
- empathy_os/workflows/progressive/README 2.md +454 -0
- empathy_os/workflows/progressive/__init__ 2.py +92 -0
- empathy_os/workflows/progressive/__init__.py +2 -12
- empathy_os/workflows/progressive/cli 2.py +242 -0
- empathy_os/workflows/progressive/cli.py +14 -37
- empathy_os/workflows/progressive/core 2.py +488 -0
- empathy_os/workflows/progressive/core.py +12 -12
- empathy_os/workflows/progressive/orchestrator 2.py +701 -0
- empathy_os/workflows/progressive/orchestrator.py +166 -144
- empathy_os/workflows/progressive/reports 2.py +528 -0
- empathy_os/workflows/progressive/reports.py +22 -31
- empathy_os/workflows/progressive/telemetry 2.py +280 -0
- empathy_os/workflows/progressive/telemetry.py +8 -14
- empathy_os/workflows/progressive/test_gen 2.py +514 -0
- empathy_os/workflows/progressive/test_gen.py +29 -48
- empathy_os/workflows/progressive/workflow 2.py +628 -0
- empathy_os/workflows/progressive/workflow.py +31 -70
- empathy_os/workflows/release_prep.py +21 -6
- empathy_os/workflows/release_prep_crew.py +1 -0
- empathy_os/workflows/secure_release.py +13 -6
- empathy_os/workflows/security_audit.py +8 -3
- empathy_os/workflows/test_coverage_boost_crew.py +3 -2
- empathy_os/workflows/test_maintenance_crew.py +1 -0
- empathy_os/workflows/test_runner.py +16 -12
- empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
- empathy_software_plugin/cli.py +0 -122
- patterns/README.md +119 -0
- patterns/__init__.py +95 -0
- patterns/behavior.py +298 -0
- patterns/code_review_memory.json +441 -0
- patterns/core.py +97 -0
- patterns/debugging.json +3763 -0
- patterns/empathy.py +268 -0
- patterns/health_check_memory.json +505 -0
- patterns/input.py +161 -0
- patterns/memory_graph.json +8 -0
- patterns/refactoring_memory.json +1113 -0
- patterns/registry.py +663 -0
- patterns/security_memory.json +8 -0
- patterns/structural.py +415 -0
- patterns/validation.py +194 -0
- coach_wizards/__init__.py +0 -45
- coach_wizards/accessibility_wizard.py +0 -91
- coach_wizards/api_wizard.py +0 -91
- coach_wizards/base_wizard.py +0 -209
- coach_wizards/cicd_wizard.py +0 -91
- coach_wizards/code_reviewer_README.md +0 -60
- coach_wizards/code_reviewer_wizard.py +0 -180
- coach_wizards/compliance_wizard.py +0 -91
- coach_wizards/database_wizard.py +0 -91
- coach_wizards/debugging_wizard.py +0 -91
- coach_wizards/documentation_wizard.py +0 -91
- coach_wizards/generate_wizards.py +0 -347
- coach_wizards/localization_wizard.py +0 -173
- coach_wizards/migration_wizard.py +0 -91
- coach_wizards/monitoring_wizard.py +0 -91
- coach_wizards/observability_wizard.py +0 -91
- coach_wizards/performance_wizard.py +0 -91
- coach_wizards/prompt_engineering_wizard.py +0 -661
- coach_wizards/refactoring_wizard.py +0 -91
- coach_wizards/scaling_wizard.py +0 -90
- coach_wizards/security_wizard.py +0 -92
- coach_wizards/testing_wizard.py +0 -91
- empathy_framework-4.6.6.dist-info/METADATA +0 -1597
- empathy_framework-4.6.6.dist-info/RECORD +0 -410
- empathy_llm_toolkit/wizards/__init__.py +0 -43
- empathy_llm_toolkit/wizards/base_wizard.py +0 -364
- empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
- empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
- empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
- empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
- empathy_os/wizard_factory_cli.py +0 -170
- empathy_software_plugin/wizards/__init__.py +0 -42
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
- empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
- empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
- empathy_software_plugin/wizards/base_wizard.py +0 -288
- empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
- empathy_software_plugin/wizards/code_review_wizard.py +0 -604
- empathy_software_plugin/wizards/debugging/__init__.py +0 -50
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
- empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
- empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
- empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
- empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
- empathy_software_plugin/wizards/debugging/verification.py +0 -369
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
- empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
- empathy_software_plugin/wizards/performance/__init__.py +0 -9
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
- empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
- empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
- empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
- empathy_software_plugin/wizards/security/__init__.py +0 -32
- empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
- empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
- empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
- empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
- empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
- empathy_software_plugin/wizards/testing/__init__.py +0 -27
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
- empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
- empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
- empathy_software_plugin/wizards/testing_wizard.py +0 -274
- wizards/__init__.py +0 -82
- wizards/admission_assessment_wizard.py +0 -644
- wizards/care_plan.py +0 -321
- wizards/clinical_assessment.py +0 -769
- wizards/discharge_planning.py +0 -77
- wizards/discharge_summary_wizard.py +0 -468
- wizards/dosage_calculation.py +0 -497
- wizards/incident_report_wizard.py +0 -454
- wizards/medication_reconciliation.py +0 -85
- wizards/nursing_assessment.py +0 -171
- wizards/patient_education.py +0 -654
- wizards/quality_improvement.py +0 -705
- wizards/sbar_report.py +0 -324
- wizards/sbar_wizard.py +0 -608
- wizards/shift_handoff_wizard.py +0 -535
- wizards/soap_note_wizard.py +0 -679
- wizards/treatment_plan.py +0 -15
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/WHEEL +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.1.dist-info}/entry_points.txt +0 -0
- {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}
|