codegpt-ai 2.27.0 → 2.28.0

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 (2) hide show
  1. package/package.json +1 -1
  2. package/tui.py +271 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codegpt-ai",
3
- "version": "2.27.0",
3
+ "version": "2.28.0",
4
4
  "description": "Local AI Assistant Hub — 123 commands, 26 tools, 8 agents, multi-AI, security. No cloud needed.",
5
5
  "author": "ArukuX",
6
6
  "license": "MIT",
package/tui.py CHANGED
@@ -3,6 +3,8 @@ import json
3
3
  import os
4
4
  import sys
5
5
  import time
6
+ import subprocess
7
+ import threading
6
8
  import requests
7
9
  from pathlib import Path
8
10
  from datetime import datetime
@@ -123,6 +125,61 @@ TUI_COMMANDS = {
123
125
  "/weather": "Get weather",
124
126
  "/agent": "Run an AI agent",
125
127
  "/quit": "Exit",
128
+ "/temp": "Set temperature (0-2)",
129
+ "/system": "Set system prompt",
130
+ "/export": "Export chat as text",
131
+ "/browse": "Fetch and summarize URL",
132
+ "/open": "Open URL in browser",
133
+ "/save": "Save conversation",
134
+ "/copy": "Copy last response",
135
+ "/regen": "Regenerate last response",
136
+ "/compact": "Summarize old messages",
137
+ "/search": "Search conversation",
138
+ "/diff": "Compare last 2 responses",
139
+ "/pin": "Pin a message",
140
+ "/pins": "Show pinned messages",
141
+ "/fork": "Fork conversation from #",
142
+ "/rate": "Rate last response (good/bad)",
143
+ "/all": "Ask ALL agents at once",
144
+ "/vote": "Agents vote on a question",
145
+ "/swarm": "6-agent pipeline",
146
+ "/team": "Group chat with 2 AIs",
147
+ "/room": "Chat room with 3+ AIs",
148
+ "/spectate": "Watch AIs debate",
149
+ "/dm": "Message a specific agent",
150
+ "/race": "Race all models",
151
+ "/compare": "Compare 2 models",
152
+ "/chain": "Chain prompts (p1 | p2 | p3)",
153
+ "/lab": "AI Lab experiments",
154
+ "/train": "AI Training Lab",
155
+ "/mem": "AI memory (save/recall)",
156
+ "/skill": "Create custom command",
157
+ "/skills": "List custom skills",
158
+ "/auto": "AI creates a skill",
159
+ "/cron": "Schedule recurring task",
160
+ "/tools": "List AI tool integrations",
161
+ "/github": "GitHub tools",
162
+ "/spotify": "Spotify controls",
163
+ "/volume": "System volume",
164
+ "/sysinfo": "System info",
165
+ "/usage": "Usage dashboard",
166
+ "/profile": "View profile",
167
+ "/setname": "Set display name",
168
+ "/setbio": "Set bio",
169
+ "/security": "Security dashboard",
170
+ "/permissions": "View permissions",
171
+ "/audit": "Security audit log",
172
+ "/pin-set": "Set login PIN",
173
+ "/lock": "Lock session",
174
+ "/qr": "QR code to connect",
175
+ "/broadcast": "Message all tools",
176
+ "/inbox": "Check messages",
177
+ "/feed": "Message feed",
178
+ "/monitor": "Live dashboard",
179
+ "/hub": "Command center",
180
+ "/shortcuts": "Keyboard shortcuts",
181
+ "/prompts": "Prompt templates",
182
+ "/desktop": "Desktop app (PC only)",
126
183
  }
127
184
 
128
185
 
@@ -386,16 +443,222 @@ def handle_command(text):
386
443
  console.print(Text(" Usage: /agent coder build a flask API", style="dim"))
387
444
  console.print()
388
445
 
446
+ elif cmd == "/temp":
447
+ if args:
448
+ try:
449
+ t = float(args)
450
+ if 0 <= t <= 2:
451
+ console.print(Text(f" Temperature: {t}", style="green"))
452
+ except:
453
+ console.print(Text(" Usage: /temp 0.7 (0.0-2.0)", style="dim"))
454
+ else:
455
+ console.print(Text(" Usage: /temp 0.7", style="dim"))
456
+ console.print()
457
+
458
+ elif cmd == "/system":
459
+ if args:
460
+ system = args
461
+ console.print(Text(" System prompt updated.", style="green"))
462
+ else:
463
+ console.print(Text(f" Current: {system[:80]}...", style="dim"))
464
+ console.print()
465
+
466
+ elif cmd == "/export":
467
+ if messages:
468
+ lines = []
469
+ for m in messages:
470
+ role = "You" if m["role"] == "user" else "AI"
471
+ lines.append(f"{role}: {m['content']}\n")
472
+ export_path = Path.home() / ".codegpt" / f"export_{datetime.now().strftime('%Y%m%d_%H%M')}.md"
473
+ export_path.parent.mkdir(parents=True, exist_ok=True)
474
+ export_path.write_text("\n".join(lines), encoding="utf-8")
475
+ console.print(Text(f" Exported: {export_path}", style="green"))
476
+ else:
477
+ console.print(Text(" Nothing to export.", style="dim"))
478
+ console.print()
479
+
480
+ elif cmd == "/browse":
481
+ if args:
482
+ url = args if args.startswith("http") else "https://" + args
483
+ console.print(Text(f" Fetching {url}...", style="dim"))
484
+ try:
485
+ import re as _re
486
+ r = requests.get(url, timeout=15, headers={"User-Agent": "CodeGPT/2.0"})
487
+ text = _re.sub(r'<script[^>]*>.*?</script>', '', r.text, flags=_re.DOTALL)
488
+ text = _re.sub(r'<style[^>]*>.*?</style>', '', text, flags=_re.DOTALL)
489
+ text = _re.sub(r'<[^>]+>', ' ', text)
490
+ text = _re.sub(r'\s+', ' ', text).strip()[:3000]
491
+ resp = requests.post(OLLAMA_URL, json={
492
+ "model": MODEL, "messages": [
493
+ {"role": "system", "content": "Summarize in 3-5 bullet points."},
494
+ {"role": "user", "content": text},
495
+ ], "stream": False,
496
+ }, timeout=60)
497
+ summary = resp.json().get("message", {}).get("content", text[:500])
498
+ print_msg("ai", summary)
499
+ except Exception as e:
500
+ console.print(Text(f" Error: {e}", style="red"))
501
+ else:
502
+ console.print(Text(" Usage: /browse github.com", style="dim"))
503
+ console.print()
504
+
505
+ elif cmd == "/open":
506
+ if args:
507
+ url = args if args.startswith("http") else "https://" + args
508
+ if os.path.exists("/data/data/com.termux"):
509
+ try:
510
+ subprocess.run(["termux-open-url", url], timeout=5)
511
+ except:
512
+ subprocess.run(["am", "start", "-a", "android.intent.action.VIEW", "-d", url], timeout=5)
513
+ else:
514
+ import webbrowser
515
+ webbrowser.open(url)
516
+ console.print(Text(f" Opened: {url}", style="green"))
517
+ else:
518
+ console.print(Text(" Usage: /open google.com", style="dim"))
519
+ console.print()
520
+
521
+ elif cmd == "/all":
522
+ if args:
523
+ agents_list = {
524
+ "coder": "You are an expert programmer.",
525
+ "debugger": "You are a debugging expert.",
526
+ "reviewer": "You are a code reviewer.",
527
+ "architect": "You are a system architect.",
528
+ "pentester": "You are an ethical pentester.",
529
+ "explainer": "You are a patient teacher.",
530
+ "optimizer": "You are a performance engineer.",
531
+ "researcher": "You are a research analyst.",
532
+ }
533
+ import threading
534
+ results = {}
535
+ def _query(n, s):
536
+ try:
537
+ r = requests.post(OLLAMA_URL, json={"model": MODEL, "messages": [
538
+ {"role": "system", "content": s}, {"role": "user", "content": args}
539
+ ], "stream": False}, timeout=90)
540
+ results[n] = r.json().get("message", {}).get("content", "")
541
+ except:
542
+ results[n] = "(error)"
543
+ threads = [threading.Thread(target=_query, args=(n, s), daemon=True) for n, s in agents_list.items()]
544
+ for t in threads: t.start()
545
+ console.print(Text(" Asking all 8 agents...", style="dim"))
546
+ for t in threads: t.join(timeout=90)
547
+ for name, resp in results.items():
548
+ console.print(Text.from_markup(f"\n [bright_blue]{name}[/]"))
549
+ console.print(Text(f" {resp[:200]}", style="white"))
550
+ console.print()
551
+ else:
552
+ console.print(Text(" Usage: /all what database should I use?", style="dim"))
553
+ console.print()
554
+
555
+ elif cmd == "/save":
556
+ if messages:
557
+ save_path = Path.home() / ".codegpt" / "chats" / f"{datetime.now().strftime('%Y%m%d_%H%M')}.json"
558
+ save_path.parent.mkdir(parents=True, exist_ok=True)
559
+ save_path.write_text(json.dumps({"messages": messages, "model": MODEL}))
560
+ console.print(Text(f" Saved: {save_path.name}", style="green"))
561
+ else:
562
+ console.print(Text(" Nothing to save.", style="dim"))
563
+ console.print()
564
+
565
+ elif cmd == "/copy":
566
+ ai_msgs = [m for m in messages if m["role"] == "assistant"]
567
+ if ai_msgs:
568
+ last = ai_msgs[-1]["content"]
569
+ try:
570
+ import subprocess
571
+ if os.path.exists("/data/data/com.termux"):
572
+ subprocess.run(["termux-clipboard-set"], input=last.encode(), timeout=5)
573
+ else:
574
+ subprocess.run("clip" if os.name == "nt" else "pbcopy", input=last.encode(), shell=True, timeout=5)
575
+ console.print(Text(" Copied to clipboard.", style="green"))
576
+ except:
577
+ console.print(Text(" Cannot copy — clipboard not available.", style="red"))
578
+ else:
579
+ console.print(Text(" No response to copy.", style="dim"))
580
+ console.print()
581
+
582
+ elif cmd == "/regen":
583
+ if messages and messages[-1]["role"] == "assistant":
584
+ messages.pop()
585
+ if messages and messages[-1]["role"] == "user":
586
+ last_q = messages[-1]["content"]
587
+ messages.pop()
588
+ print_msg("user", last_q)
589
+ chat(last_q)
590
+ else:
591
+ console.print(Text(" Nothing to regenerate.", style="dim"))
592
+ console.print()
593
+
594
+ elif cmd == "/rate":
595
+ rating = args.lower() if args else ""
596
+ if rating in ("good", "bad", "+", "-"):
597
+ ai_msgs = [m for m in messages if m["role"] == "assistant"]
598
+ if ai_msgs:
599
+ ratings_file = Path.home() / ".codegpt" / "ratings.json"
600
+ ratings = []
601
+ if ratings_file.exists():
602
+ try: ratings = json.loads(ratings_file.read_text())
603
+ except: pass
604
+ ratings.append({"rating": "good" if rating in ("good", "+") else "bad",
605
+ "response": ai_msgs[-1]["content"][:200],
606
+ "timestamp": datetime.now().isoformat()})
607
+ ratings_file.write_text(json.dumps(ratings))
608
+ console.print(Text(f" Rated: {rating}", style="green"))
609
+ else:
610
+ console.print(Text(" Usage: /rate good or /rate bad", style="dim"))
611
+ console.print()
612
+
613
+ elif cmd == "/usage":
614
+ profile = {}
615
+ if profile_file.exists():
616
+ try: profile = json.loads(profile_file.read_text())
617
+ except: pass
618
+ console.print(Text.from_markup(
619
+ f" [bold]Session[/]\n"
620
+ f" Messages [bright_blue]{len(messages)}[/]\n"
621
+ f" Tokens [bright_blue]{total_tokens}[/]\n"
622
+ f" Model [bright_blue]{MODEL}[/]\n"
623
+ f" Persona [bright_blue]{persona}[/]\n\n"
624
+ f" [bold]Lifetime[/]\n"
625
+ f" Messages [bright_blue]{profile.get('total_messages', 0)}[/]\n"
626
+ f" Sessions [bright_blue]{profile.get('total_sessions', 0)}[/]"
627
+ ))
628
+ console.print()
629
+
630
+ elif cmd == "/profile":
631
+ profile = {}
632
+ if profile_file.exists():
633
+ try: profile = json.loads(profile_file.read_text())
634
+ except: pass
635
+ console.print(Text.from_markup(
636
+ f" [bold]{profile.get('name', 'User')}[/]\n"
637
+ f" Bio {profile.get('bio', '')}\n"
638
+ f" Model [bright_blue]{profile.get('model', MODEL)}[/]\n"
639
+ f" Persona [green]{profile.get('persona', 'default')}[/]\n"
640
+ f" Sessions {profile.get('total_sessions', 0)}"
641
+ ))
642
+ console.print()
643
+
389
644
  elif cmd == "/help" or cmd == "/h":
390
- cmds = {
391
- "/new": "New chat", "/model": "Switch model", "/persona": "Switch persona",
392
- "/think": "Toggle reasoning", "/tokens": "Token count", "/clear": "Clear screen",
393
- "/sidebar": "Toggle sidebar", "/history": "Show history", "/connect": "Remote Ollama",
394
- "/server": "Server info", "/weather": "Get weather", "/agent": "Run agent",
395
- "/help": "This list", "/quit": "Exit",
645
+ groups = {
646
+ "Chat": ["/new", "/save", "/copy", "/regen", "/history", "/clear", "/export", "/quit"],
647
+ "Model": ["/model", "/models", "/persona", "/think", "/temp", "/tokens", "/system"],
648
+ "AI": ["/agent", "/all", "/vote", "/swarm", "/team", "/room", "/spectate", "/race"],
649
+ "Files": ["/browse", "/open"],
650
+ "Memory": ["/mem", "/train", "/rate", "/search", "/fork", "/pin"],
651
+ "Tools": ["/tools", "/skill", "/auto", "/cron"],
652
+ "Connect": ["/connect", "/server", "/qr", "/weather", "/sysinfo", "/github"],
653
+ "Profile": ["/profile", "/usage", "/setname", "/setbio", "/permissions"],
654
+ "Security": ["/pin-set", "/lock", "/audit", "/security"],
396
655
  }
397
- for c, d in cmds.items():
398
- console.print(Text.from_markup(f" [bright_blue]{c:<12}[/] [dim]{d}[/]"))
656
+ for group, cmds_list in groups.items():
657
+ console.print(Text(f"\n {group}", style="bold bright_blue"))
658
+ for c in cmds_list:
659
+ desc = TUI_COMMANDS.get(c, "")
660
+ if desc:
661
+ console.print(Text.from_markup(f" [bright_blue]{c:<14}[/] [dim]{desc}[/]"))
399
662
  console.print()
400
663
 
401
664
  else: