auto-coder 0.1.316__py3-none-any.whl → 0.1.318__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.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/METADATA +2 -2
- {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/RECORD +41 -20
- autocoder/auto_coder_runner.py +1 -2
- autocoder/common/__init__.py +3 -0
- autocoder/common/auto_coder_lang.py +24 -0
- autocoder/common/code_auto_merge_editblock.py +2 -42
- autocoder/common/git_utils.py +2 -2
- autocoder/common/token_cost_caculate.py +103 -42
- autocoder/common/v2/__init__.py +0 -0
- autocoder/common/v2/code_auto_generate.py +199 -0
- autocoder/common/v2/code_auto_generate_diff.py +361 -0
- autocoder/common/v2/code_auto_generate_editblock.py +380 -0
- autocoder/common/v2/code_auto_generate_strict_diff.py +269 -0
- autocoder/common/v2/code_auto_merge.py +211 -0
- autocoder/common/v2/code_auto_merge_diff.py +354 -0
- autocoder/common/v2/code_auto_merge_editblock.py +523 -0
- autocoder/common/v2/code_auto_merge_strict_diff.py +259 -0
- autocoder/common/v2/code_diff_manager.py +266 -0
- autocoder/common/v2/code_editblock_manager.py +282 -0
- autocoder/common/v2/code_manager.py +238 -0
- autocoder/common/v2/code_strict_diff_manager.py +241 -0
- autocoder/dispacher/actions/action.py +16 -0
- autocoder/dispacher/actions/plugins/action_regex_project.py +6 -0
- autocoder/events/event_manager_singleton.py +2 -2
- autocoder/helper/__init__.py +0 -0
- autocoder/helper/project_creator.py +570 -0
- autocoder/linters/linter_factory.py +44 -25
- autocoder/linters/models.py +220 -0
- autocoder/linters/python_linter.py +1 -7
- autocoder/linters/reactjs_linter.py +580 -0
- autocoder/linters/shadow_linter.py +390 -0
- autocoder/linters/vue_linter.py +576 -0
- autocoder/memory/active_context_manager.py +0 -4
- autocoder/memory/active_package.py +12 -12
- autocoder/shadows/__init__.py +0 -0
- autocoder/shadows/shadow_manager.py +235 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for linting Vue projects.
|
|
3
|
+
This module provides functionality to analyze Vue projects for code quality and best practices.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
import subprocess
|
|
9
|
+
from typing import Dict, List, Optional, Any
|
|
10
|
+
|
|
11
|
+
from autocoder.linters.base_linter import BaseLinter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class VueLinter(BaseLinter):
|
|
15
|
+
"""
|
|
16
|
+
A class that provides linting functionality for Vue projects and single files.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, verbose: bool = False):
|
|
20
|
+
"""
|
|
21
|
+
Initialize the VueLinter.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
verbose (bool): Whether to display verbose output.
|
|
25
|
+
"""
|
|
26
|
+
super().__init__(verbose)
|
|
27
|
+
|
|
28
|
+
def _check_dependencies(self) -> bool:
|
|
29
|
+
"""
|
|
30
|
+
Check if required dependencies (node, npm, npx) are installed.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
bool: True if all dependencies are available, False otherwise.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
# Check if node and npm are installed
|
|
37
|
+
subprocess.run(["node", "--version"], check=True,
|
|
38
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
39
|
+
subprocess.run(["npm", "--version"], check=True,
|
|
40
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
41
|
+
subprocess.run(["npx", "--version"], check=True,
|
|
42
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
43
|
+
return True
|
|
44
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
def get_supported_extensions(self) -> List[str]:
|
|
48
|
+
"""
|
|
49
|
+
Get the list of file extensions supported by this linter.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
List[str]: List of supported file extensions.
|
|
53
|
+
"""
|
|
54
|
+
return ['.vue', '.js', '.ts']
|
|
55
|
+
|
|
56
|
+
def _detect_project_type(self, project_path: str) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Detect whether the project is Vue.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
project_path (str): Path to the project directory.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
bool: True if it's a Vue project, False otherwise
|
|
65
|
+
"""
|
|
66
|
+
# Check for package.json
|
|
67
|
+
package_json_path = os.path.join(project_path, 'package.json')
|
|
68
|
+
if not os.path.exists(package_json_path):
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
with open(package_json_path, 'r') as f:
|
|
73
|
+
package_data = json.load(f)
|
|
74
|
+
|
|
75
|
+
dependencies = {
|
|
76
|
+
**package_data.get('dependencies', {}),
|
|
77
|
+
**package_data.get('devDependencies', {})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Check for Vue
|
|
81
|
+
return 'vue' in dependencies
|
|
82
|
+
except (json.JSONDecodeError, FileNotFoundError):
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
def _detect_file_type(self, file_path: str) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Detect if the file is a Vue file.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
file_path (str): Path to the file.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
bool: True if it's a Vue file, False otherwise
|
|
94
|
+
"""
|
|
95
|
+
if not os.path.exists(file_path) or not os.path.isfile(file_path):
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
# Get file extension
|
|
99
|
+
ext = self.get_file_extension(file_path)
|
|
100
|
+
|
|
101
|
+
if ext == '.vue':
|
|
102
|
+
return True
|
|
103
|
+
elif ext == '.js' or ext == '.ts':
|
|
104
|
+
# Check content for Vue imports
|
|
105
|
+
try:
|
|
106
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
107
|
+
content = f.read()
|
|
108
|
+
if 'import Vue' in content or 'from "vue"' in content or "from 'vue'" in content:
|
|
109
|
+
return True
|
|
110
|
+
except:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
def _install_eslint_if_needed(self, project_path: str) -> bool:
|
|
116
|
+
"""
|
|
117
|
+
Install ESLint and the appropriate Vue plugins if they're not already installed.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
project_path (str): Path to the project directory.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
bool: True if installation was successful, False otherwise.
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
# 首先尝试运行 npx eslint --version 检查是否已安装
|
|
127
|
+
if self.verbose:
|
|
128
|
+
print("Checking if ESLint is already installed...")
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
result = subprocess.run(
|
|
132
|
+
["npx", "eslint", "--version"],
|
|
133
|
+
cwd=project_path,
|
|
134
|
+
stdout=subprocess.PIPE,
|
|
135
|
+
stderr=subprocess.PIPE,
|
|
136
|
+
text=True,
|
|
137
|
+
check=False
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if result.returncode == 0:
|
|
141
|
+
if self.verbose:
|
|
142
|
+
print(f"ESLint is already installed: {result.stdout.strip()}")
|
|
143
|
+
return True
|
|
144
|
+
except subprocess.SubprocessError:
|
|
145
|
+
if self.verbose:
|
|
146
|
+
print("ESLint not found via npx, will proceed with installation")
|
|
147
|
+
|
|
148
|
+
# 检查 .eslintrc.* 配置文件是否存在
|
|
149
|
+
eslint_configs = [
|
|
150
|
+
os.path.join(project_path, '.eslintrc'),
|
|
151
|
+
os.path.join(project_path, '.eslintrc.js'),
|
|
152
|
+
os.path.join(project_path, '.eslintrc.json'),
|
|
153
|
+
os.path.join(project_path, '.eslintrc.yml'),
|
|
154
|
+
os.path.join(project_path, '.eslintrc.yaml')
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
if any(os.path.exists(config) for config in eslint_configs):
|
|
158
|
+
if self.verbose:
|
|
159
|
+
print("ESLint configuration found.")
|
|
160
|
+
return True
|
|
161
|
+
|
|
162
|
+
# Install eslint and Vue plugins
|
|
163
|
+
cmd = ["npm", "install", "--save-dev", "eslint", "eslint-plugin-vue"]
|
|
164
|
+
if self.verbose:
|
|
165
|
+
print("Installing ESLint with Vue plugins...")
|
|
166
|
+
|
|
167
|
+
result = subprocess.run(
|
|
168
|
+
cmd,
|
|
169
|
+
cwd=project_path,
|
|
170
|
+
stdout=subprocess.PIPE if not self.verbose else None,
|
|
171
|
+
stderr=subprocess.PIPE if not self.verbose else None
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Create basic configuration
|
|
175
|
+
eslint_config = {
|
|
176
|
+
"env": {
|
|
177
|
+
"browser": True,
|
|
178
|
+
"es2021": True,
|
|
179
|
+
"node": True
|
|
180
|
+
},
|
|
181
|
+
"extends": ["eslint:recommended", "plugin:vue/vue3-recommended"],
|
|
182
|
+
"plugins": ["vue"],
|
|
183
|
+
"parserOptions": {
|
|
184
|
+
"ecmaVersion": 2021,
|
|
185
|
+
"sourceType": "module"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Write configuration
|
|
190
|
+
with open(os.path.join(project_path, '.eslintrc.json'), 'w') as f:
|
|
191
|
+
json.dump(eslint_config, f, indent=2)
|
|
192
|
+
|
|
193
|
+
return result.returncode == 0
|
|
194
|
+
except subprocess.SubprocessError:
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
def _extract_json_from_output(self, output_text: str) -> str:
|
|
198
|
+
"""
|
|
199
|
+
Extract JSON string from output text that might contain non-JSON content at the beginning.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
output_text (str): The output text that may contain a JSON string after a separator.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
str: The extracted JSON string, or the original text if no separator is found.
|
|
206
|
+
"""
|
|
207
|
+
if "=============" in output_text:
|
|
208
|
+
lines = output_text.split('\n')
|
|
209
|
+
json_lines = []
|
|
210
|
+
found_separator = False
|
|
211
|
+
|
|
212
|
+
for line in lines:
|
|
213
|
+
if line.startswith("============="):
|
|
214
|
+
found_separator = True
|
|
215
|
+
continue
|
|
216
|
+
if found_separator:
|
|
217
|
+
json_lines.append(line)
|
|
218
|
+
|
|
219
|
+
if json_lines:
|
|
220
|
+
return '\n'.join(json_lines)
|
|
221
|
+
|
|
222
|
+
return output_text
|
|
223
|
+
|
|
224
|
+
def lint_file(self, file_path: str, fix: bool = False, project_path: str = None) -> Dict[str, Any]:
|
|
225
|
+
"""
|
|
226
|
+
Lint a single Vue file using ESLint.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
file_path (str): Path to the file to lint.
|
|
230
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
231
|
+
project_path (str, optional): Path to the project directory. If not provided,
|
|
232
|
+
the parent directory of the file will be used.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Dict: A dictionary containing lint results.
|
|
236
|
+
"""
|
|
237
|
+
result = {
|
|
238
|
+
'success': False,
|
|
239
|
+
'framework': 'vue',
|
|
240
|
+
'files_analyzed': 0,
|
|
241
|
+
'error_count': 0,
|
|
242
|
+
'warning_count': 0,
|
|
243
|
+
'issues': []
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# Check if file exists
|
|
247
|
+
if not os.path.exists(file_path) or not os.path.isfile(file_path):
|
|
248
|
+
result['error'] = f"File '{file_path}' does not exist or is not a file"
|
|
249
|
+
return result
|
|
250
|
+
|
|
251
|
+
# Check if file is supported
|
|
252
|
+
if not self.is_supported_file(file_path):
|
|
253
|
+
result['error'] = f"Unsupported file type for '{file_path}'"
|
|
254
|
+
return result
|
|
255
|
+
|
|
256
|
+
# Check if it's a Vue file
|
|
257
|
+
if not self._detect_file_type(file_path):
|
|
258
|
+
result['error'] = f"Not a Vue file: '{file_path}'"
|
|
259
|
+
return result
|
|
260
|
+
|
|
261
|
+
# Check dependencies
|
|
262
|
+
if not self._check_dependencies():
|
|
263
|
+
result['error'] = "Required dependencies (node, npm, npx) are not installed"
|
|
264
|
+
return result
|
|
265
|
+
|
|
266
|
+
# If project_path is not provided, use parent directory
|
|
267
|
+
if project_path is None:
|
|
268
|
+
project_path = os.path.dirname(file_path)
|
|
269
|
+
|
|
270
|
+
# Install ESLint if needed
|
|
271
|
+
if not self._install_eslint_if_needed(project_path):
|
|
272
|
+
result['error'] = "Failed to install or configure ESLint"
|
|
273
|
+
return result
|
|
274
|
+
|
|
275
|
+
# Run ESLint on the file
|
|
276
|
+
try:
|
|
277
|
+
cmd = ["npx", "eslint", "--format", "json"]
|
|
278
|
+
|
|
279
|
+
# Add fix flag if requested
|
|
280
|
+
if fix:
|
|
281
|
+
cmd.append("--fix")
|
|
282
|
+
|
|
283
|
+
# Add file path
|
|
284
|
+
cmd.append(file_path)
|
|
285
|
+
|
|
286
|
+
process = subprocess.run(
|
|
287
|
+
cmd,
|
|
288
|
+
cwd=project_path,
|
|
289
|
+
stdout=subprocess.PIPE,
|
|
290
|
+
stderr=subprocess.PIPE,
|
|
291
|
+
text=True
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Parse ESLint output
|
|
295
|
+
if process.stdout:
|
|
296
|
+
try:
|
|
297
|
+
output_text = process.stdout
|
|
298
|
+
try:
|
|
299
|
+
eslint_output = json.loads(output_text)
|
|
300
|
+
except json.JSONDecodeError:
|
|
301
|
+
# Try to extract JSON from output if it contains separator
|
|
302
|
+
json_text = self._extract_json_from_output(output_text)
|
|
303
|
+
eslint_output = json.loads(json_text)
|
|
304
|
+
|
|
305
|
+
# print(f"eslint_output: {json.dumps(eslint_output, indent=4,ensure_ascii=False)}")
|
|
306
|
+
|
|
307
|
+
# Count files analyzed (should be 1)
|
|
308
|
+
result['files_analyzed'] = len(eslint_output)
|
|
309
|
+
|
|
310
|
+
# Track overall counts
|
|
311
|
+
total_errors = 0
|
|
312
|
+
total_warnings = 0
|
|
313
|
+
|
|
314
|
+
# Process the file result
|
|
315
|
+
for file_result in eslint_output:
|
|
316
|
+
file_rel_path = os.path.relpath(
|
|
317
|
+
file_result['filePath'], project_path)
|
|
318
|
+
|
|
319
|
+
# Add error and warning counts
|
|
320
|
+
total_errors += file_result.get('errorCount', 0)
|
|
321
|
+
total_warnings += file_result.get('warningCount', 0)
|
|
322
|
+
|
|
323
|
+
# Process individual messages
|
|
324
|
+
for message in file_result.get('messages', []):
|
|
325
|
+
issue = {
|
|
326
|
+
'file': file_rel_path,
|
|
327
|
+
'line': message.get('line', 0),
|
|
328
|
+
'column': message.get('column', 0),
|
|
329
|
+
'severity': 'error' if message.get('severity', 1) == 2 else 'warning',
|
|
330
|
+
'message': message.get('message', ''),
|
|
331
|
+
'rule': message.get('ruleId', 'unknown')
|
|
332
|
+
}
|
|
333
|
+
result['issues'].append(issue)
|
|
334
|
+
|
|
335
|
+
result['error_count'] = total_errors
|
|
336
|
+
result['warning_count'] = total_warnings
|
|
337
|
+
result['success'] = True
|
|
338
|
+
except json.JSONDecodeError:
|
|
339
|
+
# Handle case where ESLint output is not valid JSON
|
|
340
|
+
result['error'] = "Failed to parse ESLint output"
|
|
341
|
+
else:
|
|
342
|
+
# Handle case where ESLint didn't produce any output
|
|
343
|
+
stderr = process.stderr.strip()
|
|
344
|
+
if stderr:
|
|
345
|
+
result['error'] = f"ESLint error: {stderr}"
|
|
346
|
+
else:
|
|
347
|
+
# No errors found
|
|
348
|
+
result['success'] = True
|
|
349
|
+
except subprocess.SubprocessError as e:
|
|
350
|
+
result['error'] = f"Error running ESLint: {str(e)}"
|
|
351
|
+
|
|
352
|
+
return result
|
|
353
|
+
|
|
354
|
+
def lint_project(self, project_path: str, fix: bool = False) -> Dict[str, Any]:
|
|
355
|
+
"""
|
|
356
|
+
Lint a Vue project.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
project_path (str): Path to the project directory.
|
|
360
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
Dict: A dictionary containing lint results.
|
|
364
|
+
"""
|
|
365
|
+
result = {
|
|
366
|
+
'success': False,
|
|
367
|
+
'framework': 'vue',
|
|
368
|
+
'files_analyzed': 0,
|
|
369
|
+
'error_count': 0,
|
|
370
|
+
'warning_count': 0,
|
|
371
|
+
'issues': []
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
# Check if the path exists
|
|
375
|
+
if not os.path.exists(project_path) or not os.path.isdir(project_path):
|
|
376
|
+
result['error'] = f"Path '{project_path}' does not exist or is not a directory"
|
|
377
|
+
return result
|
|
378
|
+
|
|
379
|
+
# Check dependencies
|
|
380
|
+
if not self._check_dependencies():
|
|
381
|
+
result['error'] = "Required dependencies (node, npm, npx) are not installed"
|
|
382
|
+
return result
|
|
383
|
+
|
|
384
|
+
# Detect project type
|
|
385
|
+
if not self._detect_project_type(project_path):
|
|
386
|
+
result['error'] = "Not a Vue project"
|
|
387
|
+
return result
|
|
388
|
+
|
|
389
|
+
# Install ESLint if needed
|
|
390
|
+
if not self._install_eslint_if_needed(project_path):
|
|
391
|
+
result['error'] = "Failed to install or configure ESLint"
|
|
392
|
+
return result
|
|
393
|
+
|
|
394
|
+
# Run ESLint
|
|
395
|
+
try:
|
|
396
|
+
cmd = ["npx", "eslint", "--format", "json"]
|
|
397
|
+
|
|
398
|
+
# Add fix flag if requested
|
|
399
|
+
if fix:
|
|
400
|
+
cmd.append("--fix")
|
|
401
|
+
|
|
402
|
+
# Target Vue files
|
|
403
|
+
cmd.extend([
|
|
404
|
+
"src/**/*.js",
|
|
405
|
+
"src/**/*.vue",
|
|
406
|
+
"src/**/*.ts"
|
|
407
|
+
])
|
|
408
|
+
|
|
409
|
+
process = subprocess.run(
|
|
410
|
+
cmd,
|
|
411
|
+
cwd=project_path,
|
|
412
|
+
stdout=subprocess.PIPE,
|
|
413
|
+
stderr=subprocess.PIPE,
|
|
414
|
+
text=True
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
# Parse ESLint output
|
|
418
|
+
if process.stdout:
|
|
419
|
+
try:
|
|
420
|
+
try:
|
|
421
|
+
eslint_output = json.loads(process.stdout)
|
|
422
|
+
except json.JSONDecodeError:
|
|
423
|
+
# Try to extract JSON from output if it contains separator
|
|
424
|
+
json_text = self._extract_json_from_output(process.stdout)
|
|
425
|
+
eslint_output = json.loads(json_text)
|
|
426
|
+
|
|
427
|
+
# Count files analyzed
|
|
428
|
+
result['files_analyzed'] = len(eslint_output)
|
|
429
|
+
|
|
430
|
+
# Track overall counts
|
|
431
|
+
total_errors = 0
|
|
432
|
+
total_warnings = 0
|
|
433
|
+
|
|
434
|
+
# Process each file result
|
|
435
|
+
for file_result in eslint_output:
|
|
436
|
+
file_path = os.path.relpath(
|
|
437
|
+
file_result['filePath'], project_path)
|
|
438
|
+
|
|
439
|
+
# Add error and warning counts
|
|
440
|
+
total_errors += file_result.get('errorCount', 0)
|
|
441
|
+
total_warnings += file_result.get('warningCount', 0)
|
|
442
|
+
|
|
443
|
+
# Process individual messages
|
|
444
|
+
for message in file_result.get('messages', []):
|
|
445
|
+
issue = {
|
|
446
|
+
'file': file_path,
|
|
447
|
+
'line': message.get('line', 0),
|
|
448
|
+
'column': message.get('column', 0),
|
|
449
|
+
'severity': 'error' if message.get('severity', 1) == 2 else 'warning',
|
|
450
|
+
'message': message.get('message', ''),
|
|
451
|
+
'rule': message.get('ruleId', 'unknown')
|
|
452
|
+
}
|
|
453
|
+
result['issues'].append(issue)
|
|
454
|
+
|
|
455
|
+
result['error_count'] = total_errors
|
|
456
|
+
result['warning_count'] = total_warnings
|
|
457
|
+
result['success'] = True
|
|
458
|
+
except json.JSONDecodeError:
|
|
459
|
+
# Handle case where ESLint output is not valid JSON
|
|
460
|
+
result['error'] = "Failed to parse ESLint output"
|
|
461
|
+
else:
|
|
462
|
+
# Handle case where ESLint didn't produce any output
|
|
463
|
+
stderr = process.stderr.strip()
|
|
464
|
+
if stderr:
|
|
465
|
+
result['error'] = f"ESLint error: {stderr}"
|
|
466
|
+
else:
|
|
467
|
+
# No errors found
|
|
468
|
+
result['success'] = True
|
|
469
|
+
except subprocess.SubprocessError as e:
|
|
470
|
+
result['error'] = f"Error running ESLint: {str(e)}"
|
|
471
|
+
|
|
472
|
+
return result
|
|
473
|
+
|
|
474
|
+
def format_lint_result(self, lint_result: Dict[str, Any]) -> str:
|
|
475
|
+
"""
|
|
476
|
+
Format lint results into a human-readable string.
|
|
477
|
+
|
|
478
|
+
Args:
|
|
479
|
+
lint_result (Dict): The lint result dictionary.
|
|
480
|
+
|
|
481
|
+
Returns:
|
|
482
|
+
str: A formatted string representation of the lint results.
|
|
483
|
+
"""
|
|
484
|
+
if not lint_result.get('success', False):
|
|
485
|
+
return f"Linting failed: {lint_result.get('error', 'Unknown error')}"
|
|
486
|
+
|
|
487
|
+
files_analyzed = lint_result.get('files_analyzed', 0)
|
|
488
|
+
error_count = lint_result.get('error_count', 0)
|
|
489
|
+
warning_count = lint_result.get('warning_count', 0)
|
|
490
|
+
|
|
491
|
+
output = [
|
|
492
|
+
"Vue Lint Results",
|
|
493
|
+
f"{'=' * 30}",
|
|
494
|
+
f"Files analyzed: {files_analyzed}",
|
|
495
|
+
f"Errors: {error_count}",
|
|
496
|
+
f"Warnings: {warning_count}",
|
|
497
|
+
""
|
|
498
|
+
]
|
|
499
|
+
|
|
500
|
+
if error_count == 0 and warning_count == 0:
|
|
501
|
+
output.append("No issues found. Great job!")
|
|
502
|
+
else:
|
|
503
|
+
output.append("Issues:")
|
|
504
|
+
output.append(f"{'-' * 30}")
|
|
505
|
+
|
|
506
|
+
# Group issues by file
|
|
507
|
+
issues_by_file = {}
|
|
508
|
+
for issue in lint_result.get('issues', []):
|
|
509
|
+
file_path = issue.get('file', '')
|
|
510
|
+
if file_path not in issues_by_file:
|
|
511
|
+
issues_by_file[file_path] = []
|
|
512
|
+
issues_by_file[file_path].append(issue)
|
|
513
|
+
|
|
514
|
+
# Display issues grouped by file
|
|
515
|
+
for file_path, issues in issues_by_file.items():
|
|
516
|
+
output.append(f"\nFile: {file_path}")
|
|
517
|
+
|
|
518
|
+
for issue in issues:
|
|
519
|
+
severity = issue.get('severity', '').upper()
|
|
520
|
+
line = issue.get('line', 0)
|
|
521
|
+
column = issue.get('column', 0)
|
|
522
|
+
message = issue.get('message', '')
|
|
523
|
+
rule = issue.get('rule', 'unknown')
|
|
524
|
+
|
|
525
|
+
output.append(
|
|
526
|
+
f" [{severity}] Line {line}, Col {column}: {message} ({rule})")
|
|
527
|
+
|
|
528
|
+
return "\n".join(output)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def lint_project(project_path: str, fix: bool = False, verbose: bool = False) -> Dict[str, Any]:
|
|
532
|
+
"""
|
|
533
|
+
Utility function to lint a Vue project.
|
|
534
|
+
|
|
535
|
+
Args:
|
|
536
|
+
project_path (str): Path to the project directory.
|
|
537
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
538
|
+
verbose (bool): Whether to display verbose output.
|
|
539
|
+
|
|
540
|
+
Returns:
|
|
541
|
+
Dict: A dictionary containing lint results.
|
|
542
|
+
"""
|
|
543
|
+
linter = VueLinter(verbose=verbose)
|
|
544
|
+
return linter.lint_project(project_path, fix=fix)
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def lint_file(file_path: str, project_path: str = None, fix: bool = False, verbose: bool = False) -> Dict[str, Any]:
|
|
548
|
+
"""
|
|
549
|
+
Utility function to lint a single Vue file.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
file_path (str): Path to the file to lint.
|
|
553
|
+
project_path (str, optional): Path to the project directory. If not provided,
|
|
554
|
+
the parent directory of the file will be used.
|
|
555
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
556
|
+
verbose (bool): Whether to display verbose output.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
Dict: A dictionary containing lint results.
|
|
560
|
+
"""
|
|
561
|
+
linter = VueLinter(verbose=verbose)
|
|
562
|
+
return linter.lint_file(file_path, fix=fix, project_path=project_path)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def format_lint_result(lint_result: Dict[str, Any]) -> str:
|
|
566
|
+
"""
|
|
567
|
+
Format lint results into a human-readable string.
|
|
568
|
+
|
|
569
|
+
Args:
|
|
570
|
+
lint_result (Dict): The lint result dictionary.
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
str: A formatted string representation of the lint results.
|
|
574
|
+
"""
|
|
575
|
+
linter = VueLinter()
|
|
576
|
+
return linter.format_lint_result(lint_result)
|
|
@@ -33,9 +33,7 @@ class ActivePackage:
|
|
|
33
33
|
self.llm = llm
|
|
34
34
|
self.product_mode = product_mode
|
|
35
35
|
# 创建专用的 logger 实例
|
|
36
|
-
self.logger = global_logger.bind(name="ActivePackage")
|
|
37
|
-
# 创建Token计费计算器
|
|
38
|
-
self.token_calculator = TokenCostCalculator(logger_name="ActivePackage.TokenCost")
|
|
36
|
+
self.logger = global_logger.bind(name="ActivePackage")
|
|
39
37
|
|
|
40
38
|
def generate_active_file(self, context: Dict[str, Any], query: str,
|
|
41
39
|
existing_file_path: Optional[str] = None,
|
|
@@ -79,7 +77,7 @@ class ActivePackage:
|
|
|
79
77
|
# 根据是否有现有内容选择不同的生成方式
|
|
80
78
|
if existing_content:
|
|
81
79
|
# 有现有内容,使用更新模式
|
|
82
|
-
file_content, usage_stats = self.generate_updated_active_file(enhanced_context, query, existing_content)
|
|
80
|
+
file_content, usage_stats = self.generate_updated_active_file(enhanced_context, query, existing_content,args)
|
|
83
81
|
# 合并token和费用统计
|
|
84
82
|
total_stats["total_tokens"] += usage_stats["total_tokens"]
|
|
85
83
|
total_stats["input_tokens"] += usage_stats["input_tokens"]
|
|
@@ -87,7 +85,7 @@ class ActivePackage:
|
|
|
87
85
|
total_stats["cost"] += usage_stats["cost"]
|
|
88
86
|
else:
|
|
89
87
|
# 无现有内容,使用创建模式
|
|
90
|
-
file_content, usage_stats = self.generate_new_active_file(enhanced_context, query)
|
|
88
|
+
file_content, usage_stats = self.generate_new_active_file(enhanced_context, query,args)
|
|
91
89
|
# 合并token和费用统计
|
|
92
90
|
total_stats["total_tokens"] += usage_stats["total_tokens"]
|
|
93
91
|
total_stats["input_tokens"] += usage_stats["input_tokens"]
|
|
@@ -174,7 +172,7 @@ class ActivePackage:
|
|
|
174
172
|
|
|
175
173
|
return enhanced_context
|
|
176
174
|
|
|
177
|
-
def generate_new_active_file(self, context: Dict[str, Any], query: str) -> Tuple[str, Dict[str, Any]]:
|
|
175
|
+
def generate_new_active_file(self, context: Dict[str, Any], query: str,args:AutoCoderArgs) -> Tuple[str, Dict[str, Any]]:
|
|
178
176
|
"""
|
|
179
177
|
生成全新的活动文件内容
|
|
180
178
|
|
|
@@ -194,7 +192,8 @@ class ActivePackage:
|
|
|
194
192
|
end_time_current_change = time.monotonic()
|
|
195
193
|
|
|
196
194
|
# 使用TokenCostCalculator跟踪token使用情况
|
|
197
|
-
|
|
195
|
+
token_calculator = TokenCostCalculator(logger_name="ActivePackage.TokenCost",args=args)
|
|
196
|
+
current_change_stats: TokenUsageStats = token_calculator.track_token_usage(
|
|
198
197
|
llm=self.llm,
|
|
199
198
|
meta_holder=meta_holder_current_change,
|
|
200
199
|
operation_name="Current Change Generation",
|
|
@@ -213,7 +212,7 @@ class ActivePackage:
|
|
|
213
212
|
end_time_document = time.monotonic()
|
|
214
213
|
|
|
215
214
|
# 使用TokenCostCalculator跟踪token使用情况
|
|
216
|
-
document_stats: TokenUsageStats =
|
|
215
|
+
document_stats: TokenUsageStats = token_calculator.track_token_usage(
|
|
217
216
|
llm=self.llm,
|
|
218
217
|
meta_holder=meta_holder_document,
|
|
219
218
|
operation_name="Document Generation",
|
|
@@ -293,7 +292,7 @@ class ActivePackage:
|
|
|
293
292
|
self.logger.error(f"Error extracting sections: {e}")
|
|
294
293
|
return header, current_change_section, document_section
|
|
295
294
|
|
|
296
|
-
def generate_updated_active_file(self, context: Dict[str, Any], query: str, existing_content: str) -> Tuple[str, Dict[str, Any]]:
|
|
295
|
+
def generate_updated_active_file(self, context: Dict[str, Any], query: str, existing_content: str,args:AutoCoderArgs) -> Tuple[str, Dict[str, Any]]:
|
|
297
296
|
"""
|
|
298
297
|
基于现有内容生成更新后的活动文件内容
|
|
299
298
|
|
|
@@ -317,7 +316,8 @@ class ActivePackage:
|
|
|
317
316
|
end_time_current_change = time.monotonic()
|
|
318
317
|
|
|
319
318
|
# 使用TokenCostCalculator跟踪token使用情况
|
|
320
|
-
|
|
319
|
+
token_calculator = TokenCostCalculator(logger_name="ActivePackage.TokenCost",args=args)
|
|
320
|
+
update_current_change_stats: TokenUsageStats = token_calculator.track_token_usage(
|
|
321
321
|
llm=self.llm,
|
|
322
322
|
meta_holder=meta_holder_current_change,
|
|
323
323
|
operation_name="Update Current Change",
|
|
@@ -335,8 +335,8 @@ class ActivePackage:
|
|
|
335
335
|
meta_holder_document).run(context, query, existing_document)
|
|
336
336
|
end_time_document = time.monotonic()
|
|
337
337
|
|
|
338
|
-
# 使用TokenCostCalculator跟踪token使用情况
|
|
339
|
-
update_document_stats: TokenUsageStats =
|
|
338
|
+
# 使用TokenCostCalculator跟踪token使用情况
|
|
339
|
+
update_document_stats: TokenUsageStats = token_calculator.track_token_usage(
|
|
340
340
|
llm=self.llm,
|
|
341
341
|
meta_holder=meta_holder_document,
|
|
342
342
|
operation_name="Update Document",
|
|
File without changes
|