handycode 2.1.3__tar.gz → 2.1.5__tar.gz
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.
- {handycode-2.1.3 → handycode-2.1.5}/PKG-INFO +1 -1
- {handycode-2.1.3 → handycode-2.1.5}/handycode/__init__.py +1 -1
- {handycode-2.1.3 → handycode-2.1.5}/handycode/assistant.py +140 -201
- {handycode-2.1.3 → handycode-2.1.5}/handycode.egg-info/PKG-INFO +1 -1
- {handycode-2.1.3 → handycode-2.1.5}/setup.py +1 -1
- {handycode-2.1.3 → handycode-2.1.5}/LICENSE +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/README.md +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/__main__.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/cli.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/config.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/file_manager.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/logo.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/main.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/models.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/project_templates.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/security.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode/utils.py +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode.egg-info/SOURCES.txt +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode.egg-info/dependency_links.txt +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode.egg-info/entry_points.txt +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode.egg-info/requires.txt +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/handycode.egg-info/top_level.txt +0 -0
- {handycode-2.1.3 → handycode-2.1.5}/setup.cfg +0 -0
|
@@ -70,6 +70,10 @@ class HandyCode:
|
|
|
70
70
|
"start_time": datetime.now()
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
# Буфер для потокового создания файлов
|
|
74
|
+
self.stream_buffer = ""
|
|
75
|
+
self.current_file_action = None
|
|
76
|
+
|
|
73
77
|
self._setup_readline()
|
|
74
78
|
signal.signal(signal.SIGINT, self._signal_handler)
|
|
75
79
|
self._interrupt_count = 0
|
|
@@ -131,64 +135,33 @@ class HandyCode:
|
|
|
131
135
|
return f"{size:.1f}TB"
|
|
132
136
|
|
|
133
137
|
def _get_system_prompt(self):
|
|
134
|
-
return """You are HandyCode -
|
|
135
|
-
|
|
136
|
-
FILE OPERATIONS - USE EXACT FORMAT:
|
|
137
|
-
To create a file, use [[CREATE:path/to/file]] followed by the complete file content on the next lines, then [[END]] to mark end of file:
|
|
138
|
-
[[CREATE:path/to/file.py]]
|
|
139
|
-
import os
|
|
138
|
+
return """You are HandyCode - an AI assistant for coding. You can create, modify, delete files and run commands.
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
print("Hello World")
|
|
140
|
+
CRITICAL: You MUST complete ALL actions in ONE response. If user asks to create AND run a project, do BOTH in the same response.
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
FILE FORMAT - use EXACTLY:
|
|
143
|
+
[[CREATE:filename]]
|
|
144
|
+
code here
|
|
146
145
|
[[END]]
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
new complete content here
|
|
147
|
+
[[MODIFY:filename]]
|
|
148
|
+
new code here
|
|
151
149
|
[[END]]
|
|
152
150
|
|
|
153
|
-
|
|
154
|
-
[[
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
[[READ:path/to/file.py]]
|
|
151
|
+
[[DELETE:filename]]
|
|
152
|
+
[[READ:filename]]
|
|
153
|
+
[[LIST:directory/]]
|
|
154
|
+
[[EXEC:command]]
|
|
158
155
|
|
|
159
|
-
|
|
160
|
-
|
|
156
|
+
RULES:
|
|
157
|
+
1. CREATE and EXEC in SAME response - create files AND run them together
|
|
158
|
+
2. Always use [[END]] to close files
|
|
159
|
+
3. NEVER put comments inside [[CREATE]]...[[END]] blocks
|
|
160
|
+
4. Put explanations BEFORE [[CREATE]] blocks, not inside
|
|
161
|
+
5. When user asks to create and run - do both immediately
|
|
162
|
+
6. Files create automatically, commands need confirmation
|
|
161
163
|
|
|
162
|
-
|
|
163
|
-
[[EXEC:python script.py]]
|
|
164
|
-
|
|
165
|
-
CRITICAL RULES:
|
|
166
|
-
1. ALWAYS put [[END]] after file content for CREATE and MODIFY
|
|
167
|
-
2. Show COMPLETE file content between [[CREATE/MODIFY:...]] and [[END]]
|
|
168
|
-
3. NEVER include comments or explanations INSIDE the file content
|
|
169
|
-
4. Only the actual code goes between [[CREATE:...]] and [[END]]
|
|
170
|
-
5. Explain what you're doing BEFORE the [[CREATE:...]] block
|
|
171
|
-
6. Do NOT put your explanations inside [[CREATE:...]] [[END]] blocks
|
|
172
|
-
7. Files are created/modified automatically without asking
|
|
173
|
-
8. Commands (EXEC) require user confirmation
|
|
174
|
-
|
|
175
|
-
Example of CORRECT format:
|
|
176
|
-
I'll create a Python script for you.
|
|
177
|
-
|
|
178
|
-
[[CREATE:hello.py]]
|
|
179
|
-
print("Hello World")
|
|
180
|
-
[[END]]
|
|
181
|
-
|
|
182
|
-
Now you can run it with: python hello.py
|
|
183
|
-
|
|
184
|
-
Example of WRONG format (DON'T DO THIS):
|
|
185
|
-
[[CREATE:hello.py]]
|
|
186
|
-
Here's your file:
|
|
187
|
-
print("Hello World")
|
|
188
|
-
This file prints hello
|
|
189
|
-
[[END]]
|
|
190
|
-
|
|
191
|
-
Respond in Russian. Write code in English."""
|
|
164
|
+
Speak Russian. Write code in English."""
|
|
192
165
|
|
|
193
166
|
def _setup_readline(self):
|
|
194
167
|
if not HAS_READLINE:
|
|
@@ -212,13 +185,71 @@ Respond in Russian. Write code in English."""
|
|
|
212
185
|
def reset_interrupt(self):
|
|
213
186
|
self._interrupt_count = 0
|
|
214
187
|
|
|
188
|
+
def _process_stream_chunk(self, chunk):
|
|
189
|
+
"""Обрабатывает кусок потокового ответа, создавая файлы на лету"""
|
|
190
|
+
self.stream_buffer += chunk
|
|
191
|
+
|
|
192
|
+
# Ищем [[CREATE:...]]...[[END]]
|
|
193
|
+
while True:
|
|
194
|
+
# Ищем начало CREATE
|
|
195
|
+
create_match = re.search(r'\[\[CREATE:(.+?)\]\](.*?)\[\[END\]\]', self.stream_buffer, re.DOTALL)
|
|
196
|
+
if create_match:
|
|
197
|
+
path = create_match.group(1).strip()
|
|
198
|
+
content = create_match.group(2).strip()
|
|
199
|
+
content = re.sub(r'^```[\w]*\n', '', content)
|
|
200
|
+
content = re.sub(r'\n```$', '', content)
|
|
201
|
+
|
|
202
|
+
if content and self.security.is_safe_path(path):
|
|
203
|
+
self.file_manager.create_file(path, content)
|
|
204
|
+
self.stats["files_created"].append(path)
|
|
205
|
+
lines = content.count('\n') + 1
|
|
206
|
+
print(f"\n ✅ Created: {path} ({lines} lines)")
|
|
207
|
+
|
|
208
|
+
# Удаляем обработанный блок из буфера
|
|
209
|
+
self.stream_buffer = self.stream_buffer[create_match.end():]
|
|
210
|
+
else:
|
|
211
|
+
break
|
|
212
|
+
|
|
213
|
+
# Ищем [[MODIFY:...]]...[[END]]
|
|
214
|
+
while True:
|
|
215
|
+
modify_match = re.search(r'\[\[MODIFY:(.+?)\]\](.*?)\[\[END\]\]', self.stream_buffer, re.DOTALL)
|
|
216
|
+
if modify_match:
|
|
217
|
+
path = modify_match.group(1).strip()
|
|
218
|
+
content = modify_match.group(2).strip()
|
|
219
|
+
content = re.sub(r'^```[\w]*\n', '', content)
|
|
220
|
+
content = re.sub(r'\n```$', '', content)
|
|
221
|
+
|
|
222
|
+
if content and self.security.is_safe_path(path):
|
|
223
|
+
self.file_manager.modify_file(path, content)
|
|
224
|
+
self.stats["files_modified"].append(path)
|
|
225
|
+
lines = content.count('\n') + 1
|
|
226
|
+
print(f"\n ✏️ Modified: {path} ({lines} lines)")
|
|
227
|
+
|
|
228
|
+
self.stream_buffer = self.stream_buffer[modify_match.end():]
|
|
229
|
+
else:
|
|
230
|
+
break
|
|
231
|
+
|
|
232
|
+
# Ищем [[EXEC:...]]
|
|
233
|
+
while True:
|
|
234
|
+
exec_match = re.search(r'\[\[EXEC:(.+?)\]\]', self.stream_buffer)
|
|
235
|
+
if exec_match:
|
|
236
|
+
command = exec_match.group(1).strip()
|
|
237
|
+
self.pending_commands.append(command)
|
|
238
|
+
self.stream_buffer = self.stream_buffer[exec_match.end():]
|
|
239
|
+
else:
|
|
240
|
+
break
|
|
241
|
+
|
|
215
242
|
def _make_request_streaming(self, data):
|
|
243
|
+
"""Потоковый запрос с обработкой файлов в реальном времени"""
|
|
244
|
+
self.stream_buffer = ""
|
|
245
|
+
self.pending_commands = []
|
|
246
|
+
|
|
216
247
|
if HAS_REQUESTS:
|
|
217
|
-
return self.
|
|
248
|
+
return self._stream_requests(data)
|
|
218
249
|
else:
|
|
219
|
-
return self.
|
|
250
|
+
return self._stream_urllib(data)
|
|
220
251
|
|
|
221
|
-
def
|
|
252
|
+
def _stream_requests(self, data):
|
|
222
253
|
try:
|
|
223
254
|
response = requests.post(
|
|
224
255
|
self.api_url,
|
|
@@ -233,6 +264,8 @@ Respond in Russian. Write code in English."""
|
|
|
233
264
|
response.raise_for_status()
|
|
234
265
|
|
|
235
266
|
full_response = ""
|
|
267
|
+
in_code_block = False
|
|
268
|
+
|
|
236
269
|
for line in response.iter_lines():
|
|
237
270
|
if line:
|
|
238
271
|
line = line.decode('utf-8')
|
|
@@ -246,18 +279,34 @@ Respond in Russian. Write code in English."""
|
|
|
246
279
|
delta = chunk['choices'][0].get('delta', {})
|
|
247
280
|
content = delta.get('content', '')
|
|
248
281
|
if content:
|
|
249
|
-
# Показываем только если это не внутри кодового блока
|
|
250
|
-
print(content, end="", flush=True)
|
|
251
282
|
full_response += content
|
|
283
|
+
|
|
284
|
+
# Определяем, нужно ли показывать
|
|
285
|
+
if '[[CREATE:' in full_response[-100:] or '[[MODIFY:' in full_response[-100:]:
|
|
286
|
+
in_code_block = True
|
|
287
|
+
|
|
288
|
+
if '[[END]]' in full_response[-20:]:
|
|
289
|
+
in_code_block = False
|
|
290
|
+
|
|
291
|
+
# Показываем только текст вне кодовых блоков
|
|
292
|
+
if not in_code_block:
|
|
293
|
+
# Не показываем маркеры
|
|
294
|
+
display = content.replace('[[CREATE:', '').replace('[[MODIFY:', '').replace('[[END]]', '').replace('[[EXEC:', '')
|
|
295
|
+
if display.strip():
|
|
296
|
+
print(display, end="", flush=True)
|
|
297
|
+
|
|
298
|
+
# Обрабатываем буфер для создания файлов
|
|
299
|
+
self._process_stream_chunk(content)
|
|
252
300
|
except:
|
|
253
301
|
continue
|
|
302
|
+
|
|
254
303
|
print()
|
|
255
304
|
return full_response
|
|
256
305
|
except Exception as e:
|
|
257
306
|
print_error(f"API Error: {e}")
|
|
258
307
|
return ""
|
|
259
308
|
|
|
260
|
-
def
|
|
309
|
+
def _stream_urllib(self, data):
|
|
261
310
|
try:
|
|
262
311
|
json_data = json.dumps({**data, "stream": True}).encode('utf-8')
|
|
263
312
|
req = urllib.request.Request(
|
|
@@ -272,6 +321,8 @@ Respond in Russian. Write code in English."""
|
|
|
272
321
|
ctx = ssl.create_default_context()
|
|
273
322
|
|
|
274
323
|
full_response = ""
|
|
324
|
+
in_code_block = False
|
|
325
|
+
|
|
275
326
|
with urllib.request.urlopen(req, context=ctx, timeout=120) as response:
|
|
276
327
|
for line in response:
|
|
277
328
|
line = line.decode('utf-8').strip()
|
|
@@ -285,8 +336,20 @@ Respond in Russian. Write code in English."""
|
|
|
285
336
|
delta = chunk['choices'][0].get('delta', {})
|
|
286
337
|
content = delta.get('content', '')
|
|
287
338
|
if content:
|
|
288
|
-
print(content, end="", flush=True)
|
|
289
339
|
full_response += content
|
|
340
|
+
|
|
341
|
+
if '[[CREATE:' in full_response[-100:] or '[[MODIFY:' in full_response[-100:]:
|
|
342
|
+
in_code_block = True
|
|
343
|
+
|
|
344
|
+
if '[[END]]' in full_response[-20:]:
|
|
345
|
+
in_code_block = False
|
|
346
|
+
|
|
347
|
+
if not in_code_block:
|
|
348
|
+
display = content.replace('[[CREATE:', '').replace('[[MODIFY:', '').replace('[[END]]', '').replace('[[EXEC:', '')
|
|
349
|
+
if display.strip():
|
|
350
|
+
print(display, end="", flush=True)
|
|
351
|
+
|
|
352
|
+
self._process_stream_chunk(content)
|
|
290
353
|
except:
|
|
291
354
|
continue
|
|
292
355
|
print()
|
|
@@ -318,155 +381,31 @@ Respond in Russian. Write code in English."""
|
|
|
318
381
|
if response:
|
|
319
382
|
self.conversation_history.append({"role": "assistant", "content": response})
|
|
320
383
|
|
|
321
|
-
|
|
322
|
-
if
|
|
323
|
-
|
|
384
|
+
# Выполняем оставшиеся команды
|
|
385
|
+
if self.pending_commands:
|
|
386
|
+
print_header("\nCOMMANDS")
|
|
387
|
+
for i, cmd in enumerate(self.pending_commands, 1):
|
|
388
|
+
print(f" {i}. {cmd}")
|
|
389
|
+
|
|
390
|
+
if self.auto_approve:
|
|
391
|
+
choice = 'A'
|
|
392
|
+
else:
|
|
393
|
+
print("\n[A] Execute all [S] Skip [C] Cancel")
|
|
394
|
+
choice = input("> ").strip().upper()
|
|
395
|
+
|
|
396
|
+
if choice == 'A':
|
|
397
|
+
for cmd in self.pending_commands:
|
|
398
|
+
if self.security.is_safe_command(cmd):
|
|
399
|
+
self.file_manager.execute_command(cmd)
|
|
400
|
+
self.stats["commands_executed"].append(cmd)
|
|
401
|
+
elif choice == 'S':
|
|
402
|
+
print_warning("Skipped")
|
|
324
403
|
|
|
325
404
|
self.stats["messages_sent"] += 1
|
|
326
405
|
return response
|
|
327
406
|
except Exception as e:
|
|
328
407
|
return print_error(f"Error: {e}")
|
|
329
408
|
|
|
330
|
-
def _parse_actions(self, response):
|
|
331
|
-
actions = []
|
|
332
|
-
|
|
333
|
-
# CREATE с [[END]]
|
|
334
|
-
create_pattern = r'\[\[CREATE:(.+?)\]\](.*?)\[\[END\]\]'
|
|
335
|
-
for match in re.finditer(create_pattern, response, re.DOTALL):
|
|
336
|
-
path = match.group(1).strip()
|
|
337
|
-
content = match.group(2).strip()
|
|
338
|
-
# Убираем маркеры кода если есть
|
|
339
|
-
content = re.sub(r'^```[\w]*\n', '', content)
|
|
340
|
-
content = re.sub(r'\n```$', '', content)
|
|
341
|
-
if content:
|
|
342
|
-
actions.append({'type': 'create', 'path': path, 'content': content})
|
|
343
|
-
|
|
344
|
-
# CREATE без [[END]] (старый формат, берём до следующего [[ или конца)
|
|
345
|
-
if not any(a['type'] == 'create' for a in actions):
|
|
346
|
-
old_create = r'\[\[CREATE:(.+?)\]\](.*?)(?=\[\[|$)'
|
|
347
|
-
for match in re.finditer(old_create, response, re.DOTALL):
|
|
348
|
-
path = match.group(1).strip()
|
|
349
|
-
content = match.group(2).strip()
|
|
350
|
-
content = re.sub(r'^```[\w]*\n', '', content)
|
|
351
|
-
content = re.sub(r'\n```$', '', content)
|
|
352
|
-
# Убираем явно не-кодовые строки
|
|
353
|
-
lines = content.split('\n')
|
|
354
|
-
clean_lines = []
|
|
355
|
-
for line in lines:
|
|
356
|
-
if not line.startswith('Here') and not line.startswith('This file') and not line.startswith('Now you'):
|
|
357
|
-
clean_lines.append(line)
|
|
358
|
-
content = '\n'.join(clean_lines).strip()
|
|
359
|
-
if content:
|
|
360
|
-
actions.append({'type': 'create', 'path': path, 'content': content})
|
|
361
|
-
|
|
362
|
-
# MODIFY с [[END]]
|
|
363
|
-
modify_pattern = r'\[\[MODIFY:(.+?)\]\](.*?)\[\[END\]\]'
|
|
364
|
-
for match in re.finditer(modify_pattern, response, re.DOTALL):
|
|
365
|
-
path = match.group(1).strip()
|
|
366
|
-
content = match.group(2).strip()
|
|
367
|
-
content = re.sub(r'^```[\w]*\n', '', content)
|
|
368
|
-
content = re.sub(r'\n```$', '', content)
|
|
369
|
-
if content:
|
|
370
|
-
actions.append({'type': 'modify', 'path': path, 'content': content})
|
|
371
|
-
|
|
372
|
-
# DELETE
|
|
373
|
-
for match in re.finditer(r'\[\[DELETE:(.+?)\]\]', response):
|
|
374
|
-
actions.append({'type': 'delete', 'path': match.group(1).strip()})
|
|
375
|
-
|
|
376
|
-
# READ
|
|
377
|
-
for match in re.finditer(r'\[\[READ:(.+?)\]\]', response):
|
|
378
|
-
actions.append({'type': 'read', 'path': match.group(1).strip()})
|
|
379
|
-
|
|
380
|
-
# LIST
|
|
381
|
-
for match in re.finditer(r'\[\[LIST:(.+?)\]\]', response):
|
|
382
|
-
actions.append({'type': 'list', 'path': match.group(1).strip()})
|
|
383
|
-
|
|
384
|
-
# EXEC
|
|
385
|
-
for match in re.finditer(r'\[\[EXEC:(.+?)\]\]', response):
|
|
386
|
-
actions.append({'type': 'exec', 'command': match.group(1).strip()})
|
|
387
|
-
|
|
388
|
-
return actions
|
|
389
|
-
|
|
390
|
-
def _execute_actions(self, actions):
|
|
391
|
-
if not actions:
|
|
392
|
-
return
|
|
393
|
-
|
|
394
|
-
file_actions = [a for a in actions if a['type'] in ['create', 'modify', 'delete', 'read', 'list']]
|
|
395
|
-
exec_actions = [a for a in actions if a['type'] == 'exec']
|
|
396
|
-
|
|
397
|
-
# Файловые операции - автоматически, показываем только информацию
|
|
398
|
-
if file_actions:
|
|
399
|
-
print_header("\nFILE OPERATIONS")
|
|
400
|
-
for i, action in enumerate(file_actions, 1):
|
|
401
|
-
if action['type'] == 'create':
|
|
402
|
-
lines = action['content'].count('\n') + 1
|
|
403
|
-
print(f" {i}. Created: {action['path']} ({lines} lines)")
|
|
404
|
-
elif action['type'] == 'modify':
|
|
405
|
-
lines = action['content'].count('\n') + 1
|
|
406
|
-
print(f" {i}. Modified: {action['path']} ({lines} lines)")
|
|
407
|
-
elif action['type'] == 'delete':
|
|
408
|
-
print(f" {i}. Deleted: {action['path']}")
|
|
409
|
-
elif action['type'] == 'read':
|
|
410
|
-
print(f" {i}. Read: {action['path']}")
|
|
411
|
-
elif action['type'] == 'list':
|
|
412
|
-
print(f" {i}. Listed: {action['path']}")
|
|
413
|
-
|
|
414
|
-
for action in file_actions:
|
|
415
|
-
self._execute_action(action)
|
|
416
|
-
|
|
417
|
-
# Команды - требуют подтверждения
|
|
418
|
-
if exec_actions:
|
|
419
|
-
print_header("\nCOMMANDS (confirmation required)")
|
|
420
|
-
for i, action in enumerate(exec_actions, 1):
|
|
421
|
-
print(f" {i}. {action['command']}")
|
|
422
|
-
|
|
423
|
-
if self.auto_approve:
|
|
424
|
-
choice = 'A'
|
|
425
|
-
else:
|
|
426
|
-
print("\n[A] Execute all [S] Skip [C] Cancel")
|
|
427
|
-
choice = input("> ").strip().upper()
|
|
428
|
-
|
|
429
|
-
if choice == 'A':
|
|
430
|
-
for action in exec_actions:
|
|
431
|
-
self._execute_action(action)
|
|
432
|
-
elif choice == 'S':
|
|
433
|
-
print_warning("Skipped")
|
|
434
|
-
elif choice == 'C':
|
|
435
|
-
print_warning("Cancelled")
|
|
436
|
-
|
|
437
|
-
def _execute_action(self, action):
|
|
438
|
-
try:
|
|
439
|
-
if action['type'] == 'create':
|
|
440
|
-
if self.security.is_safe_path(action['path']):
|
|
441
|
-
self.file_manager.create_file(action['path'], action['content'])
|
|
442
|
-
self.stats["files_created"].append(action['path'])
|
|
443
|
-
|
|
444
|
-
elif action['type'] == 'modify':
|
|
445
|
-
if self.security.is_safe_path(action['path']):
|
|
446
|
-
self.file_manager.modify_file(action['path'], action['content'])
|
|
447
|
-
self.stats["files_modified"].append(action['path'])
|
|
448
|
-
|
|
449
|
-
elif action['type'] == 'delete':
|
|
450
|
-
if self.security.is_safe_path(action['path']):
|
|
451
|
-
self.file_manager.delete_file(action['path'])
|
|
452
|
-
self.stats["files_deleted"].append(action['path'])
|
|
453
|
-
|
|
454
|
-
elif action['type'] == 'read':
|
|
455
|
-
if self.security.is_safe_path(action['path']):
|
|
456
|
-
self.file_manager.read_file(action['path'])
|
|
457
|
-
self.stats["files_read"].append(action['path'])
|
|
458
|
-
|
|
459
|
-
elif action['type'] == 'list':
|
|
460
|
-
self.file_manager.list_directory(action['path'])
|
|
461
|
-
|
|
462
|
-
elif action['type'] == 'exec':
|
|
463
|
-
if self.security.is_safe_command(action['command']):
|
|
464
|
-
self.file_manager.execute_command(action['command'])
|
|
465
|
-
self.stats["commands_executed"].append(action['command'])
|
|
466
|
-
|
|
467
|
-
except Exception as e:
|
|
468
|
-
print_error(f"Action failed: {e}")
|
|
469
|
-
|
|
470
409
|
def _handle_command(self, user_input):
|
|
471
410
|
parts = user_input.split()
|
|
472
411
|
cmd = parts[0].lower()
|
|
@@ -515,7 +454,7 @@ COMMANDS:
|
|
|
515
454
|
print()
|
|
516
455
|
print_info(f"Project: {self.project_path}")
|
|
517
456
|
print_info(f"Model: {self.current_model}")
|
|
518
|
-
print_info("Files: auto | Commands: confirmation required")
|
|
457
|
+
print_info("Files: auto-create in real-time | Commands: confirmation required")
|
|
519
458
|
print_info("/help for commands\n")
|
|
520
459
|
|
|
521
460
|
while True:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|