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.
Files changed (89) hide show
  1. ai_coding_assistant-0.5.0.dist-info/METADATA +226 -0
  2. ai_coding_assistant-0.5.0.dist-info/RECORD +89 -0
  3. ai_coding_assistant-0.5.0.dist-info/WHEEL +4 -0
  4. ai_coding_assistant-0.5.0.dist-info/entry_points.txt +3 -0
  5. ai_coding_assistant-0.5.0.dist-info/licenses/LICENSE +21 -0
  6. coding_assistant/__init__.py +3 -0
  7. coding_assistant/__main__.py +19 -0
  8. coding_assistant/cli/__init__.py +1 -0
  9. coding_assistant/cli/app.py +158 -0
  10. coding_assistant/cli/commands/__init__.py +19 -0
  11. coding_assistant/cli/commands/ask.py +178 -0
  12. coding_assistant/cli/commands/config.py +438 -0
  13. coding_assistant/cli/commands/diagram.py +267 -0
  14. coding_assistant/cli/commands/document.py +410 -0
  15. coding_assistant/cli/commands/explain.py +192 -0
  16. coding_assistant/cli/commands/fix.py +249 -0
  17. coding_assistant/cli/commands/index.py +162 -0
  18. coding_assistant/cli/commands/refactor.py +245 -0
  19. coding_assistant/cli/commands/search.py +182 -0
  20. coding_assistant/cli/commands/serve_docs.py +128 -0
  21. coding_assistant/cli/repl.py +381 -0
  22. coding_assistant/cli/theme.py +90 -0
  23. coding_assistant/codebase/__init__.py +1 -0
  24. coding_assistant/codebase/crawler.py +93 -0
  25. coding_assistant/codebase/parser.py +266 -0
  26. coding_assistant/config/__init__.py +25 -0
  27. coding_assistant/config/config_manager.py +615 -0
  28. coding_assistant/config/settings.py +82 -0
  29. coding_assistant/context/__init__.py +19 -0
  30. coding_assistant/context/chunker.py +443 -0
  31. coding_assistant/context/enhanced_retriever.py +322 -0
  32. coding_assistant/context/hybrid_search.py +311 -0
  33. coding_assistant/context/ranker.py +355 -0
  34. coding_assistant/context/retriever.py +119 -0
  35. coding_assistant/context/window.py +362 -0
  36. coding_assistant/documentation/__init__.py +23 -0
  37. coding_assistant/documentation/agents/__init__.py +27 -0
  38. coding_assistant/documentation/agents/coordinator.py +510 -0
  39. coding_assistant/documentation/agents/module_documenter.py +111 -0
  40. coding_assistant/documentation/agents/synthesizer.py +139 -0
  41. coding_assistant/documentation/agents/task_delegator.py +100 -0
  42. coding_assistant/documentation/decomposition/__init__.py +21 -0
  43. coding_assistant/documentation/decomposition/context_preserver.py +477 -0
  44. coding_assistant/documentation/decomposition/module_detector.py +302 -0
  45. coding_assistant/documentation/decomposition/partitioner.py +621 -0
  46. coding_assistant/documentation/generators/__init__.py +14 -0
  47. coding_assistant/documentation/generators/dataflow_generator.py +440 -0
  48. coding_assistant/documentation/generators/diagram_generator.py +511 -0
  49. coding_assistant/documentation/graph/__init__.py +13 -0
  50. coding_assistant/documentation/graph/dependency_builder.py +468 -0
  51. coding_assistant/documentation/graph/module_analyzer.py +475 -0
  52. coding_assistant/documentation/writers/__init__.py +11 -0
  53. coding_assistant/documentation/writers/markdown_writer.py +322 -0
  54. coding_assistant/embeddings/__init__.py +0 -0
  55. coding_assistant/embeddings/generator.py +89 -0
  56. coding_assistant/embeddings/store.py +187 -0
  57. coding_assistant/exceptions/__init__.py +50 -0
  58. coding_assistant/exceptions/base.py +110 -0
  59. coding_assistant/exceptions/llm.py +249 -0
  60. coding_assistant/exceptions/recovery.py +263 -0
  61. coding_assistant/exceptions/storage.py +213 -0
  62. coding_assistant/exceptions/validation.py +230 -0
  63. coding_assistant/llm/__init__.py +1 -0
  64. coding_assistant/llm/client.py +277 -0
  65. coding_assistant/llm/gemini_client.py +181 -0
  66. coding_assistant/llm/groq_client.py +160 -0
  67. coding_assistant/llm/prompts.py +98 -0
  68. coding_assistant/llm/together_client.py +160 -0
  69. coding_assistant/operations/__init__.py +13 -0
  70. coding_assistant/operations/differ.py +369 -0
  71. coding_assistant/operations/generator.py +347 -0
  72. coding_assistant/operations/linter.py +430 -0
  73. coding_assistant/operations/validator.py +406 -0
  74. coding_assistant/storage/__init__.py +9 -0
  75. coding_assistant/storage/database.py +363 -0
  76. coding_assistant/storage/session.py +231 -0
  77. coding_assistant/utils/__init__.py +31 -0
  78. coding_assistant/utils/cache.py +477 -0
  79. coding_assistant/utils/hardware.py +132 -0
  80. coding_assistant/utils/keystore.py +206 -0
  81. coding_assistant/utils/logger.py +32 -0
  82. coding_assistant/utils/progress.py +311 -0
  83. coding_assistant/validation/__init__.py +13 -0
  84. coding_assistant/validation/files.py +305 -0
  85. coding_assistant/validation/inputs.py +335 -0
  86. coding_assistant/validation/params.py +280 -0
  87. coding_assistant/validation/sanitizers.py +243 -0
  88. coding_assistant/vcs/__init__.py +5 -0
  89. coding_assistant/vcs/git.py +269 -0
@@ -0,0 +1,347 @@
1
+ """Code generation with extraction and validation."""
2
+
3
+ import re
4
+ from typing import List, Dict, Optional, Tuple
5
+ from dataclasses import dataclass
6
+ from coding_assistant.operations.validator import SyntaxValidator, ValidationResult
7
+
8
+
9
+ @dataclass
10
+ class CodeBlock:
11
+ """Represents a code block extracted from text."""
12
+ language: str
13
+ code: str
14
+ start_line: int
15
+ end_line: int
16
+ file_path: Optional[str] = None
17
+ is_valid: bool = False
18
+ validation_error: Optional[str] = None
19
+
20
+ def __repr__(self):
21
+ status = "✓ valid" if self.is_valid else f"✗ invalid: {self.validation_error}"
22
+ return f"CodeBlock({self.language}, lines {self.start_line}-{self.end_line}, {status})"
23
+
24
+
25
+ class CodeGenerator:
26
+ """Generate and validate code from LLM responses."""
27
+
28
+ def __init__(self):
29
+ """Initialize the code generator."""
30
+ self.validator = SyntaxValidator()
31
+
32
+ def extract_code_blocks(self, text: str, validate: bool = True) -> List[CodeBlock]:
33
+ """
34
+ Extract code blocks from markdown-formatted text.
35
+
36
+ Supports fenced code blocks with language specifiers:
37
+ ```python
38
+ code here
39
+ ```
40
+
41
+ Args:
42
+ text: The text containing code blocks (typically LLM response)
43
+ validate: Whether to validate extracted code blocks
44
+
45
+ Returns:
46
+ List of CodeBlock objects
47
+ """
48
+ code_blocks = []
49
+
50
+ # Pattern for fenced code blocks: ```language\ncode\n```
51
+ pattern = r'```(\w+)?\n(.*?)```'
52
+
53
+ lines = text.split('\n')
54
+ current_line = 0
55
+
56
+ # Find all code blocks
57
+ for match in re.finditer(pattern, text, re.DOTALL):
58
+ language = match.group(1) or 'text'
59
+ code = match.group(2)
60
+
61
+ # Calculate line numbers
62
+ start_pos = match.start()
63
+ start_line = text[:start_pos].count('\n') + 1
64
+ end_line = start_line + code.count('\n')
65
+
66
+ # Skip non-code languages
67
+ if language.lower() in ('text', 'plaintext', 'output', 'bash', 'shell', 'sh'):
68
+ continue
69
+
70
+ # Normalize language names
71
+ language = self._normalize_language(language)
72
+
73
+ # Create code block
74
+ block = CodeBlock(
75
+ language=language,
76
+ code=code.strip(),
77
+ start_line=start_line,
78
+ end_line=end_line
79
+ )
80
+
81
+ # Validate if requested
82
+ if validate and language in self.validator.SUPPORTED_LANGUAGES:
83
+ validation_result = self.validator.validate(block.code, language)
84
+ block.is_valid = validation_result.is_valid
85
+ if not validation_result.is_valid:
86
+ block.validation_error = validation_result.error_message
87
+
88
+ code_blocks.append(block)
89
+
90
+ return code_blocks
91
+
92
+ def extract_with_context(self, text: str) -> List[Dict]:
93
+ """
94
+ Extract code blocks with surrounding context.
95
+
96
+ Returns code blocks along with explanatory text before/after them.
97
+
98
+ Args:
99
+ text: The text containing code and explanations
100
+
101
+ Returns:
102
+ List of dicts with 'type' (code/text), 'content', and metadata
103
+ """
104
+ sections = []
105
+
106
+ # Split by code blocks but keep them
107
+ pattern = r'(```\w*\n.*?```)'
108
+ parts = re.split(pattern, text, flags=re.DOTALL)
109
+
110
+ for part in parts:
111
+ if part.strip():
112
+ if part.startswith('```'):
113
+ # It's a code block
114
+ blocks = self.extract_code_blocks(part)
115
+ for block in blocks:
116
+ sections.append({
117
+ 'type': 'code',
118
+ 'language': block.language,
119
+ 'content': block.code,
120
+ 'is_valid': block.is_valid,
121
+ 'validation_error': block.validation_error
122
+ })
123
+ else:
124
+ # It's text/explanation
125
+ sections.append({
126
+ 'type': 'text',
127
+ 'content': part.strip()
128
+ })
129
+
130
+ return sections
131
+
132
+ def generate_from_specification(self, specification: str,
133
+ context: Optional[List[Dict]] = None,
134
+ llm_client = None) -> List[CodeBlock]:
135
+ """
136
+ Generate code from a specification using an LLM.
137
+
138
+ Args:
139
+ specification: Description of what code to generate
140
+ context: Optional context (similar code, documentation, etc.)
141
+ llm_client: LLM client to use for generation
142
+
143
+ Returns:
144
+ List of generated and validated CodeBlock objects
145
+
146
+ Raises:
147
+ ValueError: If no LLM client provided
148
+ """
149
+ if not llm_client:
150
+ raise ValueError("LLM client required for code generation")
151
+
152
+ # Build prompt
153
+ prompt = self._build_generation_prompt(specification, context)
154
+
155
+ # Generate code
156
+ response = llm_client.generate(prompt, stream=False)
157
+
158
+ # Extract and validate code blocks
159
+ code_blocks = self.extract_code_blocks(response, validate=True)
160
+
161
+ return code_blocks
162
+
163
+ def validate_code_block(self, block: CodeBlock) -> ValidationResult:
164
+ """
165
+ Validate a code block.
166
+
167
+ Args:
168
+ block: The CodeBlock to validate
169
+
170
+ Returns:
171
+ ValidationResult
172
+ """
173
+ return self.validator.validate(block.code, block.language, block.file_path)
174
+
175
+ def attempt_fix(self, block: CodeBlock, llm_client=None) -> Optional[CodeBlock]:
176
+ """
177
+ Attempt to fix invalid code using LLM.
178
+
179
+ Args:
180
+ block: The invalid CodeBlock to fix
181
+ llm_client: LLM client for generating fixes
182
+
183
+ Returns:
184
+ Fixed CodeBlock if successful, None otherwise
185
+ """
186
+ if block.is_valid:
187
+ return block
188
+
189
+ if not llm_client:
190
+ return None
191
+
192
+ # Build fix prompt
193
+ prompt = f"""The following {block.language} code has a syntax error:
194
+
195
+ ```{block.language}
196
+ {block.code}
197
+ ```
198
+
199
+ Error: {block.validation_error}
200
+
201
+ Please fix the syntax error and provide only the corrected code in a code block.
202
+ """
203
+
204
+ try:
205
+ response = llm_client.generate(prompt, stream=False)
206
+ fixed_blocks = self.extract_code_blocks(response, validate=True)
207
+
208
+ if fixed_blocks and fixed_blocks[0].is_valid:
209
+ return fixed_blocks[0]
210
+ except Exception:
211
+ pass
212
+
213
+ return None
214
+
215
+ def _normalize_language(self, language: str) -> str:
216
+ """Normalize language identifiers."""
217
+ language = language.lower()
218
+
219
+ # Map common aliases
220
+ alias_map = {
221
+ 'py': 'python',
222
+ 'js': 'javascript',
223
+ 'ts': 'typescript',
224
+ 'jsx': 'jsx',
225
+ 'tsx': 'tsx',
226
+ 'javascript': 'javascript',
227
+ 'typescript': 'typescript',
228
+ 'python': 'python',
229
+ }
230
+
231
+ return alias_map.get(language, language)
232
+
233
+ def _build_generation_prompt(self, specification: str,
234
+ context: Optional[List[Dict]] = None) -> str:
235
+ """Build a prompt for code generation."""
236
+ prompt = "Generate code based on the following specification:\n\n"
237
+ prompt += f"{specification}\n\n"
238
+
239
+ if context:
240
+ prompt += "Context (related code from the project):\n\n"
241
+ for ctx in context:
242
+ if 'file_path' in ctx:
243
+ prompt += f"## {ctx['file_path']}\n"
244
+ prompt += f"```{ctx.get('language', 'python')}\n"
245
+ prompt += f"{ctx.get('content', '')}\n"
246
+ prompt += "```\n\n"
247
+
248
+ prompt += "\nProvide only the code in properly formatted code blocks with language specifiers."
249
+
250
+ return prompt
251
+
252
+ def extract_file_operations(self, text: str) -> List[Dict]:
253
+ """
254
+ Extract file operations from LLM response.
255
+
256
+ Looks for patterns like:
257
+ - "Create file: path/to/file.py"
258
+ - "Modify file: path/to/file.py"
259
+ - "Delete file: path/to/file.py"
260
+
261
+ Returns:
262
+ List of file operations with type, path, and code
263
+ """
264
+ operations = []
265
+
266
+ # Pattern for file operations
267
+ file_op_pattern = r'(Create|Modify|Update|Delete)\s+(?:file:?\s+)?`?([^\s`]+\.(?:py|js|ts|jsx|tsx))`?'
268
+
269
+ # Find all file operations
270
+ matches = re.finditer(file_op_pattern, text, re.IGNORECASE)
271
+
272
+ # Also extract code blocks
273
+ code_blocks = self.extract_code_blocks(text, validate=True)
274
+ code_block_idx = 0
275
+
276
+ for match in matches:
277
+ operation_type = match.group(1).lower()
278
+ file_path = match.group(2)
279
+
280
+ # Map operation types
281
+ if operation_type in ('create', 'modify', 'update'):
282
+ # Try to find associated code block
283
+ code = None
284
+ if code_block_idx < len(code_blocks):
285
+ code = code_blocks[code_block_idx].code
286
+ code_block_idx += 1
287
+
288
+ operations.append({
289
+ 'type': 'create' if operation_type == 'create' else 'modify',
290
+ 'file_path': file_path,
291
+ 'code': code,
292
+ 'validated': code_blocks[code_block_idx - 1].is_valid if code else False
293
+ })
294
+ elif operation_type == 'delete':
295
+ operations.append({
296
+ 'type': 'delete',
297
+ 'file_path': file_path
298
+ })
299
+
300
+ return operations
301
+
302
+ def format_code(self, code: str, language: str) -> str:
303
+ """
304
+ Format code according to language conventions.
305
+
306
+ Args:
307
+ code: The code to format
308
+ language: The programming language
309
+
310
+ Returns:
311
+ Formatted code
312
+ """
313
+ # For Python, could use black
314
+ # For JS/TS, could use prettier
315
+ # For now, just return as-is
316
+ # This is a placeholder for future formatting integration
317
+ return code
318
+
319
+ def count_lines(self, code: str) -> int:
320
+ """Count non-empty lines in code."""
321
+ return len([line for line in code.split('\n') if line.strip()])
322
+
323
+ def estimate_complexity(self, code: str, language: str) -> Dict:
324
+ """
325
+ Estimate code complexity.
326
+
327
+ Returns:
328
+ Dict with metrics like lines, functions, classes, etc.
329
+ """
330
+ metrics = {
331
+ 'lines': self.count_lines(code),
332
+ 'total_lines': len(code.split('\n')),
333
+ 'language': language
334
+ }
335
+
336
+ if language == 'python':
337
+ # Count Python-specific constructs
338
+ metrics['functions'] = code.count('def ')
339
+ metrics['classes'] = code.count('class ')
340
+ metrics['imports'] = code.count('import ') + code.count('from ')
341
+ elif language in ('javascript', 'typescript', 'jsx', 'tsx'):
342
+ # Count JS/TS-specific constructs
343
+ metrics['functions'] = code.count('function ') + code.count('=> ')
344
+ metrics['classes'] = code.count('class ')
345
+ metrics['imports'] = code.count('import ') + code.count('require(')
346
+
347
+ return metrics