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