ai-coding-assistant 0.5.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.
- ai_coding_assistant-0.5.0.dist-info/METADATA +226 -0
- ai_coding_assistant-0.5.0.dist-info/RECORD +89 -0
- ai_coding_assistant-0.5.0.dist-info/WHEEL +4 -0
- ai_coding_assistant-0.5.0.dist-info/entry_points.txt +3 -0
- ai_coding_assistant-0.5.0.dist-info/licenses/LICENSE +21 -0
- coding_assistant/__init__.py +3 -0
- coding_assistant/__main__.py +19 -0
- coding_assistant/cli/__init__.py +1 -0
- coding_assistant/cli/app.py +158 -0
- coding_assistant/cli/commands/__init__.py +19 -0
- coding_assistant/cli/commands/ask.py +178 -0
- coding_assistant/cli/commands/config.py +438 -0
- coding_assistant/cli/commands/diagram.py +267 -0
- coding_assistant/cli/commands/document.py +410 -0
- coding_assistant/cli/commands/explain.py +192 -0
- coding_assistant/cli/commands/fix.py +249 -0
- coding_assistant/cli/commands/index.py +162 -0
- coding_assistant/cli/commands/refactor.py +245 -0
- coding_assistant/cli/commands/search.py +182 -0
- coding_assistant/cli/commands/serve_docs.py +128 -0
- coding_assistant/cli/repl.py +381 -0
- coding_assistant/cli/theme.py +90 -0
- coding_assistant/codebase/__init__.py +1 -0
- coding_assistant/codebase/crawler.py +93 -0
- coding_assistant/codebase/parser.py +266 -0
- coding_assistant/config/__init__.py +25 -0
- coding_assistant/config/config_manager.py +615 -0
- coding_assistant/config/settings.py +82 -0
- coding_assistant/context/__init__.py +19 -0
- coding_assistant/context/chunker.py +443 -0
- coding_assistant/context/enhanced_retriever.py +322 -0
- coding_assistant/context/hybrid_search.py +311 -0
- coding_assistant/context/ranker.py +355 -0
- coding_assistant/context/retriever.py +119 -0
- coding_assistant/context/window.py +362 -0
- coding_assistant/documentation/__init__.py +23 -0
- coding_assistant/documentation/agents/__init__.py +27 -0
- coding_assistant/documentation/agents/coordinator.py +510 -0
- coding_assistant/documentation/agents/module_documenter.py +111 -0
- coding_assistant/documentation/agents/synthesizer.py +139 -0
- coding_assistant/documentation/agents/task_delegator.py +100 -0
- coding_assistant/documentation/decomposition/__init__.py +21 -0
- coding_assistant/documentation/decomposition/context_preserver.py +477 -0
- coding_assistant/documentation/decomposition/module_detector.py +302 -0
- coding_assistant/documentation/decomposition/partitioner.py +621 -0
- coding_assistant/documentation/generators/__init__.py +14 -0
- coding_assistant/documentation/generators/dataflow_generator.py +440 -0
- coding_assistant/documentation/generators/diagram_generator.py +511 -0
- coding_assistant/documentation/graph/__init__.py +13 -0
- coding_assistant/documentation/graph/dependency_builder.py +468 -0
- coding_assistant/documentation/graph/module_analyzer.py +475 -0
- coding_assistant/documentation/writers/__init__.py +11 -0
- coding_assistant/documentation/writers/markdown_writer.py +322 -0
- coding_assistant/embeddings/__init__.py +0 -0
- coding_assistant/embeddings/generator.py +89 -0
- coding_assistant/embeddings/store.py +187 -0
- coding_assistant/exceptions/__init__.py +50 -0
- coding_assistant/exceptions/base.py +110 -0
- coding_assistant/exceptions/llm.py +249 -0
- coding_assistant/exceptions/recovery.py +263 -0
- coding_assistant/exceptions/storage.py +213 -0
- coding_assistant/exceptions/validation.py +230 -0
- coding_assistant/llm/__init__.py +1 -0
- coding_assistant/llm/client.py +277 -0
- coding_assistant/llm/gemini_client.py +181 -0
- coding_assistant/llm/groq_client.py +160 -0
- coding_assistant/llm/prompts.py +98 -0
- coding_assistant/llm/together_client.py +160 -0
- coding_assistant/operations/__init__.py +13 -0
- coding_assistant/operations/differ.py +369 -0
- coding_assistant/operations/generator.py +347 -0
- coding_assistant/operations/linter.py +430 -0
- coding_assistant/operations/validator.py +406 -0
- coding_assistant/storage/__init__.py +9 -0
- coding_assistant/storage/database.py +363 -0
- coding_assistant/storage/session.py +231 -0
- coding_assistant/utils/__init__.py +31 -0
- coding_assistant/utils/cache.py +477 -0
- coding_assistant/utils/hardware.py +132 -0
- coding_assistant/utils/keystore.py +206 -0
- coding_assistant/utils/logger.py +32 -0
- coding_assistant/utils/progress.py +311 -0
- coding_assistant/validation/__init__.py +13 -0
- coding_assistant/validation/files.py +305 -0
- coding_assistant/validation/inputs.py +335 -0
- coding_assistant/validation/params.py +280 -0
- coding_assistant/validation/sanitizers.py +243 -0
- coding_assistant/vcs/__init__.py +5 -0
- coding_assistant/vcs/git.py +269 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""Syntax validation for Python and JavaScript/TypeScript code."""
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Dict, Optional, Tuple
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ValidationResult:
|
|
10
|
+
"""Result of syntax validation."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, is_valid: bool, error_message: Optional[str] = None,
|
|
13
|
+
line: Optional[int] = None, column: Optional[int] = None):
|
|
14
|
+
self.is_valid = is_valid
|
|
15
|
+
self.error_message = error_message
|
|
16
|
+
self.line = line
|
|
17
|
+
self.column = column
|
|
18
|
+
|
|
19
|
+
def __bool__(self):
|
|
20
|
+
return self.is_valid
|
|
21
|
+
|
|
22
|
+
def __repr__(self):
|
|
23
|
+
if self.is_valid:
|
|
24
|
+
return "ValidationResult(valid=True)"
|
|
25
|
+
return f"ValidationResult(valid=False, line={self.line}, error={self.error_message})"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SyntaxValidator:
|
|
29
|
+
"""Validate syntax for Python and JavaScript/TypeScript code."""
|
|
30
|
+
|
|
31
|
+
SUPPORTED_LANGUAGES = {'python', 'javascript', 'typescript', 'jsx', 'tsx'}
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
"""Initialize the syntax validator."""
|
|
35
|
+
self._tree_sitter_available = False
|
|
36
|
+
self._ts_parser = None
|
|
37
|
+
self._js_language = None
|
|
38
|
+
self._ts_language = None
|
|
39
|
+
|
|
40
|
+
# Try to initialize tree-sitter for JS/TS
|
|
41
|
+
try:
|
|
42
|
+
from tree_sitter import Language, Parser
|
|
43
|
+
# Check if language libraries are built
|
|
44
|
+
# This would need tree-sitter language grammars pre-built
|
|
45
|
+
self._tree_sitter_available = True
|
|
46
|
+
except ImportError:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
def validate(self, code: str, language: str,
|
|
50
|
+
file_path: Optional[str] = None) -> ValidationResult:
|
|
51
|
+
"""
|
|
52
|
+
Validate code syntax for the given language.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
code: The source code to validate
|
|
56
|
+
language: The programming language (python, javascript, typescript)
|
|
57
|
+
file_path: Optional file path for better error messages
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
ValidationResult indicating if code is valid
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If language is not supported
|
|
64
|
+
"""
|
|
65
|
+
language = language.lower()
|
|
66
|
+
|
|
67
|
+
if language not in self.SUPPORTED_LANGUAGES:
|
|
68
|
+
raise ValueError(
|
|
69
|
+
f"Unsupported language: {language}. "
|
|
70
|
+
f"Supported: {', '.join(self.SUPPORTED_LANGUAGES)}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if language == 'python':
|
|
74
|
+
return self._validate_python(code, file_path)
|
|
75
|
+
elif language in ('javascript', 'jsx'):
|
|
76
|
+
return self._validate_javascript(code, file_path)
|
|
77
|
+
elif language in ('typescript', 'tsx'):
|
|
78
|
+
return self._validate_typescript(code, file_path)
|
|
79
|
+
|
|
80
|
+
def _validate_python(self, code: str,
|
|
81
|
+
file_path: Optional[str] = None) -> ValidationResult:
|
|
82
|
+
"""
|
|
83
|
+
Validate Python code using AST parsing.
|
|
84
|
+
|
|
85
|
+
This provides the most accurate validation for Python code.
|
|
86
|
+
"""
|
|
87
|
+
if not code.strip():
|
|
88
|
+
return ValidationResult(False, "Empty code", line=1, column=1)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
# First try: compile to check syntax
|
|
92
|
+
compile(code, file_path or '<string>', 'exec')
|
|
93
|
+
|
|
94
|
+
# Second try: parse AST for deeper validation
|
|
95
|
+
ast.parse(code, filename=file_path or '<string>')
|
|
96
|
+
|
|
97
|
+
return ValidationResult(True)
|
|
98
|
+
|
|
99
|
+
except SyntaxError as e:
|
|
100
|
+
return ValidationResult(
|
|
101
|
+
is_valid=False,
|
|
102
|
+
error_message=f"SyntaxError: {e.msg}",
|
|
103
|
+
line=e.lineno,
|
|
104
|
+
column=e.offset
|
|
105
|
+
)
|
|
106
|
+
except IndentationError as e:
|
|
107
|
+
return ValidationResult(
|
|
108
|
+
is_valid=False,
|
|
109
|
+
error_message=f"IndentationError: {e.msg}",
|
|
110
|
+
line=e.lineno,
|
|
111
|
+
column=e.offset
|
|
112
|
+
)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
return ValidationResult(
|
|
115
|
+
is_valid=False,
|
|
116
|
+
error_message=f"Error: {str(e)}",
|
|
117
|
+
line=None,
|
|
118
|
+
column=None
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def _validate_javascript(self, code: str,
|
|
122
|
+
file_path: Optional[str] = None) -> ValidationResult:
|
|
123
|
+
"""
|
|
124
|
+
Validate JavaScript code.
|
|
125
|
+
|
|
126
|
+
Uses tree-sitter if available, otherwise basic heuristics.
|
|
127
|
+
"""
|
|
128
|
+
if not code.strip():
|
|
129
|
+
return ValidationResult(False, "Empty code", line=1, column=1)
|
|
130
|
+
|
|
131
|
+
# Try tree-sitter first (most accurate)
|
|
132
|
+
if self._tree_sitter_available:
|
|
133
|
+
return self._validate_with_tree_sitter(code, 'javascript', file_path)
|
|
134
|
+
|
|
135
|
+
# Fallback: Try using Node.js if available
|
|
136
|
+
result = self._validate_with_nodejs(code, file_path)
|
|
137
|
+
if result:
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
# Last resort: Basic syntax checks
|
|
141
|
+
return self._basic_javascript_validation(code)
|
|
142
|
+
|
|
143
|
+
def _validate_typescript(self, code: str,
|
|
144
|
+
file_path: Optional[str] = None) -> ValidationResult:
|
|
145
|
+
"""
|
|
146
|
+
Validate TypeScript code.
|
|
147
|
+
|
|
148
|
+
Uses tree-sitter if available, otherwise tries tsc.
|
|
149
|
+
"""
|
|
150
|
+
if not code.strip():
|
|
151
|
+
return ValidationResult(False, "Empty code", line=1, column=1)
|
|
152
|
+
|
|
153
|
+
# Try tree-sitter first
|
|
154
|
+
if self._tree_sitter_available:
|
|
155
|
+
return self._validate_with_tree_sitter(code, 'typescript', file_path)
|
|
156
|
+
|
|
157
|
+
# Fallback: Try TypeScript compiler if available
|
|
158
|
+
result = self._validate_with_tsc(code, file_path)
|
|
159
|
+
if result:
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
# TypeScript is superset of JavaScript, try JS validation
|
|
163
|
+
return self._validate_javascript(code, file_path)
|
|
164
|
+
|
|
165
|
+
def _validate_with_tree_sitter(self, code: str, language: str,
|
|
166
|
+
file_path: Optional[str] = None) -> ValidationResult:
|
|
167
|
+
"""Validate using tree-sitter parser."""
|
|
168
|
+
try:
|
|
169
|
+
from tree_sitter import Language, Parser
|
|
170
|
+
|
|
171
|
+
# Initialize parser if needed
|
|
172
|
+
if not self._ts_parser:
|
|
173
|
+
self._ts_parser = Parser()
|
|
174
|
+
|
|
175
|
+
# This requires pre-built language files
|
|
176
|
+
# For now, return a basic validation result
|
|
177
|
+
# TODO: Build tree-sitter language grammars
|
|
178
|
+
return ValidationResult(
|
|
179
|
+
is_valid=True,
|
|
180
|
+
error_message="Tree-sitter validation not fully implemented yet"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
# If tree-sitter fails, don't block - return warning
|
|
185
|
+
return ValidationResult(
|
|
186
|
+
is_valid=True,
|
|
187
|
+
error_message=f"Tree-sitter validation unavailable: {e}"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def _validate_with_nodejs(self, code: str,
|
|
191
|
+
file_path: Optional[str] = None) -> Optional[ValidationResult]:
|
|
192
|
+
"""Validate JavaScript using Node.js if available."""
|
|
193
|
+
import subprocess
|
|
194
|
+
import tempfile
|
|
195
|
+
import os
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
# Create temp file with code
|
|
199
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as f:
|
|
200
|
+
f.write(code)
|
|
201
|
+
temp_path = f.name
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
# Try to parse with Node.js (--check flag)
|
|
205
|
+
result = subprocess.run(
|
|
206
|
+
['node', '--check', temp_path],
|
|
207
|
+
capture_output=True,
|
|
208
|
+
text=True,
|
|
209
|
+
timeout=5
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if result.returncode == 0:
|
|
213
|
+
return ValidationResult(True)
|
|
214
|
+
else:
|
|
215
|
+
# Parse error message
|
|
216
|
+
error_msg = result.stderr.strip()
|
|
217
|
+
return self._parse_nodejs_error(error_msg)
|
|
218
|
+
|
|
219
|
+
finally:
|
|
220
|
+
# Clean up temp file
|
|
221
|
+
os.unlink(temp_path)
|
|
222
|
+
|
|
223
|
+
except FileNotFoundError:
|
|
224
|
+
# Node.js not available
|
|
225
|
+
return None
|
|
226
|
+
except Exception:
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
def _validate_with_tsc(self, code: str,
|
|
230
|
+
file_path: Optional[str] = None) -> Optional[ValidationResult]:
|
|
231
|
+
"""Validate TypeScript using tsc if available."""
|
|
232
|
+
import subprocess
|
|
233
|
+
import tempfile
|
|
234
|
+
import os
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
# Create temp file with code
|
|
238
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.ts', delete=False) as f:
|
|
239
|
+
f.write(code)
|
|
240
|
+
temp_path = f.name
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
# Try to compile with TypeScript compiler
|
|
244
|
+
result = subprocess.run(
|
|
245
|
+
['npx', 'tsc', '--noEmit', temp_path],
|
|
246
|
+
capture_output=True,
|
|
247
|
+
text=True,
|
|
248
|
+
timeout=10
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if result.returncode == 0:
|
|
252
|
+
return ValidationResult(True)
|
|
253
|
+
else:
|
|
254
|
+
# Parse error message
|
|
255
|
+
error_msg = result.stdout.strip()
|
|
256
|
+
return self._parse_tsc_error(error_msg)
|
|
257
|
+
|
|
258
|
+
finally:
|
|
259
|
+
# Clean up temp file
|
|
260
|
+
os.unlink(temp_path)
|
|
261
|
+
|
|
262
|
+
except FileNotFoundError:
|
|
263
|
+
# tsc not available
|
|
264
|
+
return None
|
|
265
|
+
except Exception:
|
|
266
|
+
return None
|
|
267
|
+
|
|
268
|
+
def _basic_javascript_validation(self, code: str) -> ValidationResult:
|
|
269
|
+
"""
|
|
270
|
+
Basic JavaScript syntax validation using heuristics.
|
|
271
|
+
|
|
272
|
+
This is a fallback when no proper parser is available.
|
|
273
|
+
"""
|
|
274
|
+
lines = code.split('\n')
|
|
275
|
+
|
|
276
|
+
# Basic bracket matching
|
|
277
|
+
stack = []
|
|
278
|
+
bracket_pairs = {'(': ')', '[': ']', '{': '}'}
|
|
279
|
+
|
|
280
|
+
for line_num, line in enumerate(lines, 1):
|
|
281
|
+
# Skip comments
|
|
282
|
+
if line.strip().startswith('//'):
|
|
283
|
+
continue
|
|
284
|
+
|
|
285
|
+
for col_num, char in enumerate(line, 1):
|
|
286
|
+
if char in bracket_pairs:
|
|
287
|
+
stack.append((char, line_num, col_num))
|
|
288
|
+
elif char in bracket_pairs.values():
|
|
289
|
+
if not stack:
|
|
290
|
+
return ValidationResult(
|
|
291
|
+
is_valid=False,
|
|
292
|
+
error_message=f"Unexpected closing bracket '{char}'",
|
|
293
|
+
line=line_num,
|
|
294
|
+
column=col_num
|
|
295
|
+
)
|
|
296
|
+
opening, _, _ = stack.pop()
|
|
297
|
+
if bracket_pairs[opening] != char:
|
|
298
|
+
return ValidationResult(
|
|
299
|
+
is_valid=False,
|
|
300
|
+
error_message=f"Mismatched brackets: '{opening}' and '{char}'",
|
|
301
|
+
line=line_num,
|
|
302
|
+
column=col_num
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if stack:
|
|
306
|
+
char, line_num, col_num = stack[-1]
|
|
307
|
+
return ValidationResult(
|
|
308
|
+
is_valid=False,
|
|
309
|
+
error_message=f"Unclosed bracket '{char}'",
|
|
310
|
+
line=line_num,
|
|
311
|
+
column=col_num
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# If basic checks pass, assume valid
|
|
315
|
+
# (This is not comprehensive but better than nothing)
|
|
316
|
+
return ValidationResult(True)
|
|
317
|
+
|
|
318
|
+
def _parse_nodejs_error(self, error_msg: str) -> ValidationResult:
|
|
319
|
+
"""Parse Node.js error message."""
|
|
320
|
+
# Node.js errors typically format as: file:line
|
|
321
|
+
# Example: "SyntaxError: Unexpected token '}'"
|
|
322
|
+
try:
|
|
323
|
+
if 'SyntaxError' in error_msg:
|
|
324
|
+
return ValidationResult(
|
|
325
|
+
is_valid=False,
|
|
326
|
+
error_message=error_msg.split('\n')[0],
|
|
327
|
+
line=None,
|
|
328
|
+
column=None
|
|
329
|
+
)
|
|
330
|
+
except:
|
|
331
|
+
pass
|
|
332
|
+
|
|
333
|
+
return ValidationResult(
|
|
334
|
+
is_valid=False,
|
|
335
|
+
error_message=error_msg[:200], # Limit message length
|
|
336
|
+
line=None,
|
|
337
|
+
column=None
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
def _parse_tsc_error(self, error_msg: str) -> ValidationResult:
|
|
341
|
+
"""Parse TypeScript compiler error message."""
|
|
342
|
+
# tsc errors format: file(line,col): error TS####: message
|
|
343
|
+
try:
|
|
344
|
+
if error_msg:
|
|
345
|
+
return ValidationResult(
|
|
346
|
+
is_valid=False,
|
|
347
|
+
error_message=error_msg.split('\n')[0],
|
|
348
|
+
line=None,
|
|
349
|
+
column=None
|
|
350
|
+
)
|
|
351
|
+
except:
|
|
352
|
+
pass
|
|
353
|
+
|
|
354
|
+
return ValidationResult(
|
|
355
|
+
is_valid=False,
|
|
356
|
+
error_message=error_msg[:200],
|
|
357
|
+
line=None,
|
|
358
|
+
column=None
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
def validate_file(self, file_path: str) -> ValidationResult:
|
|
362
|
+
"""
|
|
363
|
+
Validate a file based on its extension.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
file_path: Path to the file to validate
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
ValidationResult
|
|
370
|
+
"""
|
|
371
|
+
path = Path(file_path)
|
|
372
|
+
|
|
373
|
+
if not path.exists():
|
|
374
|
+
return ValidationResult(
|
|
375
|
+
is_valid=False,
|
|
376
|
+
error_message=f"File not found: {file_path}"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Determine language from extension
|
|
380
|
+
extension = path.suffix.lower()
|
|
381
|
+
language_map = {
|
|
382
|
+
'.py': 'python',
|
|
383
|
+
'.js': 'javascript',
|
|
384
|
+
'.jsx': 'jsx',
|
|
385
|
+
'.ts': 'typescript',
|
|
386
|
+
'.tsx': 'tsx',
|
|
387
|
+
'.mjs': 'javascript',
|
|
388
|
+
'.cjs': 'javascript',
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
language = language_map.get(extension)
|
|
392
|
+
if not language:
|
|
393
|
+
return ValidationResult(
|
|
394
|
+
is_valid=False,
|
|
395
|
+
error_message=f"Unsupported file extension: {extension}"
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
# Read and validate
|
|
399
|
+
try:
|
|
400
|
+
code = path.read_text(encoding='utf-8')
|
|
401
|
+
return self.validate(code, language, str(file_path))
|
|
402
|
+
except Exception as e:
|
|
403
|
+
return ValidationResult(
|
|
404
|
+
is_valid=False,
|
|
405
|
+
error_message=f"Error reading file: {e}"
|
|
406
|
+
)
|