cognify-code 0.2.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 (55) hide show
  1. ai_code_assistant/__init__.py +14 -0
  2. ai_code_assistant/agent/__init__.py +63 -0
  3. ai_code_assistant/agent/code_agent.py +461 -0
  4. ai_code_assistant/agent/code_generator.py +388 -0
  5. ai_code_assistant/agent/code_reviewer.py +365 -0
  6. ai_code_assistant/agent/diff_engine.py +308 -0
  7. ai_code_assistant/agent/file_manager.py +300 -0
  8. ai_code_assistant/agent/intent_classifier.py +284 -0
  9. ai_code_assistant/chat/__init__.py +11 -0
  10. ai_code_assistant/chat/agent_session.py +156 -0
  11. ai_code_assistant/chat/session.py +165 -0
  12. ai_code_assistant/cli.py +1571 -0
  13. ai_code_assistant/config.py +149 -0
  14. ai_code_assistant/editor/__init__.py +8 -0
  15. ai_code_assistant/editor/diff_handler.py +270 -0
  16. ai_code_assistant/editor/file_editor.py +350 -0
  17. ai_code_assistant/editor/prompts.py +146 -0
  18. ai_code_assistant/generator/__init__.py +7 -0
  19. ai_code_assistant/generator/code_gen.py +265 -0
  20. ai_code_assistant/generator/prompts.py +114 -0
  21. ai_code_assistant/git/__init__.py +6 -0
  22. ai_code_assistant/git/commit_generator.py +130 -0
  23. ai_code_assistant/git/manager.py +203 -0
  24. ai_code_assistant/llm.py +111 -0
  25. ai_code_assistant/providers/__init__.py +23 -0
  26. ai_code_assistant/providers/base.py +124 -0
  27. ai_code_assistant/providers/cerebras.py +97 -0
  28. ai_code_assistant/providers/factory.py +148 -0
  29. ai_code_assistant/providers/google.py +103 -0
  30. ai_code_assistant/providers/groq.py +111 -0
  31. ai_code_assistant/providers/ollama.py +86 -0
  32. ai_code_assistant/providers/openai.py +114 -0
  33. ai_code_assistant/providers/openrouter.py +130 -0
  34. ai_code_assistant/py.typed +0 -0
  35. ai_code_assistant/refactor/__init__.py +20 -0
  36. ai_code_assistant/refactor/analyzer.py +189 -0
  37. ai_code_assistant/refactor/change_plan.py +172 -0
  38. ai_code_assistant/refactor/multi_file_editor.py +346 -0
  39. ai_code_assistant/refactor/prompts.py +175 -0
  40. ai_code_assistant/retrieval/__init__.py +19 -0
  41. ai_code_assistant/retrieval/chunker.py +215 -0
  42. ai_code_assistant/retrieval/indexer.py +236 -0
  43. ai_code_assistant/retrieval/search.py +239 -0
  44. ai_code_assistant/reviewer/__init__.py +7 -0
  45. ai_code_assistant/reviewer/analyzer.py +278 -0
  46. ai_code_assistant/reviewer/prompts.py +113 -0
  47. ai_code_assistant/utils/__init__.py +18 -0
  48. ai_code_assistant/utils/file_handler.py +155 -0
  49. ai_code_assistant/utils/formatters.py +259 -0
  50. cognify_code-0.2.0.dist-info/METADATA +383 -0
  51. cognify_code-0.2.0.dist-info/RECORD +55 -0
  52. cognify_code-0.2.0.dist-info/WHEEL +5 -0
  53. cognify_code-0.2.0.dist-info/entry_points.txt +3 -0
  54. cognify_code-0.2.0.dist-info/licenses/LICENSE +22 -0
  55. cognify_code-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,388 @@
1
+ """Code Generator for AI-powered code generation."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict, List, Optional
5
+
6
+ from ai_code_assistant.agent.file_manager import FileContextManager, ProjectContext
7
+
8
+
9
+ @dataclass
10
+ class GeneratedCode:
11
+ """Represents generated code."""
12
+ code: str
13
+ file_path: str
14
+ language: str
15
+ description: str
16
+ is_new_file: bool = True
17
+ imports: List[str] = field(default_factory=list)
18
+ dependencies: List[str] = field(default_factory=list)
19
+
20
+
21
+ @dataclass
22
+ class CodeGenerationRequest:
23
+ """Request for code generation."""
24
+ description: str
25
+ language: Optional[str] = None
26
+ file_path: Optional[str] = None
27
+ context_files: List[str] = field(default_factory=list)
28
+ style_guide: Optional[str] = None
29
+ framework: Optional[str] = None
30
+
31
+
32
+ # Language-specific templates
33
+ CODE_TEMPLATES = {
34
+ "python": {
35
+ "function": '''def {name}({params}){return_type}:
36
+ """{docstring}"""
37
+ {body}
38
+ ''',
39
+ "class": '''class {name}{base}:
40
+ """{docstring}"""
41
+
42
+ def __init__(self{init_params}):
43
+ {init_body}
44
+
45
+ {methods}
46
+ ''',
47
+ "api_endpoint": '''@app.{method}("{path}")
48
+ async def {name}({params}){return_type}:
49
+ """{docstring}"""
50
+ {body}
51
+ ''',
52
+ },
53
+ "typescript": {
54
+ "function": '''export function {name}({params}): {return_type} {{
55
+ {body}
56
+ }}
57
+ ''',
58
+ "class": '''export class {name}{base} {{
59
+ {properties}
60
+
61
+ constructor({init_params}) {{
62
+ {init_body}
63
+ }}
64
+
65
+ {methods}
66
+ }}
67
+ ''',
68
+ "component": '''import React from 'react';
69
+
70
+ interface {name}Props {{
71
+ {props}
72
+ }}
73
+
74
+ export const {name}: React.FC<{name}Props> = ({{ {destructured_props} }}) => {{
75
+ return (
76
+ {jsx}
77
+ );
78
+ }};
79
+ ''',
80
+ },
81
+ }
82
+
83
+
84
+ GENERATION_PROMPT = '''You are an expert code generator. Generate high-quality, production-ready code based on the user's request.
85
+
86
+ ## Project Context
87
+ {project_context}
88
+
89
+ ## Existing Code Context
90
+ {code_context}
91
+
92
+ ## User Request
93
+ {request}
94
+
95
+ ## Requirements
96
+ - Language: {language}
97
+ - File path: {file_path}
98
+ - Framework: {framework}
99
+
100
+ ## Instructions
101
+ 1. Generate clean, well-documented code
102
+ 2. Follow best practices for {language}
103
+ 3. Include proper error handling
104
+ 4. Add type hints/annotations where applicable
105
+ 5. Include necessary imports at the top
106
+ 6. Match the style of existing code in the project
107
+
108
+ ## Output Format
109
+ Respond with ONLY the code, no explanations. Start with any necessary imports.
110
+ If creating a new file, include the complete file content.
111
+ If modifying existing code, show only the new/changed code.
112
+
113
+ ```{language}
114
+ '''
115
+
116
+
117
+ class CodeGenerator:
118
+ """Generates code using AI with project context awareness."""
119
+
120
+ def __init__(self, llm_manager, file_manager: Optional[FileContextManager] = None):
121
+ self.llm = llm_manager
122
+ self.file_manager = file_manager or FileContextManager()
123
+
124
+ def generate(self, request: CodeGenerationRequest) -> GeneratedCode:
125
+ """Generate code based on the request."""
126
+ # Get project context
127
+ project_context = self._build_project_context()
128
+
129
+ # Get code context from related files
130
+ code_context = self._build_code_context(request)
131
+
132
+ # Determine language and file path
133
+ language = request.language or self._detect_language(request)
134
+ file_path = request.file_path or self._suggest_file_path(request, language)
135
+
136
+ # Build prompt
137
+ prompt = GENERATION_PROMPT.format(
138
+ project_context=project_context,
139
+ code_context=code_context,
140
+ request=request.description,
141
+ language=language,
142
+ file_path=file_path,
143
+ framework=request.framework or "standard library",
144
+ )
145
+
146
+ # Generate code
147
+ response = self.llm.invoke(prompt)
148
+
149
+ # Parse response
150
+ code = self._extract_code(response, language)
151
+ imports = self._extract_imports(code, language)
152
+
153
+ return GeneratedCode(
154
+ code=code,
155
+ file_path=file_path,
156
+ language=language,
157
+ description=request.description,
158
+ is_new_file=not self.file_manager.file_exists(file_path),
159
+ imports=imports,
160
+ )
161
+
162
+ def generate_function(self, name: str, description: str,
163
+ language: str = "python",
164
+ params: Optional[List[Dict]] = None,
165
+ return_type: Optional[str] = None) -> GeneratedCode:
166
+ """Generate a function."""
167
+ params_str = self._format_params(params or [], language)
168
+
169
+ request = CodeGenerationRequest(
170
+ description=f"Create a function named '{name}' that {description}. "
171
+ f"Parameters: {params_str}. "
172
+ f"Return type: {return_type or 'appropriate type'}",
173
+ language=language,
174
+ )
175
+
176
+ return self.generate(request)
177
+
178
+ def generate_class(self, name: str, description: str,
179
+ language: str = "python",
180
+ methods: Optional[List[str]] = None,
181
+ base_class: Optional[str] = None) -> GeneratedCode:
182
+ """Generate a class."""
183
+ methods_str = ", ".join(methods) if methods else "appropriate methods"
184
+
185
+ request = CodeGenerationRequest(
186
+ description=f"Create a class named '{name}' that {description}. "
187
+ f"Include methods: {methods_str}. "
188
+ f"Base class: {base_class or 'none'}",
189
+ language=language,
190
+ )
191
+
192
+ return self.generate(request)
193
+
194
+ def generate_api_endpoint(self, path: str, method: str,
195
+ description: str,
196
+ framework: str = "fastapi") -> GeneratedCode:
197
+ """Generate an API endpoint."""
198
+ request = CodeGenerationRequest(
199
+ description=f"Create a {method.upper()} endpoint at '{path}' that {description}",
200
+ language="python",
201
+ framework=framework,
202
+ )
203
+
204
+ return self.generate(request)
205
+
206
+ def generate_test(self, target_file: str,
207
+ test_framework: str = "pytest") -> GeneratedCode:
208
+ """Generate tests for a file."""
209
+ # Read the target file
210
+ content = self.file_manager.read_file(target_file)
211
+
212
+ if not content:
213
+ raise ValueError(f"Cannot read file: {target_file}")
214
+
215
+ request = CodeGenerationRequest(
216
+ description=f"Generate comprehensive unit tests for the following code:\n\n{content[:3000]}",
217
+ language="python",
218
+ framework=test_framework,
219
+ context_files=[target_file],
220
+ )
221
+
222
+ result = self.generate(request)
223
+
224
+ # Suggest test file path
225
+ if target_file.startswith("src/"):
226
+ result.file_path = target_file.replace("src/", "tests/test_")
227
+ else:
228
+ result.file_path = f"tests/test_{target_file.split('/')[-1]}"
229
+
230
+ return result
231
+
232
+ def _build_project_context(self) -> str:
233
+ """Build project context string."""
234
+ context = self.file_manager.get_project_context()
235
+
236
+ lines = [
237
+ f"Project root: {context.root_path.name}",
238
+ f"Languages: {', '.join(context.languages)}",
239
+ f"Total files: {context.total_code_files} code files",
240
+ "",
241
+ "Project structure (summary):",
242
+ self.file_manager.get_structure_summary(max_depth=2),
243
+ ]
244
+
245
+ return "\n".join(lines)
246
+
247
+ def _build_code_context(self, request: CodeGenerationRequest) -> str:
248
+ """Build code context from related files."""
249
+ context_parts = []
250
+
251
+ # Read explicitly requested context files
252
+ for file_path in request.context_files[:5]: # Limit to 5 files
253
+ content = self.file_manager.read_file(file_path)
254
+ if content:
255
+ # Truncate large files
256
+ if len(content) > 2000:
257
+ content = content[:2000] + "\n... (truncated)"
258
+ context_parts.append(f"### {file_path}\n```\n{content}\n```")
259
+
260
+ # If file_path specified, get related files
261
+ if request.file_path:
262
+ related = self.file_manager.get_related_files(request.file_path)
263
+ for rel_path in related[:3]:
264
+ if rel_path not in request.context_files:
265
+ content = self.file_manager.read_file(rel_path)
266
+ if content and len(content) < 1500:
267
+ context_parts.append(f"### {rel_path} (related)\n```\n{content}\n```")
268
+
269
+ return "\n\n".join(context_parts) if context_parts else "No existing code context."
270
+
271
+ def _detect_language(self, request: CodeGenerationRequest) -> str:
272
+ """Detect language from request."""
273
+ desc_lower = request.description.lower()
274
+
275
+ language_keywords = {
276
+ "python": ["python", "django", "flask", "fastapi", "pytest"],
277
+ "typescript": ["typescript", "react", "angular", "vue", "tsx"],
278
+ "javascript": ["javascript", "node", "express", "js"],
279
+ "java": ["java", "spring", "maven", "gradle"],
280
+ "go": ["go", "golang", "gin"],
281
+ "rust": ["rust", "cargo"],
282
+ }
283
+
284
+ for lang, keywords in language_keywords.items():
285
+ if any(kw in desc_lower for kw in keywords):
286
+ return lang
287
+
288
+ # Default based on project
289
+ context = self.file_manager.get_project_context()
290
+ if context.languages:
291
+ # Return most common language
292
+ return list(context.languages)[0]
293
+
294
+ return "python"
295
+
296
+ def _suggest_file_path(self, request: CodeGenerationRequest, language: str) -> str:
297
+ """Suggest a file path for the generated code."""
298
+ ext_map = {
299
+ "python": ".py",
300
+ "typescript": ".ts",
301
+ "javascript": ".js",
302
+ "java": ".java",
303
+ "go": ".go",
304
+ "rust": ".rs",
305
+ }
306
+
307
+ ext = ext_map.get(language, ".txt")
308
+
309
+ # Extract a name from the description
310
+ desc_words = request.description.lower().split()
311
+ name_words = []
312
+
313
+ skip_words = {"create", "write", "generate", "make", "build", "a", "an", "the", "that", "which", "for"}
314
+
315
+ for word in desc_words[:5]:
316
+ word = ''.join(c for c in word if c.isalnum())
317
+ if word and word not in skip_words:
318
+ name_words.append(word)
319
+ if len(name_words) >= 2:
320
+ break
321
+
322
+ name = "_".join(name_words) if name_words else "generated"
323
+
324
+ return f"src/{name}{ext}"
325
+
326
+ def _extract_code(self, response: str, language: str) -> str:
327
+ """Extract code from LLM response."""
328
+ # Try to find code block
329
+ import re
330
+
331
+ # Match ```language or ``` code blocks
332
+ pattern = rf"```(?:{language})?\s*\n(.*?)```"
333
+ match = re.search(pattern, response, re.DOTALL | re.IGNORECASE)
334
+
335
+ if match:
336
+ return match.group(1).strip()
337
+
338
+ # If no code block, return cleaned response
339
+ lines = response.strip().split("\n")
340
+
341
+ # Remove common non-code prefixes
342
+ cleaned = []
343
+ for line in lines:
344
+ if not line.startswith(("Here", "This", "I'll", "The following", "```")):
345
+ cleaned.append(line)
346
+
347
+ return "\n".join(cleaned).strip()
348
+
349
+ def _extract_imports(self, code: str, language: str) -> List[str]:
350
+ """Extract import statements from code."""
351
+ imports = []
352
+
353
+ for line in code.split("\n"):
354
+ line = line.strip()
355
+
356
+ if language == "python":
357
+ if line.startswith("import ") or line.startswith("from "):
358
+ imports.append(line)
359
+ elif language in ("typescript", "javascript"):
360
+ if line.startswith("import "):
361
+ imports.append(line)
362
+ elif language == "java":
363
+ if line.startswith("import "):
364
+ imports.append(line)
365
+ elif language == "go":
366
+ if line.startswith("import "):
367
+ imports.append(line)
368
+
369
+ return imports
370
+
371
+ def _format_params(self, params: List[Dict], language: str) -> str:
372
+ """Format parameters for display."""
373
+ if not params:
374
+ return "none"
375
+
376
+ formatted = []
377
+ for p in params:
378
+ name = p.get("name", "param")
379
+ ptype = p.get("type", "")
380
+
381
+ if language == "python":
382
+ formatted.append(f"{name}: {ptype}" if ptype else name)
383
+ elif language in ("typescript", "javascript"):
384
+ formatted.append(f"{name}: {ptype}" if ptype else name)
385
+ else:
386
+ formatted.append(f"{ptype} {name}" if ptype else name)
387
+
388
+ return ", ".join(formatted)