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.
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/METADATA +7 -6
- empathy_framework-4.7.0.dist-info/RECORD +354 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/top_level.txt +0 -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.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/__init__.py +2 -12
- empathy_os/workflows/progressive/cli.py +14 -37
- empathy_os/workflows/progressive/core.py +12 -12
- empathy_os/workflows/progressive/orchestrator.py +166 -144
- empathy_os/workflows/progressive/reports.py +22 -31
- empathy_os/workflows/progressive/telemetry.py +8 -14
- empathy_os/workflows/progressive/test_gen.py +29 -48
- 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
- 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/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.0.dist-info}/WHEEL +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/entry_points.txt +0 -0
- {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
|
-
}
|