lintro 0.13.2__py3-none-any.whl → 0.17.2__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.
- lintro/__init__.py +1 -1
- lintro/cli.py +226 -16
- lintro/cli_utils/commands/__init__.py +8 -1
- lintro/cli_utils/commands/check.py +1 -0
- lintro/cli_utils/commands/config.py +325 -0
- lintro/cli_utils/commands/init.py +361 -0
- lintro/cli_utils/commands/list_tools.py +180 -42
- lintro/cli_utils/commands/test.py +316 -0
- lintro/cli_utils/commands/versions.py +81 -0
- lintro/config/__init__.py +62 -0
- lintro/config/config_loader.py +420 -0
- lintro/config/lintro_config.py +189 -0
- lintro/config/tool_config_generator.py +403 -0
- lintro/enums/tool_name.py +2 -0
- lintro/enums/tool_type.py +2 -0
- lintro/formatters/tools/__init__.py +12 -0
- lintro/formatters/tools/eslint_formatter.py +108 -0
- lintro/formatters/tools/markdownlint_formatter.py +88 -0
- lintro/formatters/tools/pytest_formatter.py +201 -0
- lintro/parsers/__init__.py +69 -9
- lintro/parsers/bandit/__init__.py +6 -0
- lintro/parsers/bandit/bandit_issue.py +49 -0
- lintro/parsers/bandit/bandit_parser.py +99 -0
- lintro/parsers/black/black_issue.py +4 -0
- lintro/parsers/eslint/__init__.py +6 -0
- lintro/parsers/eslint/eslint_issue.py +26 -0
- lintro/parsers/eslint/eslint_parser.py +63 -0
- lintro/parsers/markdownlint/__init__.py +6 -0
- lintro/parsers/markdownlint/markdownlint_issue.py +22 -0
- lintro/parsers/markdownlint/markdownlint_parser.py +113 -0
- lintro/parsers/pytest/__init__.py +21 -0
- lintro/parsers/pytest/pytest_issue.py +28 -0
- lintro/parsers/pytest/pytest_parser.py +483 -0
- lintro/tools/__init__.py +2 -0
- lintro/tools/core/timeout_utils.py +112 -0
- lintro/tools/core/tool_base.py +255 -45
- lintro/tools/core/tool_manager.py +77 -24
- lintro/tools/core/version_requirements.py +482 -0
- lintro/tools/implementations/pytest/pytest_command_builder.py +311 -0
- lintro/tools/implementations/pytest/pytest_config.py +200 -0
- lintro/tools/implementations/pytest/pytest_error_handler.py +128 -0
- lintro/tools/implementations/pytest/pytest_executor.py +122 -0
- lintro/tools/implementations/pytest/pytest_handlers.py +375 -0
- lintro/tools/implementations/pytest/pytest_option_validators.py +212 -0
- lintro/tools/implementations/pytest/pytest_output_processor.py +408 -0
- lintro/tools/implementations/pytest/pytest_result_processor.py +113 -0
- lintro/tools/implementations/pytest/pytest_utils.py +697 -0
- lintro/tools/implementations/tool_actionlint.py +106 -16
- lintro/tools/implementations/tool_bandit.py +23 -7
- lintro/tools/implementations/tool_black.py +236 -29
- lintro/tools/implementations/tool_darglint.py +180 -21
- lintro/tools/implementations/tool_eslint.py +374 -0
- lintro/tools/implementations/tool_hadolint.py +94 -25
- lintro/tools/implementations/tool_markdownlint.py +354 -0
- lintro/tools/implementations/tool_prettier.py +313 -26
- lintro/tools/implementations/tool_pytest.py +327 -0
- lintro/tools/implementations/tool_ruff.py +247 -70
- lintro/tools/implementations/tool_yamllint.py +448 -34
- lintro/tools/tool_enum.py +6 -0
- lintro/utils/config.py +41 -18
- lintro/utils/console_logger.py +211 -25
- lintro/utils/path_utils.py +42 -0
- lintro/utils/tool_executor.py +336 -39
- lintro/utils/tool_utils.py +38 -2
- lintro/utils/unified_config.py +926 -0
- {lintro-0.13.2.dist-info → lintro-0.17.2.dist-info}/METADATA +131 -29
- lintro-0.17.2.dist-info/RECORD +134 -0
- lintro-0.13.2.dist-info/RECORD +0 -96
- {lintro-0.13.2.dist-info → lintro-0.17.2.dist-info}/WHEEL +0 -0
- {lintro-0.13.2.dist-info → lintro-0.17.2.dist-info}/entry_points.txt +0 -0
- {lintro-0.13.2.dist-info → lintro-0.17.2.dist-info}/licenses/LICENSE +0 -0
- {lintro-0.13.2.dist-info → lintro-0.17.2.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""Prettier code formatter integration."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import subprocess # nosec B404 - used safely with shell disabled
|
|
4
5
|
from dataclasses import dataclass, field
|
|
5
6
|
|
|
6
7
|
from loguru import logger
|
|
7
8
|
|
|
8
9
|
from lintro.enums.tool_type import ToolType
|
|
9
10
|
from lintro.models.core.tool import Tool, ToolConfig, ToolResult
|
|
11
|
+
from lintro.parsers.prettier.prettier_issue import PrettierIssue
|
|
10
12
|
from lintro.parsers.prettier.prettier_parser import parse_prettier_output
|
|
11
13
|
from lintro.tools.core.tool_base import BaseTool
|
|
12
14
|
from lintro.utils.tool_utils import walk_files_with_excludes
|
|
@@ -55,12 +57,21 @@ class PrettierTool(BaseTool):
|
|
|
55
57
|
),
|
|
56
58
|
)
|
|
57
59
|
|
|
60
|
+
def __post_init__(self) -> None:
|
|
61
|
+
"""Initialize prettier tool."""
|
|
62
|
+
super().__post_init__()
|
|
63
|
+
# Note: .prettierignore is handled by passing --ignore-path to prettier
|
|
64
|
+
# rather than loading into lintro's exclude patterns, to ensure prettier's
|
|
65
|
+
# native ignore logic is used consistently
|
|
66
|
+
|
|
58
67
|
def set_options(
|
|
59
68
|
self,
|
|
60
69
|
exclude_patterns: list[str] | None = None,
|
|
61
70
|
include_venv: bool = False,
|
|
62
71
|
timeout: int | None = None,
|
|
63
72
|
verbose_fix_output: bool | None = None,
|
|
73
|
+
line_length: int | None = None,
|
|
74
|
+
**kwargs,
|
|
64
75
|
) -> None:
|
|
65
76
|
"""Set options for the core.
|
|
66
77
|
|
|
@@ -69,13 +80,26 @@ class PrettierTool(BaseTool):
|
|
|
69
80
|
include_venv: Whether to include virtual environment directories
|
|
70
81
|
timeout: Timeout in seconds per file (default: 30)
|
|
71
82
|
verbose_fix_output: If True, include raw Prettier output in fix()
|
|
83
|
+
line_length: Print width for prettier (maps to --print-width).
|
|
84
|
+
If provided, this will be stored and used in CLI args.
|
|
85
|
+
**kwargs: Additional options (ignored for compatibility)
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
ValueError: If line_length is not a positive integer.
|
|
72
89
|
"""
|
|
73
|
-
|
|
90
|
+
if exclude_patterns is not None:
|
|
91
|
+
self.exclude_patterns = exclude_patterns.copy()
|
|
74
92
|
self.include_venv = include_venv
|
|
75
93
|
if timeout is not None:
|
|
76
94
|
self.timeout = timeout
|
|
77
95
|
if verbose_fix_output is not None:
|
|
78
96
|
self.options["verbose_fix_output"] = verbose_fix_output
|
|
97
|
+
if line_length is not None:
|
|
98
|
+
if not isinstance(line_length, int):
|
|
99
|
+
raise ValueError("line_length must be an integer")
|
|
100
|
+
if line_length <= 0:
|
|
101
|
+
raise ValueError("line_length must be positive")
|
|
102
|
+
self.options["line_length"] = line_length
|
|
79
103
|
|
|
80
104
|
def _find_config(self) -> str | None:
|
|
81
105
|
"""Locate a Prettier config if none is found by native discovery.
|
|
@@ -90,6 +114,150 @@ class PrettierTool(BaseTool):
|
|
|
90
114
|
"""
|
|
91
115
|
return None
|
|
92
116
|
|
|
117
|
+
def _find_prettier_config(self, search_dir: str | None = None) -> str | None:
|
|
118
|
+
"""Locate prettier config file by walking up the directory tree.
|
|
119
|
+
|
|
120
|
+
Prettier searches upward from the file's directory to find config files,
|
|
121
|
+
so we do the same to match native behavior and ensure config is found
|
|
122
|
+
even when cwd is a subdirectory.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
search_dir: Directory to start searching from. If None, searches from
|
|
126
|
+
current working directory.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
str | None: Path to config file if found, None otherwise.
|
|
130
|
+
"""
|
|
131
|
+
config_paths = [
|
|
132
|
+
".prettierrc",
|
|
133
|
+
".prettierrc.json",
|
|
134
|
+
".prettierrc.js",
|
|
135
|
+
".prettierrc.yaml",
|
|
136
|
+
".prettierrc.yml",
|
|
137
|
+
"prettier.config.js",
|
|
138
|
+
"package.json",
|
|
139
|
+
]
|
|
140
|
+
# Search upward from search_dir (or cwd) to find config, just like prettier does
|
|
141
|
+
start_dir = os.path.abspath(search_dir) if search_dir else os.getcwd()
|
|
142
|
+
current_dir = start_dir
|
|
143
|
+
|
|
144
|
+
# Walk upward from the directory to find config
|
|
145
|
+
# Stop at filesystem root to avoid infinite loop
|
|
146
|
+
while True:
|
|
147
|
+
for config_name in config_paths:
|
|
148
|
+
config_path = os.path.join(current_dir, config_name)
|
|
149
|
+
if os.path.exists(config_path):
|
|
150
|
+
# For package.json, check if it contains prettier config
|
|
151
|
+
if config_name == "package.json":
|
|
152
|
+
try:
|
|
153
|
+
import json
|
|
154
|
+
|
|
155
|
+
with open(config_path, encoding="utf-8") as f:
|
|
156
|
+
pkg_data = json.load(f)
|
|
157
|
+
if "prettier" not in pkg_data:
|
|
158
|
+
continue
|
|
159
|
+
except (
|
|
160
|
+
json.JSONDecodeError,
|
|
161
|
+
FileNotFoundError,
|
|
162
|
+
PermissionError,
|
|
163
|
+
):
|
|
164
|
+
# Skip invalid or unreadable package.json files
|
|
165
|
+
continue
|
|
166
|
+
logger.debug(
|
|
167
|
+
f"[PrettierTool] Found config file: {config_path} "
|
|
168
|
+
f"(searched from {start_dir})",
|
|
169
|
+
)
|
|
170
|
+
return config_path
|
|
171
|
+
|
|
172
|
+
# Move up one directory
|
|
173
|
+
parent_dir = os.path.dirname(current_dir)
|
|
174
|
+
# Stop if we've reached the filesystem root (parent == current)
|
|
175
|
+
if parent_dir == current_dir:
|
|
176
|
+
break
|
|
177
|
+
current_dir = parent_dir
|
|
178
|
+
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
def _find_prettierignore(self, search_dir: str | None = None) -> str | None:
|
|
182
|
+
"""Locate .prettierignore file by walking up the directory tree.
|
|
183
|
+
|
|
184
|
+
Prettier searches upward from the file's directory to find .prettierignore,
|
|
185
|
+
so we do the same to match native behavior and ensure ignore file is found
|
|
186
|
+
even when cwd is a subdirectory.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
search_dir: Directory to start searching from. If None, searches from
|
|
190
|
+
current working directory.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
str | None: Path to .prettierignore file if found, None otherwise.
|
|
194
|
+
"""
|
|
195
|
+
ignore_filename = ".prettierignore"
|
|
196
|
+
# Search upward from search_dir (or cwd) to find ignore file
|
|
197
|
+
start_dir = os.path.abspath(search_dir) if search_dir else os.getcwd()
|
|
198
|
+
current_dir = start_dir
|
|
199
|
+
|
|
200
|
+
# Walk upward from the directory to find ignore file
|
|
201
|
+
# Stop at filesystem root to avoid infinite loop
|
|
202
|
+
while True:
|
|
203
|
+
ignore_path = os.path.join(current_dir, ignore_filename)
|
|
204
|
+
if os.path.exists(ignore_path):
|
|
205
|
+
logger.debug(
|
|
206
|
+
f"[PrettierTool] Found .prettierignore: {ignore_path} "
|
|
207
|
+
f"(searched from {start_dir})",
|
|
208
|
+
)
|
|
209
|
+
return ignore_path
|
|
210
|
+
|
|
211
|
+
# Move up one directory
|
|
212
|
+
parent_dir = os.path.dirname(current_dir)
|
|
213
|
+
# Stop if we've reached the filesystem root (parent == current)
|
|
214
|
+
if parent_dir == current_dir:
|
|
215
|
+
break
|
|
216
|
+
current_dir = parent_dir
|
|
217
|
+
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
def _create_timeout_result(
|
|
221
|
+
self,
|
|
222
|
+
timeout_val: int,
|
|
223
|
+
initial_issues: list | None = None,
|
|
224
|
+
initial_count: int = 0,
|
|
225
|
+
) -> ToolResult:
|
|
226
|
+
"""Create a ToolResult for timeout scenarios.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
timeout_val: The timeout value that was exceeded.
|
|
230
|
+
initial_issues: Optional list of issues found before timeout.
|
|
231
|
+
initial_count: Optional count of initial issues.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
ToolResult: ToolResult instance representing timeout failure.
|
|
235
|
+
"""
|
|
236
|
+
timeout_msg = (
|
|
237
|
+
f"Prettier execution timed out ({timeout_val}s limit exceeded).\n\n"
|
|
238
|
+
"This may indicate:\n"
|
|
239
|
+
" - Large codebase taking too long to process\n"
|
|
240
|
+
" - Need to increase timeout via --tool-options prettier:timeout=N"
|
|
241
|
+
)
|
|
242
|
+
timeout_issue = PrettierIssue(
|
|
243
|
+
file="execution",
|
|
244
|
+
line=None,
|
|
245
|
+
code="TIMEOUT",
|
|
246
|
+
message=timeout_msg,
|
|
247
|
+
column=None,
|
|
248
|
+
)
|
|
249
|
+
combined_issues = (initial_issues or []) + [timeout_issue]
|
|
250
|
+
return ToolResult(
|
|
251
|
+
name=self.name,
|
|
252
|
+
success=False,
|
|
253
|
+
output=timeout_msg,
|
|
254
|
+
issues_count=len(combined_issues),
|
|
255
|
+
issues=combined_issues,
|
|
256
|
+
initial_issues_count=initial_count,
|
|
257
|
+
fixed_issues_count=0,
|
|
258
|
+
remaining_issues_count=len(combined_issues),
|
|
259
|
+
)
|
|
260
|
+
|
|
93
261
|
def check(
|
|
94
262
|
self,
|
|
95
263
|
paths: list[str],
|
|
@@ -102,6 +270,11 @@ class PrettierTool(BaseTool):
|
|
|
102
270
|
Returns:
|
|
103
271
|
ToolResult instance
|
|
104
272
|
"""
|
|
273
|
+
# Check version requirements
|
|
274
|
+
version_result = self._verify_tool_version()
|
|
275
|
+
if version_result is not None:
|
|
276
|
+
return version_result
|
|
277
|
+
|
|
105
278
|
self._validate_paths(paths=paths)
|
|
106
279
|
prettier_files: list[str] = walk_files_with_excludes(
|
|
107
280
|
paths=paths,
|
|
@@ -109,6 +282,17 @@ class PrettierTool(BaseTool):
|
|
|
109
282
|
exclude_patterns=self.exclude_patterns,
|
|
110
283
|
include_venv=self.include_venv,
|
|
111
284
|
)
|
|
285
|
+
logger.debug(
|
|
286
|
+
f"[PrettierTool] Discovered {len(prettier_files)} files matching patterns: "
|
|
287
|
+
f"{self.config.file_patterns}",
|
|
288
|
+
)
|
|
289
|
+
logger.debug(
|
|
290
|
+
f"[PrettierTool] Exclude patterns applied: {self.exclude_patterns}",
|
|
291
|
+
)
|
|
292
|
+
if prettier_files:
|
|
293
|
+
logger.debug(
|
|
294
|
+
f"[PrettierTool] Files to check (first 10): " f"{prettier_files[:10]}",
|
|
295
|
+
)
|
|
112
296
|
if not prettier_files:
|
|
113
297
|
return Tool.to_result(
|
|
114
298
|
name=self.name,
|
|
@@ -118,6 +302,7 @@ class PrettierTool(BaseTool):
|
|
|
118
302
|
)
|
|
119
303
|
# Use relative paths and set cwd to the common parent
|
|
120
304
|
cwd: str = self.get_cwd(paths=prettier_files)
|
|
305
|
+
logger.debug(f"[PrettierTool] Working directory: {cwd}")
|
|
121
306
|
rel_files: list[str] = [
|
|
122
307
|
os.path.relpath(f, cwd) if cwd else f for f in prettier_files
|
|
123
308
|
]
|
|
@@ -125,14 +310,53 @@ class PrettierTool(BaseTool):
|
|
|
125
310
|
cmd: list[str] = self._get_executable_command(tool_name="prettier") + [
|
|
126
311
|
"--check",
|
|
127
312
|
]
|
|
128
|
-
|
|
313
|
+
|
|
314
|
+
# Add Lintro config injection args (--no-config, --config)
|
|
315
|
+
# This takes precedence over native config auto-discovery
|
|
316
|
+
config_args = self._build_config_args()
|
|
317
|
+
if config_args:
|
|
318
|
+
cmd.extend(config_args)
|
|
319
|
+
logger.debug(
|
|
320
|
+
"[PrettierTool] Using Lintro config injection",
|
|
321
|
+
)
|
|
322
|
+
else:
|
|
323
|
+
# Fallback: Find config and ignore files by walking up from cwd
|
|
324
|
+
found_config = self._find_prettier_config(search_dir=cwd)
|
|
325
|
+
if found_config:
|
|
326
|
+
logger.debug(
|
|
327
|
+
f"[PrettierTool] Found config: {found_config} (auto-detecting)",
|
|
328
|
+
)
|
|
329
|
+
else:
|
|
330
|
+
logger.debug(
|
|
331
|
+
"[PrettierTool] No prettier config file found (using defaults)",
|
|
332
|
+
)
|
|
333
|
+
# Apply line_length as --print-width if set and no config found
|
|
334
|
+
line_length = self.options.get("line_length")
|
|
335
|
+
if line_length:
|
|
336
|
+
cmd.extend(["--print-width", str(line_length)])
|
|
337
|
+
logger.debug(
|
|
338
|
+
"[PrettierTool] Using --print-width=%s from options",
|
|
339
|
+
line_length,
|
|
340
|
+
)
|
|
341
|
+
# Find .prettierignore by walking up from cwd
|
|
342
|
+
prettierignore_path = self._find_prettierignore(search_dir=cwd)
|
|
343
|
+
if prettierignore_path:
|
|
344
|
+
logger.debug(
|
|
345
|
+
f"[PrettierTool] Found .prettierignore: {prettierignore_path} "
|
|
346
|
+
"(auto-detecting)",
|
|
347
|
+
)
|
|
348
|
+
|
|
129
349
|
cmd.extend(rel_files)
|
|
130
350
|
logger.debug(f"[PrettierTool] Running: {' '.join(cmd)} (cwd={cwd})")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
351
|
+
timeout_val: int = self.options.get("timeout", self._default_timeout)
|
|
352
|
+
try:
|
|
353
|
+
result = self._run_subprocess(
|
|
354
|
+
cmd=cmd,
|
|
355
|
+
timeout=timeout_val,
|
|
356
|
+
cwd=cwd,
|
|
357
|
+
)
|
|
358
|
+
except subprocess.TimeoutExpired:
|
|
359
|
+
return self._create_timeout_result(timeout_val=timeout_val)
|
|
136
360
|
output: str = result[1]
|
|
137
361
|
# Do not filter lines post-hoc; rely on discovery and ignore files
|
|
138
362
|
issues: list = parse_prettier_output(output=output)
|
|
@@ -164,6 +388,11 @@ class PrettierTool(BaseTool):
|
|
|
164
388
|
Returns:
|
|
165
389
|
ToolResult: Result object with counts and messages.
|
|
166
390
|
"""
|
|
391
|
+
# Check version requirements
|
|
392
|
+
version_result = self._verify_tool_version()
|
|
393
|
+
if version_result is not None:
|
|
394
|
+
return version_result
|
|
395
|
+
|
|
167
396
|
self._validate_paths(paths=paths)
|
|
168
397
|
prettier_files: list[str] = walk_files_with_excludes(
|
|
169
398
|
paths=paths,
|
|
@@ -185,18 +414,55 @@ class PrettierTool(BaseTool):
|
|
|
185
414
|
os.path.relpath(f, cwd) if cwd else f for f in prettier_files
|
|
186
415
|
]
|
|
187
416
|
|
|
417
|
+
# Get Lintro config injection args (--no-config, --config)
|
|
418
|
+
config_args = self._build_config_args()
|
|
419
|
+
fallback_args: list[str] = []
|
|
420
|
+
if not config_args:
|
|
421
|
+
# Fallback: Find config and ignore files by walking up from cwd
|
|
422
|
+
found_config = self._find_prettier_config(search_dir=cwd)
|
|
423
|
+
if found_config:
|
|
424
|
+
logger.debug(
|
|
425
|
+
f"[PrettierTool] Found config: {found_config} (auto-detecting)",
|
|
426
|
+
)
|
|
427
|
+
else:
|
|
428
|
+
logger.debug(
|
|
429
|
+
"[PrettierTool] No prettier config file found (using defaults)",
|
|
430
|
+
)
|
|
431
|
+
# Apply line_length as --print-width if set and no config found
|
|
432
|
+
line_length = self.options.get("line_length")
|
|
433
|
+
if line_length:
|
|
434
|
+
fallback_args.extend(["--print-width", str(line_length)])
|
|
435
|
+
logger.debug(
|
|
436
|
+
"[PrettierTool] Using --print-width=%s from options",
|
|
437
|
+
line_length,
|
|
438
|
+
)
|
|
439
|
+
prettierignore_path = self._find_prettierignore(search_dir=cwd)
|
|
440
|
+
if prettierignore_path:
|
|
441
|
+
logger.debug(
|
|
442
|
+
f"[PrettierTool] Found .prettierignore: {prettierignore_path} "
|
|
443
|
+
"(auto-detecting)",
|
|
444
|
+
)
|
|
445
|
+
|
|
188
446
|
# Check for issues first
|
|
189
447
|
check_cmd: list[str] = self._get_executable_command(tool_name="prettier") + [
|
|
190
448
|
"--check",
|
|
191
449
|
]
|
|
192
|
-
#
|
|
450
|
+
# Add Lintro config injection if available, otherwise use fallback args
|
|
451
|
+
if config_args:
|
|
452
|
+
check_cmd.extend(config_args)
|
|
453
|
+
elif fallback_args:
|
|
454
|
+
check_cmd.extend(fallback_args)
|
|
193
455
|
check_cmd.extend(rel_files)
|
|
194
456
|
logger.debug(f"[PrettierTool] Checking: {' '.join(check_cmd)} (cwd={cwd})")
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
457
|
+
timeout_val: int = self.options.get("timeout", self._default_timeout)
|
|
458
|
+
try:
|
|
459
|
+
check_result = self._run_subprocess(
|
|
460
|
+
cmd=check_cmd,
|
|
461
|
+
timeout=timeout_val,
|
|
462
|
+
cwd=cwd,
|
|
463
|
+
)
|
|
464
|
+
except subprocess.TimeoutExpired:
|
|
465
|
+
return self._create_timeout_result(timeout_val=timeout_val)
|
|
200
466
|
check_output: str = check_result[1]
|
|
201
467
|
|
|
202
468
|
# Parse initial issues
|
|
@@ -207,21 +473,40 @@ class PrettierTool(BaseTool):
|
|
|
207
473
|
fix_cmd: list[str] = self._get_executable_command(tool_name="prettier") + [
|
|
208
474
|
"--write",
|
|
209
475
|
]
|
|
476
|
+
# Add Lintro config injection if available, otherwise use fallback args
|
|
477
|
+
if config_args:
|
|
478
|
+
fix_cmd.extend(config_args)
|
|
479
|
+
elif fallback_args:
|
|
480
|
+
fix_cmd.extend(fallback_args)
|
|
210
481
|
fix_cmd.extend(rel_files)
|
|
211
482
|
logger.debug(f"[PrettierTool] Fixing: {' '.join(fix_cmd)} (cwd={cwd})")
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
483
|
+
try:
|
|
484
|
+
fix_result = self._run_subprocess(
|
|
485
|
+
cmd=fix_cmd,
|
|
486
|
+
timeout=timeout_val,
|
|
487
|
+
cwd=cwd,
|
|
488
|
+
)
|
|
489
|
+
except subprocess.TimeoutExpired:
|
|
490
|
+
return self._create_timeout_result(
|
|
491
|
+
timeout_val=timeout_val,
|
|
492
|
+
initial_issues=initial_issues,
|
|
493
|
+
initial_count=initial_count,
|
|
494
|
+
)
|
|
217
495
|
fix_output: str = fix_result[1]
|
|
218
496
|
|
|
219
497
|
# Check for remaining issues after fixing
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
498
|
+
try:
|
|
499
|
+
final_check_result = self._run_subprocess(
|
|
500
|
+
cmd=check_cmd,
|
|
501
|
+
timeout=timeout_val,
|
|
502
|
+
cwd=cwd,
|
|
503
|
+
)
|
|
504
|
+
except subprocess.TimeoutExpired:
|
|
505
|
+
return self._create_timeout_result(
|
|
506
|
+
timeout_val=timeout_val,
|
|
507
|
+
initial_issues=initial_issues,
|
|
508
|
+
initial_count=initial_count,
|
|
509
|
+
)
|
|
225
510
|
final_check_output: str = final_check_result[1]
|
|
226
511
|
remaining_issues: list = parse_prettier_output(output=final_check_output)
|
|
227
512
|
remaining_count: int = len(remaining_issues)
|
|
@@ -261,15 +546,17 @@ class PrettierTool(BaseTool):
|
|
|
261
546
|
# Success means no remaining issues
|
|
262
547
|
success: bool = remaining_count == 0
|
|
263
548
|
|
|
549
|
+
# Combine initial and remaining issues so formatter can split them by fixability
|
|
550
|
+
all_issues = (initial_issues or []) + (remaining_issues or [])
|
|
551
|
+
|
|
264
552
|
return ToolResult(
|
|
265
553
|
name=self.name,
|
|
266
554
|
success=success,
|
|
267
555
|
output=final_output,
|
|
268
556
|
# For fix operations, issues_count represents remaining for summaries
|
|
269
557
|
issues_count=remaining_count,
|
|
270
|
-
# Provide
|
|
271
|
-
|
|
272
|
-
issues=(initial_issues if initial_issues else remaining_issues),
|
|
558
|
+
# Provide both initial (fixed) and remaining issues for display
|
|
559
|
+
issues=all_issues,
|
|
273
560
|
initial_issues_count=initial_count,
|
|
274
561
|
fixed_issues_count=fixed_count,
|
|
275
562
|
remaining_issues_count=remaining_count,
|