handycode 2.1.5__tar.gz → 2.1.6__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.5 → handycode-2.1.6}/PKG-INFO +1 -1
- {handycode-2.1.5 → handycode-2.1.6}/handycode/__init__.py +1 -1
- {handycode-2.1.5 → handycode-2.1.6}/handycode/assistant.py +128 -128
- {handycode-2.1.5 → handycode-2.1.6}/handycode/logo.py +17 -18
- handycode-2.1.6/handycode/utils.py +140 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/PKG-INFO +1 -1
- {handycode-2.1.5 → handycode-2.1.6}/setup.py +1 -1
- handycode-2.1.5/handycode/utils.py +0 -92
- {handycode-2.1.5 → handycode-2.1.6}/LICENSE +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/README.md +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/__main__.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/cli.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/config.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/file_manager.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/main.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/models.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/project_templates.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode/security.py +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/SOURCES.txt +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/dependency_links.txt +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/entry_points.txt +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/requires.txt +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/top_level.txt +0 -0
- {handycode-2.1.5 → handycode-2.1.6}/setup.cfg +0 -0
|
@@ -14,12 +14,14 @@ from datetime import datetime
|
|
|
14
14
|
|
|
15
15
|
try:
|
|
16
16
|
import readline
|
|
17
|
+
|
|
17
18
|
HAS_READLINE = True
|
|
18
19
|
except ImportError:
|
|
19
20
|
HAS_READLINE = False
|
|
20
21
|
|
|
21
22
|
try:
|
|
22
23
|
import requests
|
|
24
|
+
|
|
23
25
|
HAS_REQUESTS = True
|
|
24
26
|
except ImportError:
|
|
25
27
|
HAS_REQUESTS = False
|
|
@@ -32,8 +34,9 @@ from handycode.models import MODELS, get_model_settings
|
|
|
32
34
|
from handycode.file_manager import FileManager
|
|
33
35
|
from handycode.security import SecurityChecker
|
|
34
36
|
from handycode.utils import (
|
|
35
|
-
print_colored, print_header, print_success,
|
|
36
|
-
print_error, print_warning, print_info, print_logo
|
|
37
|
+
Colors, print_colored, print_header, print_success,
|
|
38
|
+
print_error, print_warning, print_info, print_logo,
|
|
39
|
+
print_divider, print_file_action, print_command, print_status
|
|
37
40
|
)
|
|
38
41
|
|
|
39
42
|
|
|
@@ -70,9 +73,8 @@ class HandyCode:
|
|
|
70
73
|
"start_time": datetime.now()
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
# Буфер для потокового создания файлов
|
|
74
76
|
self.stream_buffer = ""
|
|
75
|
-
self.
|
|
77
|
+
self.pending_commands = []
|
|
76
78
|
|
|
77
79
|
self._setup_readline()
|
|
78
80
|
signal.signal(signal.SIGINT, self._signal_handler)
|
|
@@ -81,7 +83,6 @@ class HandyCode:
|
|
|
81
83
|
def _build_project_context(self):
|
|
82
84
|
context = f"\n\n=== CURRENT PROJECT ===\n"
|
|
83
85
|
context += f"Directory: {self.project_path}\n"
|
|
84
|
-
|
|
85
86
|
try:
|
|
86
87
|
all_files = []
|
|
87
88
|
for ext in self.file_manager.allowed_extensions:
|
|
@@ -106,25 +107,8 @@ class HandyCode:
|
|
|
106
107
|
context += f" {rel_path} ({self._format_size(size)})\n"
|
|
107
108
|
except:
|
|
108
109
|
pass
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
total = 0
|
|
112
|
-
for file in files:
|
|
113
|
-
if total > 50000:
|
|
114
|
-
break
|
|
115
|
-
try:
|
|
116
|
-
content = file.read_text(encoding='utf-8', errors='ignore')
|
|
117
|
-
if len(content) > 3000:
|
|
118
|
-
content = content[:3000] + "\n... (truncated)"
|
|
119
|
-
rel_path = file.relative_to(self.project_path)
|
|
120
|
-
context += f"\n=== {rel_path} ===\n{content}\n"
|
|
121
|
-
total += len(content)
|
|
122
|
-
except:
|
|
123
|
-
pass
|
|
124
|
-
|
|
125
|
-
except Exception as e:
|
|
126
|
-
context += f"\nError: {e}\n"
|
|
127
|
-
|
|
110
|
+
except:
|
|
111
|
+
pass
|
|
128
112
|
return context
|
|
129
113
|
|
|
130
114
|
def _format_size(self, size):
|
|
@@ -135,33 +119,27 @@ class HandyCode:
|
|
|
135
119
|
return f"{size:.1f}TB"
|
|
136
120
|
|
|
137
121
|
def _get_system_prompt(self):
|
|
138
|
-
return """You are HandyCode -
|
|
122
|
+
return """You are HandyCode - AI coding assistant. Create/modify/delete files and run commands.
|
|
139
123
|
|
|
140
|
-
|
|
124
|
+
FORMAT:
|
|
125
|
+
[[CREATE:path/file]]
|
|
126
|
+
code here
|
|
127
|
+
[[END]]
|
|
141
128
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
[[END]]
|
|
129
|
+
[[MODIFY:path/file]]
|
|
130
|
+
new code here
|
|
131
|
+
[[END]]
|
|
146
132
|
|
|
147
|
-
[[
|
|
148
|
-
new code here
|
|
149
|
-
[[END]]
|
|
133
|
+
[[EXEC:command]]
|
|
150
134
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
[[
|
|
154
|
-
[[
|
|
135
|
+
RULES:
|
|
136
|
+
1. CREATE + EXEC in ONE response
|
|
137
|
+
2. Use [[END]] after file content
|
|
138
|
+
3. NO comments inside [[CREATE]]...[[END]]
|
|
139
|
+
4. Explanations BEFORE [[CREATE]] blocks
|
|
140
|
+
5. Files create automatically, commands need confirmation
|
|
155
141
|
|
|
156
|
-
|
|
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
|
|
163
|
-
|
|
164
|
-
Speak Russian. Write code in English."""
|
|
142
|
+
Speak Russian. Write code in English."""
|
|
165
143
|
|
|
166
144
|
def _setup_readline(self):
|
|
167
145
|
if not HAS_READLINE:
|
|
@@ -178,7 +156,7 @@ Speak Russian. Write code in English."""
|
|
|
178
156
|
def _signal_handler(self, sig, frame):
|
|
179
157
|
self._interrupt_count += 1
|
|
180
158
|
if self._interrupt_count == 1:
|
|
181
|
-
print("\n\
|
|
159
|
+
print("\n\n ⚠ Press Ctrl+C again to exit")
|
|
182
160
|
else:
|
|
183
161
|
os._exit(0)
|
|
184
162
|
|
|
@@ -186,16 +164,14 @@ Speak Russian. Write code in English."""
|
|
|
186
164
|
self._interrupt_count = 0
|
|
187
165
|
|
|
188
166
|
def _process_stream_chunk(self, chunk):
|
|
189
|
-
"""Обрабатывает кусок потокового ответа, создавая файлы на лету"""
|
|
190
167
|
self.stream_buffer += chunk
|
|
191
168
|
|
|
192
|
-
#
|
|
169
|
+
# CREATE файлы в реальном времени
|
|
193
170
|
while True:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
content = create_match.group(2).strip()
|
|
171
|
+
match = re.search(r'\[\[CREATE:(.+?)\]\](.*?)\[\[END\]\]', self.stream_buffer, re.DOTALL)
|
|
172
|
+
if match:
|
|
173
|
+
path = match.group(1).strip()
|
|
174
|
+
content = match.group(2).strip()
|
|
199
175
|
content = re.sub(r'^```[\w]*\n', '', content)
|
|
200
176
|
content = re.sub(r'\n```$', '', content)
|
|
201
177
|
|
|
@@ -203,19 +179,18 @@ Speak Russian. Write code in English."""
|
|
|
203
179
|
self.file_manager.create_file(path, content)
|
|
204
180
|
self.stats["files_created"].append(path)
|
|
205
181
|
lines = content.count('\n') + 1
|
|
206
|
-
|
|
182
|
+
print_file_action('create', path, f"({lines} lines)")
|
|
207
183
|
|
|
208
|
-
|
|
209
|
-
self.stream_buffer = self.stream_buffer[create_match.end():]
|
|
184
|
+
self.stream_buffer = self.stream_buffer[match.end():]
|
|
210
185
|
else:
|
|
211
186
|
break
|
|
212
187
|
|
|
213
|
-
#
|
|
188
|
+
# MODIFY файлы
|
|
214
189
|
while True:
|
|
215
|
-
|
|
216
|
-
if
|
|
217
|
-
path =
|
|
218
|
-
content =
|
|
190
|
+
match = re.search(r'\[\[MODIFY:(.+?)\]\](.*?)\[\[END\]\]', self.stream_buffer, re.DOTALL)
|
|
191
|
+
if match:
|
|
192
|
+
path = match.group(1).strip()
|
|
193
|
+
content = match.group(2).strip()
|
|
219
194
|
content = re.sub(r'^```[\w]*\n', '', content)
|
|
220
195
|
content = re.sub(r'\n```$', '', content)
|
|
221
196
|
|
|
@@ -223,24 +198,22 @@ Speak Russian. Write code in English."""
|
|
|
223
198
|
self.file_manager.modify_file(path, content)
|
|
224
199
|
self.stats["files_modified"].append(path)
|
|
225
200
|
lines = content.count('\n') + 1
|
|
226
|
-
|
|
201
|
+
print_file_action('modify', path, f"({lines} lines)")
|
|
227
202
|
|
|
228
|
-
self.stream_buffer = self.stream_buffer[
|
|
203
|
+
self.stream_buffer = self.stream_buffer[match.end():]
|
|
229
204
|
else:
|
|
230
205
|
break
|
|
231
206
|
|
|
232
|
-
#
|
|
207
|
+
# EXEC команды
|
|
233
208
|
while True:
|
|
234
|
-
|
|
235
|
-
if
|
|
236
|
-
|
|
237
|
-
self.
|
|
238
|
-
self.stream_buffer = self.stream_buffer[exec_match.end():]
|
|
209
|
+
match = re.search(r'\[\[EXEC:(.+?)\]\]', self.stream_buffer)
|
|
210
|
+
if match:
|
|
211
|
+
self.pending_commands.append(match.group(1).strip())
|
|
212
|
+
self.stream_buffer = self.stream_buffer[match.end():]
|
|
239
213
|
else:
|
|
240
214
|
break
|
|
241
215
|
|
|
242
216
|
def _make_request_streaming(self, data):
|
|
243
|
-
"""Потоковый запрос с обработкой файлов в реальном времени"""
|
|
244
217
|
self.stream_buffer = ""
|
|
245
218
|
self.pending_commands = []
|
|
246
219
|
|
|
@@ -264,7 +237,7 @@ Speak Russian. Write code in English."""
|
|
|
264
237
|
response.raise_for_status()
|
|
265
238
|
|
|
266
239
|
full_response = ""
|
|
267
|
-
|
|
240
|
+
in_code = False
|
|
268
241
|
|
|
269
242
|
for line in response.iter_lines():
|
|
270
243
|
if line:
|
|
@@ -281,21 +254,17 @@ Speak Russian. Write code in English."""
|
|
|
281
254
|
if content:
|
|
282
255
|
full_response += content
|
|
283
256
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if '[[END]]' in full_response[-20:]:
|
|
289
|
-
in_code_block = False
|
|
257
|
+
if '[[CREATE:' in content or '[[MODIFY:' in content:
|
|
258
|
+
in_code = True
|
|
259
|
+
if '[[END]]' in content:
|
|
260
|
+
in_code = False
|
|
290
261
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
print(display, end="", flush=True)
|
|
262
|
+
if not in_code:
|
|
263
|
+
clean = content.replace('[[CREATE:', '').replace('[[MODIFY:', '').replace(
|
|
264
|
+
'[[END]]', '').replace('[[EXEC:', '').replace(']]', '')
|
|
265
|
+
if clean.strip():
|
|
266
|
+
print(clean, end="", flush=True)
|
|
297
267
|
|
|
298
|
-
# Обрабатываем буфер для создания файлов
|
|
299
268
|
self._process_stream_chunk(content)
|
|
300
269
|
except:
|
|
301
270
|
continue
|
|
@@ -321,7 +290,7 @@ Speak Russian. Write code in English."""
|
|
|
321
290
|
ctx = ssl.create_default_context()
|
|
322
291
|
|
|
323
292
|
full_response = ""
|
|
324
|
-
|
|
293
|
+
in_code = False
|
|
325
294
|
|
|
326
295
|
with urllib.request.urlopen(req, context=ctx, timeout=120) as response:
|
|
327
296
|
for line in response:
|
|
@@ -338,16 +307,16 @@ Speak Russian. Write code in English."""
|
|
|
338
307
|
if content:
|
|
339
308
|
full_response += content
|
|
340
309
|
|
|
341
|
-
if '[[CREATE:' in
|
|
342
|
-
|
|
310
|
+
if '[[CREATE:' in content or '[[MODIFY:' in content:
|
|
311
|
+
in_code = True
|
|
312
|
+
if '[[END]]' in content:
|
|
313
|
+
in_code = False
|
|
343
314
|
|
|
344
|
-
if
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if display.strip():
|
|
350
|
-
print(display, end="", flush=True)
|
|
315
|
+
if not in_code:
|
|
316
|
+
clean = content.replace('[[CREATE:', '').replace('[[MODIFY:', '').replace(
|
|
317
|
+
'[[END]]', '').replace('[[EXEC:', '').replace(']]', '')
|
|
318
|
+
if clean.strip():
|
|
319
|
+
print(clean, end="", flush=True)
|
|
351
320
|
|
|
352
321
|
self._process_stream_chunk(content)
|
|
353
322
|
except:
|
|
@@ -375,31 +344,37 @@ Speak Russian. Write code in English."""
|
|
|
375
344
|
}
|
|
376
345
|
|
|
377
346
|
try:
|
|
378
|
-
|
|
347
|
+
print_divider("─", 60, Colors.BRIGHT_BLACK)
|
|
348
|
+
print(colorize(" HandyCode", Colors.BRIGHT_CYAN + Colors.BOLD))
|
|
349
|
+
print_divider("─", 60, Colors.BRIGHT_BLACK)
|
|
350
|
+
|
|
379
351
|
response = self._make_request_streaming(payload)
|
|
380
352
|
|
|
381
353
|
if response:
|
|
382
354
|
self.conversation_history.append({"role": "assistant", "content": response})
|
|
383
355
|
|
|
384
|
-
# Выполняем оставшиеся команды
|
|
385
356
|
if self.pending_commands:
|
|
386
|
-
|
|
357
|
+
print()
|
|
358
|
+
print_divider("─", 60, Colors.BRIGHT_BLACK)
|
|
359
|
+
print(colorize(" ⚡ Commands (confirmation required):", Colors.YELLOW))
|
|
387
360
|
for i, cmd in enumerate(self.pending_commands, 1):
|
|
388
|
-
|
|
361
|
+
print_command(cmd, i)
|
|
389
362
|
|
|
390
363
|
if self.auto_approve:
|
|
391
364
|
choice = 'A'
|
|
392
365
|
else:
|
|
393
|
-
print(
|
|
394
|
-
|
|
366
|
+
print()
|
|
367
|
+
print(colorize(f" {Colors.BRIGHT_BLACK}[A] Execute all [S] Skip [C] Cancel{Colors.RESET}",
|
|
368
|
+
Colors.BRIGHT_BLACK))
|
|
369
|
+
choice = input(colorize(" > ", Colors.WHITE)).strip().upper()
|
|
395
370
|
|
|
396
371
|
if choice == 'A':
|
|
372
|
+
print()
|
|
397
373
|
for cmd in self.pending_commands:
|
|
398
374
|
if self.security.is_safe_command(cmd):
|
|
375
|
+
print_status(f"Running: {cmd}")
|
|
399
376
|
self.file_manager.execute_command(cmd)
|
|
400
377
|
self.stats["commands_executed"].append(cmd)
|
|
401
|
-
elif choice == 'S':
|
|
402
|
-
print_warning("Skipped")
|
|
403
378
|
|
|
404
379
|
self.stats["messages_sent"] += 1
|
|
405
380
|
return response
|
|
@@ -411,38 +386,50 @@ Speak Russian. Write code in English."""
|
|
|
411
386
|
cmd = parts[0].lower()
|
|
412
387
|
|
|
413
388
|
if cmd in ['/help', '/h']:
|
|
414
|
-
print(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
/
|
|
418
|
-
/
|
|
419
|
-
/
|
|
420
|
-
/
|
|
421
|
-
/
|
|
422
|
-
/
|
|
423
|
-
/
|
|
424
|
-
""
|
|
389
|
+
print()
|
|
390
|
+
print(colorize(" Commands:", Colors.BRIGHT_CYAN + Colors.BOLD))
|
|
391
|
+
print_divider("─", 40, Colors.BRIGHT_BLACK)
|
|
392
|
+
print(colorize(" /help Show this help", Colors.WHITE))
|
|
393
|
+
print(colorize(" /scan Scan project files", Colors.WHITE))
|
|
394
|
+
print(colorize(" /models List AI models", Colors.WHITE))
|
|
395
|
+
print(colorize(" /model NAME Switch model", Colors.WHITE))
|
|
396
|
+
print(colorize(" /clear Clear chat history", Colors.WHITE))
|
|
397
|
+
print(colorize(" /save Save session to file", Colors.WHITE))
|
|
398
|
+
print(colorize(" /stats Show statistics", Colors.WHITE))
|
|
399
|
+
print(colorize(" /exit Exit program", Colors.WHITE))
|
|
400
|
+
print()
|
|
425
401
|
elif cmd in ['/scan', '/s']:
|
|
402
|
+
print()
|
|
403
|
+
print(colorize(" Project Files:", Colors.BRIGHT_CYAN + Colors.BOLD))
|
|
404
|
+
print_divider("─", 40, Colors.BRIGHT_BLACK)
|
|
426
405
|
print(self.file_manager.scan_project())
|
|
427
406
|
elif cmd in ['/models', '/m']:
|
|
407
|
+
print()
|
|
408
|
+
print(colorize(" Available Models:", Colors.BRIGHT_CYAN + Colors.BOLD))
|
|
409
|
+
print_divider("─", 40, Colors.BRIGHT_BLACK)
|
|
428
410
|
for name in MODELS:
|
|
429
|
-
|
|
411
|
+
marker = colorize(" (current)", Colors.GREEN) if MODELS[name] == self.current_model else ""
|
|
412
|
+
print(f" • {name}{marker}")
|
|
430
413
|
elif cmd in ['/model'] and len(parts) > 1:
|
|
431
414
|
model_name = parts[1]
|
|
432
415
|
if model_name in MODELS:
|
|
433
416
|
self.current_model = MODELS[model_name]
|
|
434
417
|
self.model_settings = get_model_settings(self.current_model)
|
|
435
|
-
print_success(f"Switched to
|
|
418
|
+
print_success(f"Switched to {model_name}")
|
|
436
419
|
elif cmd in ['/clear', '/c']:
|
|
437
420
|
self.conversation_history = [self.conversation_history[0]]
|
|
438
|
-
print_success("
|
|
421
|
+
print_success("Chat history cleared")
|
|
439
422
|
elif cmd in ['/stats']:
|
|
440
|
-
print(
|
|
441
|
-
print(
|
|
442
|
-
|
|
443
|
-
print(f"
|
|
444
|
-
print(f"
|
|
423
|
+
print()
|
|
424
|
+
print(colorize(" Session Stats:", Colors.BRIGHT_CYAN + Colors.BOLD))
|
|
425
|
+
print_divider("─", 40, Colors.BRIGHT_BLACK)
|
|
426
|
+
print(colorize(f" Messages: {self.stats['messages_sent']}", Colors.WHITE))
|
|
427
|
+
print(colorize(f" Created: {len(self.stats['files_created'])} files", Colors.GREEN))
|
|
428
|
+
print(colorize(f" Modified: {len(self.stats['files_modified'])} files", Colors.YELLOW))
|
|
429
|
+
print(colorize(f" Deleted: {len(self.stats['files_deleted'])} files", Colors.RED))
|
|
430
|
+
print(colorize(f" Commands: {len(self.stats['commands_executed'])}", Colors.CYAN))
|
|
445
431
|
elif cmd in ['/exit', '/q']:
|
|
432
|
+
print_success("Goodbye!")
|
|
446
433
|
os._exit(0)
|
|
447
434
|
return ""
|
|
448
435
|
|
|
@@ -451,19 +438,32 @@ COMMANDS:
|
|
|
451
438
|
|
|
452
439
|
def run(self):
|
|
453
440
|
print_logo()
|
|
441
|
+
print_divider("─", 60, Colors.BRIGHT_BLACK)
|
|
442
|
+
print(colorize(f" 📁 Project: {self.project_path}", Colors.WHITE))
|
|
443
|
+
print(colorize(f" 🤖 Model: {self.current_model}", Colors.WHITE))
|
|
444
|
+
print(colorize(f" {Colors.BRIGHT_BLACK}/help for commands{Colors.RESET}", Colors.BRIGHT_BLACK))
|
|
445
|
+
print_divider("─", 60, Colors.BRIGHT_BLACK)
|
|
454
446
|
print()
|
|
455
|
-
print_info(f"Project: {self.project_path}")
|
|
456
|
-
print_info(f"Model: {self.current_model}")
|
|
457
|
-
print_info("Files: auto-create in real-time | Commands: confirmation required")
|
|
458
|
-
print_info("/help for commands\n")
|
|
459
447
|
|
|
460
448
|
while True:
|
|
461
449
|
try:
|
|
462
450
|
self.reset_interrupt()
|
|
463
|
-
user_input = input("
|
|
451
|
+
user_input = input(colorize(" ❯ ", Colors.BRIGHT_CYAN + Colors.BOLD)).strip()
|
|
464
452
|
if user_input:
|
|
465
453
|
self.send_message(user_input)
|
|
466
454
|
except KeyboardInterrupt:
|
|
467
455
|
continue
|
|
468
456
|
except EOFError:
|
|
469
|
-
break
|
|
457
|
+
break
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
# Добавляем colorize как глобальную функцию для удобства
|
|
461
|
+
def colorize(text, color):
|
|
462
|
+
if supports_color():
|
|
463
|
+
return f"{color}{text}{Colors.RESET}"
|
|
464
|
+
return text
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def supports_color():
|
|
468
|
+
from handycode.utils import supports_color as sc
|
|
469
|
+
return sc()
|
|
@@ -51,7 +51,6 @@ def get_logo_plain() -> str:
|
|
|
51
51
|
║ ║
|
|
52
52
|
║ AI Ассистент для разработки ║
|
|
53
53
|
║ Prod. by AURA Tec. ║
|
|
54
|
-
║ 2.1.2 ║
|
|
55
54
|
║ ║
|
|
56
55
|
╚══════════════════════════════════════════════════════════════╝
|
|
57
56
|
"""
|
|
@@ -61,10 +60,10 @@ def get_logo_plain() -> str:
|
|
|
61
60
|
def get_small_logo() -> str:
|
|
62
61
|
"""Возвращает маленький логотип"""
|
|
63
62
|
if not supports_color():
|
|
64
|
-
return "HandyCode v2.
|
|
63
|
+
return "HandyCode v2.1.3"
|
|
65
64
|
|
|
66
65
|
C = Colors
|
|
67
|
-
return f"{C.CYAN}HandyCode{C.RESET} {C.WHITE}v2.
|
|
66
|
+
return f"{C.CYAN}HandyCode{C.RESET} {C.WHITE}v2.1.3{C.RESET} - {C.GREEN}AI Ассистент{C.RESET} | {C.BRIGHT_BLACK}Prod. by AURA Tec.{C.RESET}"
|
|
68
67
|
|
|
69
68
|
|
|
70
69
|
def get_install_logo() -> str:
|
|
@@ -74,19 +73,19 @@ def get_install_logo() -> str:
|
|
|
74
73
|
|
|
75
74
|
C = Colors
|
|
76
75
|
logo = f"""
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
76
|
+
{C.CYAN}{C.BOLD}╔═════════════════════════════════════════════════════════════════════════════════════════════════╗
|
|
77
|
+
║ ║
|
|
78
|
+
║ {C.YELLOW}██╗ ██╗{C.CYAN} {C.GREEN}█████╗{C.CYAN} {C.BLUE}███╗ ██╗{C.CYAN} {C.MAGENTA}██████╗{C.CYAN} {C.RED}██╗ ██╗{C.CYAN} {C.WHITE}██████╗{C.CYAN} {C.GREEN}███████╗{C.CYAN} {C.BLUE}██████╗{C.CYAN} {C.MAGENTA}███████╗{C.CYAN} ║
|
|
79
|
+
║ {C.YELLOW}██║ ██║{C.CYAN} {C.GREEN}██╔══██╗{C.CYAN} {C.BLUE}████╗ ██║{C.CYAN} {C.MAGENTA}██╔══██╗{C.CYAN} {C.RED}╚██╗ ██╔╝{C.CYAN} {C.WHITE}██╔════╝{C.CYAN} {C.GREEN}██╔════██╗{C.CYAN} {C.BLUE}██╔══██╗{C.CYAN} {C.MAGENTA}██╔════╝{C.CYAN} ║
|
|
80
|
+
║ {C.YELLOW}███████║{C.CYAN} {C.GREEN}███████║{C.CYAN} {C.BLUE}██╔██╗ ██║{C.CYAN} {C.MAGENTA}██║ ██║{C.CYAN} {C.RED}╚████╔╝{C.CYAN} {C.WHITE}██║{C.CYAN} {C.GREEN}██║ ██║{C.CYAN} {C.BLUE}██║ ██║{C.CYAN} {C.MAGENTA}█████╗{C.CYAN} ║
|
|
81
|
+
║ {C.YELLOW}██╔══██║{C.CYAN} {C.GREEN}██╔══██║{C.CYAN} {C.BLUE}██║╚██╗██║{C.CYAN} {C.MAGENTA}██║ ██║{C.CYAN} {C.RED}╚██╔╝{C.CYAN} {C.WHITE}██║{C.CYAN} {C.GREEN}██║ ██║{C.CYAN} {C.BLUE}██║ ██║{C.CYAN} {C.MAGENTA}██╔══╝{C.CYAN} ║
|
|
82
|
+
║ {C.YELLOW}██║ ██║{C.CYAN} {C.GREEN}██║ ██║{C.CYAN} {C.BLUE}██║ ╚████║{C.CYAN} {C.MAGENTA}██████╔╝{C.CYAN} {C.RED}██║{C.CYAN} {C.WHITE}╚██████╗{C.CYAN} {C.GREEN}███████╔╝{C.CYAN} {C.BLUE}██████╔╝{C.CYAN} {C.MAGENTA}███████╗{C.CYAN} ║
|
|
83
|
+
║ {C.YELLOW}╚═╝ ╚═╝{C.CYAN} {C.GREEN}╚═╝ ╚═╝{C.CYAN} {C.BLUE}╚═╝ ╚═══╝{C.CYAN} {C.MAGENTA}╚═════╝{C.CYAN} {C.RED}╚═╝{C.CYAN} {C.WHITE} ╚═════╝{C.CYAN} {C.GREEN}╚═════╝{C.CYAN} {C.BLUE}╚═════╝{C.CYAN} {C.MAGENTA}╚══════╝{C.CYAN} ║
|
|
84
|
+
║ ║
|
|
85
|
+
║ {C.WHITE}{C.BOLD}УСТАНОВКА HANDYCODE{C.CYAN} ║
|
|
86
|
+
║ {C.WHITE}AI Ассистент для разработки{C.CYAN} ║
|
|
87
|
+
║ {C.WHITE}Prod. by AURA Tec.{C.CYAN} ║
|
|
88
|
+
║ ║
|
|
89
|
+
╚═════════════════════════════════════════════════════════════════════════════════════════════════╝{C.RESET}
|
|
90
|
+
"""
|
|
92
91
|
return logo
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Вспомогательные функции для HandyCode
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Colors:
|
|
10
|
+
RESET = '\033[0m'
|
|
11
|
+
RED = '\033[91m'
|
|
12
|
+
GREEN = '\033[92m'
|
|
13
|
+
YELLOW = '\033[93m'
|
|
14
|
+
BLUE = '\033[94m'
|
|
15
|
+
MAGENTA = '\033[95m'
|
|
16
|
+
CYAN = '\033[96m'
|
|
17
|
+
WHITE = '\033[97m'
|
|
18
|
+
BRIGHT_BLACK = '\033[90m'
|
|
19
|
+
BRIGHT_RED = '\033[91m'
|
|
20
|
+
BRIGHT_GREEN = '\033[92m'
|
|
21
|
+
BRIGHT_YELLOW = '\033[93m'
|
|
22
|
+
BRIGHT_BLUE = '\033[94m'
|
|
23
|
+
BRIGHT_MAGENTA = '\033[95m'
|
|
24
|
+
BRIGHT_CYAN = '\033[96m'
|
|
25
|
+
BRIGHT_WHITE = '\033[97m'
|
|
26
|
+
BOLD = '\033[1m'
|
|
27
|
+
DIM = '\033[2m'
|
|
28
|
+
ITALIC = '\033[3m'
|
|
29
|
+
UNDERLINE = '\033[4m'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def supports_color():
|
|
33
|
+
if os.name == 'nt':
|
|
34
|
+
try:
|
|
35
|
+
import ctypes
|
|
36
|
+
kernel32 = ctypes.windll.kernel32
|
|
37
|
+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
38
|
+
return True
|
|
39
|
+
except:
|
|
40
|
+
return False
|
|
41
|
+
return hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def colorize(text, color):
|
|
45
|
+
if supports_color():
|
|
46
|
+
return f"{color}{text}{Colors.RESET}"
|
|
47
|
+
return text
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def print_colored(text, color):
|
|
51
|
+
print(colorize(text, color))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def print_header(text):
|
|
55
|
+
print(colorize(text, Colors.CYAN + Colors.BOLD))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def print_success(text):
|
|
59
|
+
print(colorize(f" ✓ {text}", Colors.GREEN))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def print_error(text):
|
|
63
|
+
print(colorize(f" ✗ {text}", Colors.RED))
|
|
64
|
+
return text
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def print_warning(text):
|
|
68
|
+
print(colorize(f" ⚠ {text}", Colors.YELLOW))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def print_info(text):
|
|
72
|
+
print(colorize(f" ℹ {text}", Colors.BLUE))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def print_logo():
|
|
76
|
+
from .logo import get_small_logo
|
|
77
|
+
print(get_small_logo())
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def truncate(text, max_length=100):
|
|
81
|
+
if len(text) <= max_length:
|
|
82
|
+
return text
|
|
83
|
+
return text[:max_length - 3] + "..."
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def format_size(size_bytes):
|
|
87
|
+
for unit in ['B', 'KB', 'MB', 'GB']:
|
|
88
|
+
if size_bytes < 1024:
|
|
89
|
+
return f"{size_bytes:.1f} {unit}"
|
|
90
|
+
size_bytes /= 1024
|
|
91
|
+
return f"{size_bytes:.1f} TB"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def print_box(text, color=Colors.CYAN):
|
|
95
|
+
"""Рисует рамку вокруг текста"""
|
|
96
|
+
lines = text.strip().split('\n')
|
|
97
|
+
width = max(len(line) for line in lines) + 4
|
|
98
|
+
print(colorize(f"╭{'─' * (width - 2)}╮", color))
|
|
99
|
+
for line in lines:
|
|
100
|
+
print(colorize(f"│ {line.ljust(width - 4)} │", color))
|
|
101
|
+
print(colorize(f"╰{'─' * (width - 2)}╯", color))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def print_divider(char="─", width=60, color=Colors.BRIGHT_BLACK):
|
|
105
|
+
"""Рисует разделитель"""
|
|
106
|
+
print(colorize(char * width, color))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def print_file_action(action_type, path, details=""):
|
|
110
|
+
"""Красиво показывает действие с файлом"""
|
|
111
|
+
icons = {
|
|
112
|
+
'create': '📄',
|
|
113
|
+
'modify': '✏️',
|
|
114
|
+
'delete': '🗑️',
|
|
115
|
+
'read': '📖',
|
|
116
|
+
}
|
|
117
|
+
colors_map = {
|
|
118
|
+
'create': Colors.GREEN,
|
|
119
|
+
'modify': Colors.YELLOW,
|
|
120
|
+
'delete': Colors.RED,
|
|
121
|
+
'read': Colors.BLUE,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
icon = icons.get(action_type, '•')
|
|
125
|
+
color = colors_map.get(action_type, Colors.WHITE)
|
|
126
|
+
|
|
127
|
+
if details:
|
|
128
|
+
print(colorize(f" {icon} {path} {Colors.BRIGHT_BLACK}{details}{Colors.RESET}", color))
|
|
129
|
+
else:
|
|
130
|
+
print(colorize(f" {icon} {path}", color))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def print_command(cmd, index=1):
|
|
134
|
+
"""Красиво показывает команду"""
|
|
135
|
+
print(colorize(f" {index}. ⚡ {cmd}", Colors.YELLOW))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def print_status(msg):
|
|
139
|
+
"""Показывает статус"""
|
|
140
|
+
print(colorize(f" ● {msg}", Colors.CYAN))
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Вспомогательные функции для HandyCode
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import sys
|
|
6
|
-
import os
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# Цвета для терминала
|
|
10
|
-
class Colors:
|
|
11
|
-
RESET = '\033[0m'
|
|
12
|
-
RED = '\033[91m'
|
|
13
|
-
GREEN = '\033[92m'
|
|
14
|
-
YELLOW = '\033[93m'
|
|
15
|
-
BLUE = '\033[94m'
|
|
16
|
-
MAGENTA = '\033[95m'
|
|
17
|
-
CYAN = '\033[96m'
|
|
18
|
-
WHITE = '\033[97m'
|
|
19
|
-
BOLD = '\033[1m'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def supports_color():
|
|
23
|
-
"""Проверяет поддержку цветов"""
|
|
24
|
-
if os.name == 'nt': # Windows
|
|
25
|
-
try:
|
|
26
|
-
import ctypes
|
|
27
|
-
kernel32 = ctypes.windll.kernel32
|
|
28
|
-
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
29
|
-
return True
|
|
30
|
-
except:
|
|
31
|
-
return False
|
|
32
|
-
return hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def colorize(text, color):
|
|
36
|
-
"""Добавляет цвет"""
|
|
37
|
-
if supports_color():
|
|
38
|
-
return f"{color}{text}{Colors.RESET}"
|
|
39
|
-
return text
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def print_colored(text, color):
|
|
43
|
-
"""Выводит цветной текст"""
|
|
44
|
-
print(colorize(text, color))
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def print_header(text):
|
|
48
|
-
"""Выводит заголовок"""
|
|
49
|
-
print(colorize(text, Colors.CYAN + Colors.BOLD))
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def print_success(text):
|
|
53
|
-
"""Выводит успех"""
|
|
54
|
-
print(colorize(text, Colors.GREEN))
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def print_error(text):
|
|
58
|
-
"""Выводит ошибку"""
|
|
59
|
-
print(colorize(text, Colors.RED))
|
|
60
|
-
return text
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def print_warning(text):
|
|
64
|
-
"""Выводит предупреждение"""
|
|
65
|
-
print(colorize(text, Colors.YELLOW))
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def print_info(text):
|
|
69
|
-
"""Выводит информацию"""
|
|
70
|
-
print(colorize(text, Colors.BLUE))
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def print_logo():
|
|
74
|
-
"""Выводит логотип"""
|
|
75
|
-
from .logo import get_logo
|
|
76
|
-
print(get_logo())
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def truncate(text, max_length=100):
|
|
80
|
-
"""Обрезает текст"""
|
|
81
|
-
if len(text) <= max_length:
|
|
82
|
-
return text
|
|
83
|
-
return text[:max_length - 3] + "..."
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def format_size(size_bytes):
|
|
87
|
-
"""Форматирует размер"""
|
|
88
|
-
for unit in ['B', 'KB', 'MB', 'GB']:
|
|
89
|
-
if size_bytes < 1024:
|
|
90
|
-
return f"{size_bytes:.1f} {unit}"
|
|
91
|
-
size_bytes /= 1024
|
|
92
|
-
return f"{size_bytes:.1f} TB"
|
|
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
|