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.
Files changed (24) hide show
  1. {handycode-2.1.5 → handycode-2.1.6}/PKG-INFO +1 -1
  2. {handycode-2.1.5 → handycode-2.1.6}/handycode/__init__.py +1 -1
  3. {handycode-2.1.5 → handycode-2.1.6}/handycode/assistant.py +128 -128
  4. {handycode-2.1.5 → handycode-2.1.6}/handycode/logo.py +17 -18
  5. handycode-2.1.6/handycode/utils.py +140 -0
  6. {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/PKG-INFO +1 -1
  7. {handycode-2.1.5 → handycode-2.1.6}/setup.py +1 -1
  8. handycode-2.1.5/handycode/utils.py +0 -92
  9. {handycode-2.1.5 → handycode-2.1.6}/LICENSE +0 -0
  10. {handycode-2.1.5 → handycode-2.1.6}/README.md +0 -0
  11. {handycode-2.1.5 → handycode-2.1.6}/handycode/__main__.py +0 -0
  12. {handycode-2.1.5 → handycode-2.1.6}/handycode/cli.py +0 -0
  13. {handycode-2.1.5 → handycode-2.1.6}/handycode/config.py +0 -0
  14. {handycode-2.1.5 → handycode-2.1.6}/handycode/file_manager.py +0 -0
  15. {handycode-2.1.5 → handycode-2.1.6}/handycode/main.py +0 -0
  16. {handycode-2.1.5 → handycode-2.1.6}/handycode/models.py +0 -0
  17. {handycode-2.1.5 → handycode-2.1.6}/handycode/project_templates.py +0 -0
  18. {handycode-2.1.5 → handycode-2.1.6}/handycode/security.py +0 -0
  19. {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/SOURCES.txt +0 -0
  20. {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/dependency_links.txt +0 -0
  21. {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/entry_points.txt +0 -0
  22. {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/requires.txt +0 -0
  23. {handycode-2.1.5 → handycode-2.1.6}/handycode.egg-info/top_level.txt +0 -0
  24. {handycode-2.1.5 → handycode-2.1.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: handycode
3
- Version: 2.1.5
3
+ Version: 2.1.6
4
4
  Summary: AI Code Assistant for DeepSeek
5
5
  Home-page: https://github.com/AuraTechno/HandyCode
6
6
  Author: AuraTechno
@@ -3,7 +3,7 @@ HandyCode - AI Ассистент для разработки
3
3
  Аналог Claude Code для командной строки
4
4
  """
5
5
 
6
- __version__ = "2.1.5"
6
+ __version__ = "2.1.6"
7
7
  __author__ = "AURA Tec."
8
8
  __license__ = "MIT"
9
9
 
@@ -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.current_file_action = None
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
- context += f"\nFile contents:\n"
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 - an AI assistant for coding. You can create, modify, delete files and run commands.
122
+ return """You are HandyCode - AI coding assistant. Create/modify/delete files and run commands.
139
123
 
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.
124
+ FORMAT:
125
+ [[CREATE:path/file]]
126
+ code here
127
+ [[END]]
141
128
 
142
- FILE FORMAT - use EXACTLY:
143
- [[CREATE:filename]]
144
- code here
145
- [[END]]
129
+ [[MODIFY:path/file]]
130
+ new code here
131
+ [[END]]
146
132
 
147
- [[MODIFY:filename]]
148
- new code here
149
- [[END]]
133
+ [[EXEC:command]]
150
134
 
151
- [[DELETE:filename]]
152
- [[READ:filename]]
153
- [[LIST:directory/]]
154
- [[EXEC:command]]
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
- 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
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\nPress Ctrl+C again to exit")
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
- # Ищем [[CREATE:...]]...[[END]]
169
+ # CREATE файлы в реальном времени
193
170
  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()
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
- print(f"\n ✅ Created: {path} ({lines} lines)")
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
- # Ищем [[MODIFY:...]]...[[END]]
188
+ # MODIFY файлы
214
189
  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()
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
- print(f"\n ✏️ Modified: {path} ({lines} lines)")
201
+ print_file_action('modify', path, f"({lines} lines)")
227
202
 
228
- self.stream_buffer = self.stream_buffer[modify_match.end():]
203
+ self.stream_buffer = self.stream_buffer[match.end():]
229
204
  else:
230
205
  break
231
206
 
232
- # Ищем [[EXEC:...]]
207
+ # EXEC команды
233
208
  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():]
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
- in_code_block = False
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
- 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
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
- 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)
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
- in_code_block = False
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 full_response[-100:] or '[[MODIFY:' in full_response[-100:]:
342
- in_code_block = True
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 '[[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)
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
- print_info(f"\nDEEPSEEK:")
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
- print_header("\nCOMMANDS")
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
- print(f" {i}. {cmd}")
361
+ print_command(cmd, i)
389
362
 
390
363
  if self.auto_approve:
391
364
  choice = 'A'
392
365
  else:
393
- print("\n[A] Execute all [S] Skip [C] Cancel")
394
- choice = input("> ").strip().upper()
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
- COMMANDS:
416
- /help Show help
417
- /scan Scan project
418
- /models List models
419
- /model NAME Switch model
420
- /clear Clear history
421
- /save Save session
422
- /stats Statistics
423
- /exit Exit
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
- print(f" {name}")
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: {model_name}")
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("Cleared")
421
+ print_success("Chat history cleared")
439
422
  elif cmd in ['/stats']:
440
- print(f"Messages: {self.stats['messages_sent']}")
441
- print(f"Created: {len(self.stats['files_created'])}")
442
- print(f"Modified: {len(self.stats['files_modified'])}")
443
- print(f"Deleted: {len(self.stats['files_deleted'])}")
444
- print(f"Commands: {len(self.stats['commands_executed'])}")
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("> ").strip()
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.0.0"
63
+ return "HandyCode v2.1.3"
65
64
 
66
65
  C = Colors
67
- return f"{C.CYAN}HandyCode{C.RESET} {C.WHITE}v2.0.0{C.RESET} - {C.GREEN}AI Ассистент{C.RESET}"
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
- {C.CYAN}{C.BOLD}╔═════════════════════════════════════════════════════════════════════════════════════════════════╗
78
- ║ ║
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
- ║ {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} ║
85
- ║ ║
86
- {C.WHITE}AI Ассистент для разработки{C.CYAN}
87
- {C.WHITE}Prod. by AURA Tec.{C.CYAN}
88
- {C.WHITE}2.1.3{C.CYAN}
89
- ║ ║
90
- ╚═════════════════════════════════════════════════════════════════════════════════════════════════╝{C.RESET}
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: handycode
3
- Version: 2.1.5
3
+ Version: 2.1.6
4
4
  Summary: AI Code Assistant for DeepSeek
5
5
  Home-page: https://github.com/AuraTechno/HandyCode
6
6
  Author: AuraTechno
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="handycode",
5
- version="2.1.5",
5
+ version="2.1.6",
6
6
  author="AuraTechno",
7
7
  description="AI Code Assistant for DeepSeek",
8
8
  long_description="HandyCode - AI Code Assistant",
@@ -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