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.
- ai_code_assistant/__init__.py +14 -0
- ai_code_assistant/agent/__init__.py +63 -0
- ai_code_assistant/agent/code_agent.py +461 -0
- ai_code_assistant/agent/code_generator.py +388 -0
- ai_code_assistant/agent/code_reviewer.py +365 -0
- ai_code_assistant/agent/diff_engine.py +308 -0
- ai_code_assistant/agent/file_manager.py +300 -0
- ai_code_assistant/agent/intent_classifier.py +284 -0
- ai_code_assistant/chat/__init__.py +11 -0
- ai_code_assistant/chat/agent_session.py +156 -0
- ai_code_assistant/chat/session.py +165 -0
- ai_code_assistant/cli.py +1571 -0
- ai_code_assistant/config.py +149 -0
- ai_code_assistant/editor/__init__.py +8 -0
- ai_code_assistant/editor/diff_handler.py +270 -0
- ai_code_assistant/editor/file_editor.py +350 -0
- ai_code_assistant/editor/prompts.py +146 -0
- ai_code_assistant/generator/__init__.py +7 -0
- ai_code_assistant/generator/code_gen.py +265 -0
- ai_code_assistant/generator/prompts.py +114 -0
- ai_code_assistant/git/__init__.py +6 -0
- ai_code_assistant/git/commit_generator.py +130 -0
- ai_code_assistant/git/manager.py +203 -0
- ai_code_assistant/llm.py +111 -0
- ai_code_assistant/providers/__init__.py +23 -0
- ai_code_assistant/providers/base.py +124 -0
- ai_code_assistant/providers/cerebras.py +97 -0
- ai_code_assistant/providers/factory.py +148 -0
- ai_code_assistant/providers/google.py +103 -0
- ai_code_assistant/providers/groq.py +111 -0
- ai_code_assistant/providers/ollama.py +86 -0
- ai_code_assistant/providers/openai.py +114 -0
- ai_code_assistant/providers/openrouter.py +130 -0
- ai_code_assistant/py.typed +0 -0
- ai_code_assistant/refactor/__init__.py +20 -0
- ai_code_assistant/refactor/analyzer.py +189 -0
- ai_code_assistant/refactor/change_plan.py +172 -0
- ai_code_assistant/refactor/multi_file_editor.py +346 -0
- ai_code_assistant/refactor/prompts.py +175 -0
- ai_code_assistant/retrieval/__init__.py +19 -0
- ai_code_assistant/retrieval/chunker.py +215 -0
- ai_code_assistant/retrieval/indexer.py +236 -0
- ai_code_assistant/retrieval/search.py +239 -0
- ai_code_assistant/reviewer/__init__.py +7 -0
- ai_code_assistant/reviewer/analyzer.py +278 -0
- ai_code_assistant/reviewer/prompts.py +113 -0
- ai_code_assistant/utils/__init__.py +18 -0
- ai_code_assistant/utils/file_handler.py +155 -0
- ai_code_assistant/utils/formatters.py +259 -0
- cognify_code-0.2.0.dist-info/METADATA +383 -0
- cognify_code-0.2.0.dist-info/RECORD +55 -0
- cognify_code-0.2.0.dist-info/WHEEL +5 -0
- cognify_code-0.2.0.dist-info/entry_points.txt +3 -0
- cognify_code-0.2.0.dist-info/licenses/LICENSE +22 -0
- cognify_code-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cognify AI - Your Local AI-Powered Code Assistant.
|
|
3
|
+
|
|
4
|
+
Review, generate, search, and refactor code with an intelligent AI agent.
|
|
5
|
+
All running locally with complete privacy using Ollama.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.2.0"
|
|
9
|
+
__author__ = "Ashok Kumar"
|
|
10
|
+
|
|
11
|
+
from ai_code_assistant.config import Config, load_config
|
|
12
|
+
from ai_code_assistant.llm import LLMManager
|
|
13
|
+
|
|
14
|
+
__all__ = ["Config", "load_config", "LLMManager", "__version__"]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""AI Code Agent - Intelligent code generation, review, and editing."""
|
|
2
|
+
|
|
3
|
+
from ai_code_assistant.agent.file_manager import (
|
|
4
|
+
FileContextManager,
|
|
5
|
+
FileInfo,
|
|
6
|
+
ProjectContext,
|
|
7
|
+
)
|
|
8
|
+
from ai_code_assistant.agent.intent_classifier import (
|
|
9
|
+
IntentClassifier,
|
|
10
|
+
Intent,
|
|
11
|
+
IntentType,
|
|
12
|
+
)
|
|
13
|
+
from ai_code_assistant.agent.code_generator import (
|
|
14
|
+
CodeGenerator,
|
|
15
|
+
CodeGenerationRequest,
|
|
16
|
+
GeneratedCode,
|
|
17
|
+
)
|
|
18
|
+
from ai_code_assistant.agent.diff_engine import (
|
|
19
|
+
DiffEngine,
|
|
20
|
+
ChangeSet,
|
|
21
|
+
FileDiff,
|
|
22
|
+
ChangeType,
|
|
23
|
+
)
|
|
24
|
+
from ai_code_assistant.agent.code_reviewer import (
|
|
25
|
+
CodeReviewer,
|
|
26
|
+
CodeIssue,
|
|
27
|
+
ReviewResult,
|
|
28
|
+
IssueSeverity,
|
|
29
|
+
IssueCategory,
|
|
30
|
+
)
|
|
31
|
+
from ai_code_assistant.agent.code_agent import (
|
|
32
|
+
CodeAgent,
|
|
33
|
+
AgentResponse,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
# File Manager
|
|
38
|
+
"FileContextManager",
|
|
39
|
+
"FileInfo",
|
|
40
|
+
"ProjectContext",
|
|
41
|
+
# Intent Classifier
|
|
42
|
+
"IntentClassifier",
|
|
43
|
+
"Intent",
|
|
44
|
+
"IntentType",
|
|
45
|
+
# Code Generator
|
|
46
|
+
"CodeGenerator",
|
|
47
|
+
"CodeGenerationRequest",
|
|
48
|
+
"GeneratedCode",
|
|
49
|
+
# Diff Engine
|
|
50
|
+
"DiffEngine",
|
|
51
|
+
"ChangeSet",
|
|
52
|
+
"FileDiff",
|
|
53
|
+
"ChangeType",
|
|
54
|
+
# Code Reviewer
|
|
55
|
+
"CodeReviewer",
|
|
56
|
+
"CodeIssue",
|
|
57
|
+
"ReviewResult",
|
|
58
|
+
"IssueSeverity",
|
|
59
|
+
"IssueCategory",
|
|
60
|
+
# Code Agent
|
|
61
|
+
"CodeAgent",
|
|
62
|
+
"AgentResponse",
|
|
63
|
+
]
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"""Code Agent - Main orchestrator for code generation, review, and editing."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Callable, List, Optional, Tuple
|
|
6
|
+
|
|
7
|
+
from ai_code_assistant.agent.file_manager import FileContextManager
|
|
8
|
+
from ai_code_assistant.agent.intent_classifier import IntentClassifier, Intent, IntentType
|
|
9
|
+
from ai_code_assistant.agent.code_generator import CodeGenerator, CodeGenerationRequest, GeneratedCode
|
|
10
|
+
from ai_code_assistant.agent.diff_engine import DiffEngine, ChangeSet, FileDiff
|
|
11
|
+
from ai_code_assistant.agent.code_reviewer import CodeReviewer, ReviewResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class AgentResponse:
|
|
16
|
+
"""Response from the code agent."""
|
|
17
|
+
message: str
|
|
18
|
+
intent: Optional[Intent] = None
|
|
19
|
+
generated_code: Optional[GeneratedCode] = None
|
|
20
|
+
review_result: Optional[ReviewResult] = None
|
|
21
|
+
changeset: Optional[ChangeSet] = None
|
|
22
|
+
requires_confirmation: bool = False
|
|
23
|
+
action_callback: Optional[Callable] = None
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def has_changes(self) -> bool:
|
|
27
|
+
return self.changeset is not None and self.changeset.files_changed > 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CodeAgent:
|
|
31
|
+
"""Main agent that orchestrates code operations based on user intent."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, llm_manager, root_path: Optional[Path] = None):
|
|
34
|
+
self.llm = llm_manager
|
|
35
|
+
self.file_manager = FileContextManager(root_path)
|
|
36
|
+
self.intent_classifier = IntentClassifier(llm_manager)
|
|
37
|
+
self.code_generator = CodeGenerator(llm_manager, self.file_manager)
|
|
38
|
+
self.diff_engine = DiffEngine(self.file_manager)
|
|
39
|
+
self.code_reviewer = CodeReviewer(llm_manager, self.file_manager)
|
|
40
|
+
|
|
41
|
+
# Pending changes awaiting confirmation
|
|
42
|
+
self._pending_changeset: Optional[ChangeSet] = None
|
|
43
|
+
|
|
44
|
+
def process(self, message: str, use_llm_classification: bool = True) -> AgentResponse:
|
|
45
|
+
"""Process a user message and return appropriate response."""
|
|
46
|
+
# Classify intent
|
|
47
|
+
if use_llm_classification:
|
|
48
|
+
intent = self.intent_classifier.classify_with_llm(message)
|
|
49
|
+
else:
|
|
50
|
+
intent = self.intent_classifier.classify(message)
|
|
51
|
+
|
|
52
|
+
# Route to appropriate handler
|
|
53
|
+
handlers = {
|
|
54
|
+
IntentType.CODE_GENERATE: self._handle_generate,
|
|
55
|
+
IntentType.CODE_EDIT: self._handle_edit,
|
|
56
|
+
IntentType.CODE_REVIEW: self._handle_review,
|
|
57
|
+
IntentType.CODE_EXPLAIN: self._handle_explain,
|
|
58
|
+
IntentType.CODE_REFACTOR: self._handle_refactor,
|
|
59
|
+
IntentType.TEST_GENERATE: self._handle_test_generate,
|
|
60
|
+
IntentType.FILE_CREATE: self._handle_file_create,
|
|
61
|
+
IntentType.FILE_DELETE: self._handle_file_delete,
|
|
62
|
+
IntentType.PROJECT_INFO: self._handle_project_info,
|
|
63
|
+
IntentType.GENERAL_CHAT: self._handle_general_chat,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
handler = handlers.get(intent.type, self._handle_general_chat)
|
|
67
|
+
return handler(message, intent)
|
|
68
|
+
|
|
69
|
+
def confirm_changes(self) -> Tuple[bool, str]:
|
|
70
|
+
"""Apply pending changes after user confirmation."""
|
|
71
|
+
if not self._pending_changeset:
|
|
72
|
+
return False, "No pending changes to apply."
|
|
73
|
+
|
|
74
|
+
successful, failed = self.diff_engine.apply_changeset(self._pending_changeset)
|
|
75
|
+
self._pending_changeset = None
|
|
76
|
+
|
|
77
|
+
if failed == 0:
|
|
78
|
+
return True, f"✓ Successfully applied changes to {successful} file(s)."
|
|
79
|
+
else:
|
|
80
|
+
return False, f"Applied {successful} file(s), {failed} failed."
|
|
81
|
+
|
|
82
|
+
def reject_changes(self) -> str:
|
|
83
|
+
"""Reject pending changes."""
|
|
84
|
+
self._pending_changeset = None
|
|
85
|
+
return "Changes discarded."
|
|
86
|
+
|
|
87
|
+
def _handle_generate(self, message: str, intent: Intent) -> AgentResponse:
|
|
88
|
+
"""Handle code generation requests."""
|
|
89
|
+
request = CodeGenerationRequest(
|
|
90
|
+
description=message,
|
|
91
|
+
language=intent.language,
|
|
92
|
+
file_path=intent.file_paths[0] if intent.file_paths else None,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
generated = self.code_generator.generate(request)
|
|
96
|
+
|
|
97
|
+
# Create changeset
|
|
98
|
+
changeset = ChangeSet(description=f"Generate: {message[:50]}...")
|
|
99
|
+
diff = self.diff_engine.create_file_diff(generated.file_path, generated.code)
|
|
100
|
+
changeset.diffs.append(diff)
|
|
101
|
+
|
|
102
|
+
self._pending_changeset = changeset
|
|
103
|
+
|
|
104
|
+
# Format response
|
|
105
|
+
preview = self.diff_engine.format_diff_simple(diff)
|
|
106
|
+
|
|
107
|
+
return AgentResponse(
|
|
108
|
+
message=f"I'll create the following code:\n\n{preview}",
|
|
109
|
+
intent=intent,
|
|
110
|
+
generated_code=generated,
|
|
111
|
+
changeset=changeset,
|
|
112
|
+
requires_confirmation=True,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def _handle_edit(self, message: str, intent: Intent) -> AgentResponse:
|
|
116
|
+
"""Handle code editing requests."""
|
|
117
|
+
if not intent.file_paths:
|
|
118
|
+
return AgentResponse(
|
|
119
|
+
message="Please specify which file you want to edit. For example: 'Edit src/main.py to add error handling'",
|
|
120
|
+
intent=intent,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
file_path = intent.file_paths[0]
|
|
124
|
+
original = self.file_manager.read_file(file_path)
|
|
125
|
+
|
|
126
|
+
if not original:
|
|
127
|
+
return AgentResponse(
|
|
128
|
+
message=f"Cannot find file: {file_path}",
|
|
129
|
+
intent=intent,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Generate edited code
|
|
133
|
+
prompt = f"""Edit the following code according to the user's request.
|
|
134
|
+
|
|
135
|
+
## Original Code ({file_path})
|
|
136
|
+
```
|
|
137
|
+
{original[:5000]}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## User Request
|
|
141
|
+
{message}
|
|
142
|
+
|
|
143
|
+
## Instructions
|
|
144
|
+
1. Make the requested changes
|
|
145
|
+
2. Keep the rest of the code unchanged
|
|
146
|
+
3. Return the COMPLETE modified file
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
response = self.llm.invoke(prompt)
|
|
152
|
+
new_code = self._extract_code(response)
|
|
153
|
+
|
|
154
|
+
# Create changeset
|
|
155
|
+
changeset = ChangeSet(description=f"Edit: {message[:50]}...")
|
|
156
|
+
diff = self.diff_engine.create_diff(original, new_code, file_path)
|
|
157
|
+
changeset.diffs.append(diff)
|
|
158
|
+
|
|
159
|
+
self._pending_changeset = changeset
|
|
160
|
+
|
|
161
|
+
preview = self.diff_engine.format_diff_simple(diff)
|
|
162
|
+
|
|
163
|
+
return AgentResponse(
|
|
164
|
+
message=f"Here are the proposed changes:\n\n{preview}",
|
|
165
|
+
intent=intent,
|
|
166
|
+
changeset=changeset,
|
|
167
|
+
requires_confirmation=True,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def _handle_review(self, message: str, intent: Intent) -> AgentResponse:
|
|
171
|
+
"""Handle code review requests."""
|
|
172
|
+
if not intent.file_paths:
|
|
173
|
+
# Try to find files to review
|
|
174
|
+
context = self.file_manager.get_project_context()
|
|
175
|
+
py_files = [f.relative_path for f in context.files if f.extension == ".py"][:5]
|
|
176
|
+
|
|
177
|
+
if py_files:
|
|
178
|
+
return AgentResponse(
|
|
179
|
+
message=f"Which file would you like me to review? Found these Python files:\n" +
|
|
180
|
+
"\n".join(f" • {f}" for f in py_files),
|
|
181
|
+
intent=intent,
|
|
182
|
+
)
|
|
183
|
+
return AgentResponse(
|
|
184
|
+
message="Please specify which file you want me to review.",
|
|
185
|
+
intent=intent,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
file_path = intent.file_paths[0]
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
result = self.code_reviewer.review_file(file_path)
|
|
192
|
+
except ValueError as e:
|
|
193
|
+
return AgentResponse(message=str(e), intent=intent)
|
|
194
|
+
|
|
195
|
+
# Format review output
|
|
196
|
+
lines = [result.format_summary()]
|
|
197
|
+
|
|
198
|
+
if result.issues:
|
|
199
|
+
lines.append("Issues found:\n")
|
|
200
|
+
for issue in result.issues[:10]:
|
|
201
|
+
lines.append(issue.format())
|
|
202
|
+
lines.append("")
|
|
203
|
+
|
|
204
|
+
if len(result.issues) > 10:
|
|
205
|
+
lines.append(f"... and {len(result.issues) - 10} more issues")
|
|
206
|
+
|
|
207
|
+
return AgentResponse(
|
|
208
|
+
message="\n".join(lines),
|
|
209
|
+
intent=intent,
|
|
210
|
+
review_result=result,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def _handle_explain(self, message: str, intent: Intent) -> AgentResponse:
|
|
214
|
+
"""Handle code explanation requests."""
|
|
215
|
+
if not intent.file_paths:
|
|
216
|
+
return AgentResponse(
|
|
217
|
+
message="Please specify which file or code you want me to explain.",
|
|
218
|
+
intent=intent,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
file_path = intent.file_paths[0]
|
|
222
|
+
content = self.file_manager.read_file(file_path)
|
|
223
|
+
|
|
224
|
+
if not content:
|
|
225
|
+
return AgentResponse(
|
|
226
|
+
message=f"Cannot find file: {file_path}",
|
|
227
|
+
intent=intent,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
prompt = f"""Explain the following code in a clear, educational way.
|
|
231
|
+
|
|
232
|
+
## Code ({file_path})
|
|
233
|
+
```
|
|
234
|
+
{content[:5000]}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Instructions
|
|
238
|
+
1. Start with a high-level overview
|
|
239
|
+
2. Explain the main components/functions
|
|
240
|
+
3. Describe the flow of execution
|
|
241
|
+
4. Note any important patterns or techniques used
|
|
242
|
+
5. Keep the explanation concise but thorough
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
explanation = self.llm.invoke(prompt)
|
|
246
|
+
|
|
247
|
+
return AgentResponse(
|
|
248
|
+
message=f"📖 **Explanation of {file_path}**\n\n{explanation}",
|
|
249
|
+
intent=intent,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def _handle_refactor(self, message: str, intent: Intent) -> AgentResponse:
|
|
253
|
+
"""Handle code refactoring requests."""
|
|
254
|
+
if not intent.file_paths:
|
|
255
|
+
return AgentResponse(
|
|
256
|
+
message="Please specify which file you want to refactor.",
|
|
257
|
+
intent=intent,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
file_path = intent.file_paths[0]
|
|
261
|
+
original = self.file_manager.read_file(file_path)
|
|
262
|
+
|
|
263
|
+
if not original:
|
|
264
|
+
return AgentResponse(
|
|
265
|
+
message=f"Cannot find file: {file_path}",
|
|
266
|
+
intent=intent,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
prompt = f"""Refactor the following code to improve its quality.
|
|
270
|
+
|
|
271
|
+
## Original Code ({file_path})
|
|
272
|
+
```
|
|
273
|
+
{original[:5000]}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## User Request
|
|
277
|
+
{message}
|
|
278
|
+
|
|
279
|
+
## Refactoring Goals
|
|
280
|
+
1. Improve readability
|
|
281
|
+
2. Reduce complexity
|
|
282
|
+
3. Follow best practices
|
|
283
|
+
4. Improve performance where possible
|
|
284
|
+
5. Add/improve documentation
|
|
285
|
+
|
|
286
|
+
Return the COMPLETE refactored file.
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
response = self.llm.invoke(prompt)
|
|
292
|
+
new_code = self._extract_code(response)
|
|
293
|
+
|
|
294
|
+
# Create changeset
|
|
295
|
+
changeset = ChangeSet(description=f"Refactor: {file_path}")
|
|
296
|
+
diff = self.diff_engine.create_diff(original, new_code, file_path)
|
|
297
|
+
changeset.diffs.append(diff)
|
|
298
|
+
|
|
299
|
+
self._pending_changeset = changeset
|
|
300
|
+
|
|
301
|
+
preview = self.diff_engine.format_diff_simple(diff)
|
|
302
|
+
|
|
303
|
+
return AgentResponse(
|
|
304
|
+
message=f"Here's the refactored code:\n\n{preview}",
|
|
305
|
+
intent=intent,
|
|
306
|
+
changeset=changeset,
|
|
307
|
+
requires_confirmation=True,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def _handle_test_generate(self, message: str, intent: Intent) -> AgentResponse:
|
|
311
|
+
"""Handle test generation requests."""
|
|
312
|
+
if not intent.file_paths:
|
|
313
|
+
return AgentResponse(
|
|
314
|
+
message="Please specify which file you want to generate tests for.",
|
|
315
|
+
intent=intent,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
file_path = intent.file_paths[0]
|
|
319
|
+
|
|
320
|
+
try:
|
|
321
|
+
generated = self.code_generator.generate_test(file_path)
|
|
322
|
+
except ValueError as e:
|
|
323
|
+
return AgentResponse(message=str(e), intent=intent)
|
|
324
|
+
|
|
325
|
+
# Create changeset
|
|
326
|
+
changeset = ChangeSet(description=f"Generate tests for {file_path}")
|
|
327
|
+
diff = self.diff_engine.create_file_diff(generated.file_path, generated.code)
|
|
328
|
+
changeset.diffs.append(diff)
|
|
329
|
+
|
|
330
|
+
self._pending_changeset = changeset
|
|
331
|
+
|
|
332
|
+
preview = self.diff_engine.format_diff_simple(diff)
|
|
333
|
+
|
|
334
|
+
return AgentResponse(
|
|
335
|
+
message=f"I'll create tests at {generated.file_path}:\n\n{preview}",
|
|
336
|
+
intent=intent,
|
|
337
|
+
generated_code=generated,
|
|
338
|
+
changeset=changeset,
|
|
339
|
+
requires_confirmation=True,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def _handle_file_create(self, message: str, intent: Intent) -> AgentResponse:
|
|
343
|
+
"""Handle file creation requests."""
|
|
344
|
+
# Extract file path from message or intent
|
|
345
|
+
file_path = intent.file_paths[0] if intent.file_paths else None
|
|
346
|
+
|
|
347
|
+
if not file_path:
|
|
348
|
+
return AgentResponse(
|
|
349
|
+
message="Please specify the file path to create.",
|
|
350
|
+
intent=intent,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Generate initial content based on file type
|
|
354
|
+
request = CodeGenerationRequest(
|
|
355
|
+
description=f"Create a new file: {message}",
|
|
356
|
+
file_path=file_path,
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
generated = self.code_generator.generate(request)
|
|
360
|
+
|
|
361
|
+
changeset = ChangeSet(description=f"Create file: {file_path}")
|
|
362
|
+
diff = self.diff_engine.create_file_diff(file_path, generated.code)
|
|
363
|
+
changeset.diffs.append(diff)
|
|
364
|
+
|
|
365
|
+
self._pending_changeset = changeset
|
|
366
|
+
|
|
367
|
+
preview = self.diff_engine.format_diff_simple(diff)
|
|
368
|
+
|
|
369
|
+
return AgentResponse(
|
|
370
|
+
message=f"I'll create {file_path}:\n\n{preview}",
|
|
371
|
+
intent=intent,
|
|
372
|
+
generated_code=generated,
|
|
373
|
+
changeset=changeset,
|
|
374
|
+
requires_confirmation=True,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
def _handle_file_delete(self, message: str, intent: Intent) -> AgentResponse:
|
|
378
|
+
"""Handle file deletion requests."""
|
|
379
|
+
if not intent.file_paths:
|
|
380
|
+
return AgentResponse(
|
|
381
|
+
message="Please specify which file to delete.",
|
|
382
|
+
intent=intent,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
file_path = intent.file_paths[0]
|
|
386
|
+
|
|
387
|
+
if not self.file_manager.file_exists(file_path):
|
|
388
|
+
return AgentResponse(
|
|
389
|
+
message=f"File not found: {file_path}",
|
|
390
|
+
intent=intent,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
content = self.file_manager.read_file(file_path) or ""
|
|
394
|
+
|
|
395
|
+
changeset = ChangeSet(description=f"Delete file: {file_path}")
|
|
396
|
+
diff = self.diff_engine.create_diff(content, "", file_path)
|
|
397
|
+
changeset.diffs.append(diff)
|
|
398
|
+
|
|
399
|
+
self._pending_changeset = changeset
|
|
400
|
+
|
|
401
|
+
return AgentResponse(
|
|
402
|
+
message=f"⚠️ This will delete {file_path} ({len(content)} bytes). Are you sure?",
|
|
403
|
+
intent=intent,
|
|
404
|
+
changeset=changeset,
|
|
405
|
+
requires_confirmation=True,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
def _handle_project_info(self, message: str, intent: Intent) -> AgentResponse:
|
|
409
|
+
"""Handle project information requests."""
|
|
410
|
+
context = self.file_manager.get_project_context()
|
|
411
|
+
structure = self.file_manager.get_structure_summary(max_depth=3)
|
|
412
|
+
|
|
413
|
+
info = f"""📁 **Project: {context.root_path.name}**
|
|
414
|
+
|
|
415
|
+
**Statistics:**
|
|
416
|
+
• Total files: {context.total_files}
|
|
417
|
+
• Code files: {context.total_code_files}
|
|
418
|
+
• Languages: {', '.join(sorted(context.languages)) or 'None detected'}
|
|
419
|
+
|
|
420
|
+
**Structure:**
|
|
421
|
+
{structure}
|
|
422
|
+
"""
|
|
423
|
+
|
|
424
|
+
return AgentResponse(message=info, intent=intent)
|
|
425
|
+
|
|
426
|
+
def _handle_general_chat(self, message: str, intent: Intent) -> AgentResponse:
|
|
427
|
+
"""Handle general chat/questions."""
|
|
428
|
+
# Get project context for better responses
|
|
429
|
+
context = self.file_manager.get_project_context()
|
|
430
|
+
|
|
431
|
+
prompt = f"""You are a helpful coding assistant. Answer the user's question.
|
|
432
|
+
|
|
433
|
+
Project context:
|
|
434
|
+
- Root: {context.root_path.name}
|
|
435
|
+
- Languages: {', '.join(context.languages)}
|
|
436
|
+
- Files: {context.total_code_files} code files
|
|
437
|
+
|
|
438
|
+
User: {message}
|
|
439
|
+
|
|
440
|
+
Provide a helpful, concise response. If the question is about code, you can suggest using specific commands like:
|
|
441
|
+
- "Create a function that..." for code generation
|
|
442
|
+
- "Review src/file.py" for code review
|
|
443
|
+
- "Explain src/file.py" for explanations
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
response = self.llm.invoke(prompt)
|
|
447
|
+
|
|
448
|
+
return AgentResponse(message=response, intent=intent)
|
|
449
|
+
|
|
450
|
+
def _extract_code(self, response: str) -> str:
|
|
451
|
+
"""Extract code from LLM response."""
|
|
452
|
+
import re
|
|
453
|
+
|
|
454
|
+
# Try to find code block
|
|
455
|
+
pattern = r"```(?:\w+)?\s*\n(.*?)```"
|
|
456
|
+
match = re.search(pattern, response, re.DOTALL)
|
|
457
|
+
|
|
458
|
+
if match:
|
|
459
|
+
return match.group(1).strip()
|
|
460
|
+
|
|
461
|
+
return response.strip()
|